It’s a simple CRUD example which allows searching, creating and modifying vineyards and the vines they produce. The application is definitely ugly but its goal is simply to demonstrate the following features :

Basic CRUD with a Spring service

Support for lazy-loading of JPA x-to-many associations

Dirty-checking

Client validation

Real-time push

Reverse lazy-loading

Each step corresponds to a tag on the GitHub project so you can see what has been changed at each step.

As you can see, there are two specific annotations on the service. @RemoteDestination indicates that the service is exposed to the Flex application, and that an ActionScript3 proxy will be generated for the service. @DataEnabled indicates that GraniteDS will track the JPA updates on the entities and report them automatically to the relevant clients.

This is no major complexity here, but there are some things that can be noted :

It uses the PagedQuery component to display the list of existing vineyards. The configuration section at the beginning (addComponentWithFactory…) links the client component to the service method we have define in our Spring service, so each time the component needs to fetch data, it will call the remote service transparently. This component also handles paging, so it will fetch next elements on demand when there are lots of data on the server and the user scrolls the list. This is also why it is wrapped in an AsyncListView collection view.

The PagedQuery also handles transparently filtering and sorting. Here we simply bind a TextInput to the filter object that is passed to the server method. When the user clicks on ‘Search’, we simply have to refresh the collection, as we would have done for a client filter. Sorting is even easier and is handled completely transparently when the collection is bound to a UI component that allows sorting, for example DataGrid.

The remote service invocation is completely typesafe thanks to the ActionScript 3 generator and the [Inject] annotation. If you refactor the service, you will instantly detect the inconsistency between the client and the server at build time. You can also benefit from code completion in the IDE.

The CRUD operations are completely ‘fire and forget’. You just call the server method, and GraniteDS will automatically handle the updates, you don’t have to do anything yourself. There is not even a result handler and the server method do not return anything. In fact GraniteDS listens to all JPA events though the DataPublishListener in AbstractEntity and dispatches them transparently to the Flex clients, including the client from which the call originates. The local caching does everything else and GraniteDS is able to match and merge the result with the initial object on the client. The remove operation also works transparently because the PagedQuery is able to handle the remove events and update itself. It we were using simple collections, we would have to handle manually the persist and remove events.

Now you can build the application with mvn clean install, restart jetty and check your changes.

Step 3 : Support for JPA lazy associations

There is nothing much to do, simply add a form item allowing to edit the list of wines for the selected vineyard. We can for example use a list with an item renderer containing editors for the properties of the Wine entity :

As you can see, this is purely client code. We rely on cascading to persist the changes in the database, and GraniteDS is able to cleanly transfer lazy associations without much hassle.

More, when entities are fetched in the list of vineyards, their collections of wines are still not loaded. When the user selects a vineyard, the binding {vineyard.wines} on the list automatically triggers the loading of the collection from the server. This is completely transparent so you don’t even have to think about it !!

Step 4 : Dirty checking / Undo

If you have played with the application you may have noticed that using bidirectional bindings leads to strange behaviour. Even without saving your changes, the local objects are still modified. GraniteDS tracks all updates made on the managed entities and is able to easily restore the last known stable state of the objects (usually the last fetch from the server).

It’s also easily possible to enable or disable the ‘Save’ button depending on the fact that the user has modified something or not.

To achieve this, we have to ensure that the entity bound to the form is managed by GraniteDS (in particular for newly created entities because entities retrieved from the server are always managed). We have just to add a few lines when the user selects another element in the main list to restore the state of the previously edited element :

import org.granite.tide.spring.Context;

[Inject] [Bindable]

public var tideContext:Context;

private function selectVineyard(vineyard:Vineyard):void {

Managed.resetEntity(this.vineyard);

tideContext.vineyard = this.vineyard = vineyard;

vineyardsList.selectedItem = vineyard;

}

[/js]

Then we can use the meta_dirty property of the Tide context to enable/disable the ‘Save’ button :

Step 5 : Validation

Great, we can now create, edit and search in our database. Now we would like to ensure that the data is consistent. Instead of manually defining Flex validators on each field, we are going to use the Bean Validation API on the server and its GraniteDS implementation on the client.

First let’s add a few Bean Validation annotations on the model :

@Basic
@Size(min=5, max=100,
message="The name must be between {min} and {max} characters")
private String name;
@Basic
@Min(value=1900,
message="The year must be greater than {value}")
@Max(value=2050,
message="The year must be less than {value}")
private Integer year;
@Enumerated(EnumType.STRING)
@NotNull
private Type type;

