More details on the new features in GraniteDS 2.3

GraniteDS 2.3.0 RC1 is mostly a bugfix and incremental improvements release but contains a few new features :

Support for JBoss AS 7 / Hibernate 4

JBoss have once again changed their VFS internal implementation in AS 7, breaking the class scanner in GraniteDS. It was still possible to run GraniteDS in non-scan mode but this is now fixed and you can now benefit from the very fast JBoss AS 7 with GDS 2.3.

Another issue with JBoss AS 7 is its deep integration with Hibernate 4 which makes very painful to deploy Hibernate 3.x applications (i.e. all Hibernate applications). There are a few workarounds described on the Hibernate blog here. However it’s recommended to upgrade to H4 as soon as possible, and GraniteDS now fully supports Hibernate 4. Just use the granite-hibernate4.jar instead of granite-hibernate.jar and you’re done.

Support for Flex 4.5+

Flex 4.5 broke a few APIs and there were two main issues with Tide :

  • The client libraries crashed when run in a mobile application
  • The PagedQuery was unusable

These two issues are now fixed and you will find a version of the granite-flex45.swc client libraries compiled with the Flex SDK 4.5 here. Unfortunately it is not yet available with the ‘normal’ distribution because our build system was not able to build with two different versions of the Flex SDK. The library can be found only in the new Community Platform distribution (which will be the default distribution format starting from GraniteDS 3.0). For the final release we will try to add it manually and it should be also available as a maven artifact.

Reverse lazy-loading

The support of lazy-loaded associations when retrieving detached entities from the server is an important feature of GraniteDS and greatly helps limiting the amount of data transferred through the network. However it works only in the server to client direction. The problem is that once you have loaded all associations on the client, passing an object as an argument of a remote method call will send the whole loaded object graph to the server, even if you have only changed a simple property.

public function savePerson():void {

    person.lastName = "Test";

    personService.save(person);   // This will send all loaded collections associated to the Person object

}

Obviously this is not very efficient, so you can now ask Tide to uninitialize the object graph before sending it. You can do it manually with :

var uperson:Person = new EntityGraphUnintializer(tideContext).uninitializeEntityGraph(person) as Person;

personService.save(uperson);

Here all loaded collections of the Person object will be uninitialized so uperson contains only the minimum of data to correctly merge your changes in the server persistence context. If there is a change deeper in the object graph, the uninitializer is able to detect it and will not uninitialize the corresponding graph so the server receives all changes.

person.contacts.getItemAt(0).email = 'test@test.com';

var uperson:Person = new EntityGraphUnintializer(tideContext).uninitializeEntityGraph(person) as Person;

personService.save(uperson);

Here uperson will still contain the loaded collection of contacts, but if there are other collections, they will be uninitialized.

If you want to uninitialize more than one argument, you have to use the same EntityGraphUninitializer for all so they share the same context :

var egu:EntityGraphUnintializer = new EntityGraphUninitialize(tideContext);

uperson1 = egu.uninitializeEntityGraph(person1);

uperson2 = egu.uninitializeEntityGraph(person2);

personService.save(uperson1, uperson2);

Calling the EntityGraphUninitializer manually is a bit tedious and ugly, so there is a cleaner possibility when you are using generated typesafe service proxies. You can annotate your service method arguments with @org.granite.tide.data.Lazy :

public void save(@Lazy Person person) {

}

Gas3 will then generate a [Lazy] annotation on your service methods (so take care that you need to add the [Lazy] annotation to your Flex metadata compilation configuration). Next in the Flex application, register the UninitializeArgumentPreprocessor component in Tide.

Tide.getInstance().addComponents([UninitializeArgumentPreprocessor]);

Once you have done this, all calls to PersonService.save() will use an uninitialized version of the person argument.

It is important to note that it will not uninitialize the associations in your ‘normal’ Tide context and that there will not be any change to your client entities. Tide will simply copy the necessary entities in a temporary context, uninitialize the associations of the copies before the passing them as arguments to the remote call and then discard everything.

Unfortunately this new feature cannot yet work with Seam context variables (and thus with the Home component). Only method arguments can be processed, but this should cover be the vast majority of use cases. Support for context variables requires a major refactoring and will come with GDS 3.0.

Injection of the Gravity singleton

In GDS 2.2 you needed a ServletContext to retrieve the Gravity singleton from a server application, which is not always possible or suitable and implies a dependency on the Servlet API. With GDS 2.3, the singleton is available in the framework context and can simply be injected.

With Spring or CDI :

@Inject

private Gravity gravity;

With Seam :

@In("org.granite.seam.gravity")

private Gravity gravity;

The DI capabilities of EJB3 are too limited to allow something like this. If you need to be independent from the Gravity API, just use a JMS topic and send messages with the JMS API.

Improved support for transparent data push (ON_COMMIT mode and non-GDS threads)

GraniteDS provides a data push feature allowing to track updates on JPA entities and transparently dispatch them in real-time through Gravity to Flex clients. However in GDS 2.2 there were two limitations : the updates could be tracked only from a thread managed by GraniteDS (an HTTP remoting or messaging request), and the ON_COMMIT mode allowing transactional dispatch of the updates through JMS was not supported out-of-the-box.

GraniteDS 2.3 comes with a set of interceptors (unfortunately one for each technology : Spring, Seam, EJB3 and CDI ; so much for interoperability for such a basic thing as an interceptor) managing the ON_COMMIT mode that can also be used to track the updates on any non-GraniteDS thread.

The setup is simple, just add the useInterceptor=true parameter on the @DataEnabled annotation and use either ON_SUCCESS or ON_COMMIT. ON_SUCCESS is the default mode and simply means that the dispatch will occur for all successful calls. ON_COMMIT means that the dispatch will occur when the transaction commits, it ensures that the dispatch is transactional when used in conjuction with a transacted JMS topic.

Then configure the interceptor for your target framework :

For Spring, add this in your context :

<graniteds:tide-data-publishing-advice/>

Take care that you have to use the latest xsd :

xsi:schemaLocation="http://www.graniteds.org/config http://www.graniteds.org/public/dtd/2.3.0/granite-config-2.3.xsd"

For Seam, nothing to do, the interceptor is implicitly setup when the @DataEnabled(useInterceptor=true) annotation is applied.

For CDI, just enable the interceptor in beans.xml :

<beans 

    xmlns="http://java.sun.com/xml/ns/javaee"	

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"	

    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">	  	

    <interceptors>

        <class>org.granite.tide.cdi.TideDataPublishingInterceptor</class>

    </interceptors>

</beans>

For EJB3, you have to setup the interceptor on each EJB :

@Stateless

@Local(MyService.class)

@Interceptors(TideDataPublishingInterceptor.class)

@DataEnabled(topic="myTopic", publish=PublishMode.ON_COMMIT, useInterceptor=true)

public class MyServiceBean {

    ...

}

Or globally in ejb-jar.xml :

<assembly-descriptor>

      <interceptor-binding>

         <ejb-name>*</ejb-name>

         <interceptor-class>org.granite.tide.ejb.TideDataPublishingInterceptor</interceptor-class>

      </interceptor-binding>

      ...

</assembly-descriptor>
Tags:

Author: Franck

Leave a Comment