This will at least ensure that we cannot save invalid entities. However we would like that our user is informed that the operation has failed. One ugly way would be to add a fault handler on the save operation call with an alert. Instead we are simply going to use the FormValidator component that will validate the entity locally and interpret server exceptions to propagate the error messages to the correct input field.

First you have to register the validation exception handler that will process the validation errors coming from the server. This is not required in this example because all the constraints can be processed locally on the client, but it’s always useful in case the server has additional constraints. Just add this in the init method of Main.mxml.

Spring.getInstance().addExceptionHandler(ValidatorExceptionHandler);

In Home.mxml, add the v namespace and define a FormValidation attached to the edit form and the bound entity :

These two declarations will allow to display error messages on the field during editing. The FormValidator takes advantage of the replication of the Bean Validation annotations to ActionScript 3 to know what validations have to be applied on the client data.

Finally we can keep the user from trying to save an invalid object by adding the following line :

private function save():void {

if (formValidator.validateEntity())

wineshopService.save(vineyard);

}

[/js]

mvn install, jetty, …

Step 6 : Real-time data push

Enabling data push is just a question of configuration. There are 4 things to check :

Declare a topic messaging destination in the Spring configuration app-config.xml

Of course the three declarations should use the same topic name, but this is all you need to enable data push.

mvn install, jetty, …

Now if you open many browsers, all the changes made in one browser should be dispatched to all other browsers.

Step 7 : Conflict handling

In any multiuser application, there may be cases where different users do changes on the same entity concurrently. Using optimistic locking is a common technique to handle these cases and avoid database inconsistencies. GraniteDS is able to handle these errors cleanly in either normal remoting operations or with real-time data push.

This is quite simple to configure, you just need to register an exception handler for the JPA OptimistickLockException and an event listener on the Tide context that will be called when a concurrent modification conflict occurs :

The most difficult part is to actually obtain a conflict. After your rebuild and restart jetty, open two browsers. Create a vineyard in one of the browsers, it will appear in the second one. Edit it in the second browser and change something, for example its name, without saving. Then in the first browser, change the name to a different value and save. An alert should appear in the second browser.

Step 8 : Reverse lazy loading

This final feature is not very visual, but it can improve a lot the performance of your application. The support for server-to-client lazy loading ensures that the amount of data transferred in this direction is limited, but a problem can arise in the other direction (client-to-server). Once all your object graph is loaded on the client by transparent loading or manual operations, the complete loaded object graph will be sent back to the server even when only a property of the root object has been changed. With very deep and complex object graphs, this can really kill the performance of write operations.

GraniteDS now provides a new feature called reverse lazy loading that allows to fold the object graph before transmitting it to the server. It will take in account the changes made locally by the user to fold all parts of the graph that have not been updated, and still send the parts of the graph containing the changes.

This can be setup as follows : in the initialization method of the application, register an argument preprocessor :

And then in the service methods that update entities, simply add the @Lazy annotation to the incoming arguments :

public void save(@Lazy Vineyard vineyard);

To really see what happens, you have to run mvn jetty:run-war in debug mode from the Eclipse M2E plugin, and put a breakpoint in the method save(). Then create a vineyard and some wines, and save it. Close and reopen the browser to restart from a clean state, edit and change the name of the vineyard you just created and click on Save. By inspecting the incoming Vineyard object on the debugger, you can see that the collection of wines is marked uninitialized. Now change the name of one of the wine and click on Save. This time the collection will be initialized and contain the Wine object you just updated.

Or you can just trust that it works and that it’s better to use it than not…

Conclusion

We’re done with this tutorial on the data management features. You are now be able to see what you can do with GraniteDS and how it can simplify your developments, and even bring new possibilities for your applications.

I could not get this example to compile running maven 3.0.3. Also, your mvn archetype is incorrect. To download the archetype you will need to remove “org” from the artifactId tag and add “-hibernate” to the same tag like so: “graniteds-tide-jpa-hibernate”

I was able bringing the project into Eclipse and compile each module separately and reassemble to load in Tomcat 7.0 – unfortunately, the application does not exhibit any data push. When I have to instances running, the only way to get my new vineyard to appear in the other client is to manually enter a search for the new vineyard. Any ideas of what might be the issue? My guess is that the Gravity servlet is failing.

I’m receiving a couple maven errors that say “Plugin execution not covered by lifecycle configuration: …” I tried looking this issue up and configuring the maven lifecycle mapping; however, I was unable to get it working. Any thoughts?