| « GWT-Ext : Quelles leçons pour GWT ? | Why GWT 1.5 won’t solve your Hibernate issues » |
BlazeDS: Use hibernate4gwt as Hibernate adapter
Foreword
I am currently working on a EJB3 / Flex3 project and face a very familiar issue.
BlazeDS is an open-source library, published by Adobe, to send messages from Flex code to java one (and vice-versa). The source code is an extract of LiveCycle Data Services, its commercial ancestor, still alive and handling more functionalities.
One of the most missing features of BlazeDS is an adapter to send Hibernate beans from a Java server to the Flex/Action Script client side, whereas this adapter is available in LiveCycle DataService (but it costs a lot of $$$, including runtime fees).
Hibernate4GWT ?
Hibernate4GWT is a open-source library I am working on since more than one year now to allow sharing Hibernate beans from server to GWT client side. But, despites of its name, the core of the library is really independent of GWT (don’t worry, you won’t have to add GWT libraries to your Flex project ;) ):
- The ‘clone’ operation consists of converting Hibernate beans to pure, regular beans. Proxies are removed, and Persistent collections converted to their classic counterparts. Of course, persistence information handled by proxies and persistent collections are stored to be reused when the POJO comes back.
- The ‘merge’ operation takes the neutral bean and merges it back to a Hibernate bean. Proxies and persistent collections are regenerated accordingly to what was undone by the clone operation.
Defining your own adapter
It is pretty simple, and well documented here.
So here is the code:
/**
* Hibernate adapter for BlazeDS
* It is based on hibernate4gwt core and delegates Hibernate beans management to it.
* @author bruno.marchesson
*
*/
public class HibernateAdapter extends JavaAdapter
{
/**
* The Hibernate bean manager
*/
private HibernateBeanManager _beanManager;
//-------------------------------------------------------------------------
// Java adapter override
//-------------------------------------------------------------------------
/**
* Adapter initialisation
*/
@Override
public void initialize(String name, ConfigMap config)
{
// Call base method
//
super.start();
// Get EntityManagerFactory
//
Object entityManagerFactory = getEntityManagerFactory();
// Create hibernate bean manager
//
_beanManager = new HibernateBeanManager();
_beanManager.setEntityManagerFactory(entityManagerFactory);
}/**
* Invoke adapter
*/
@Override
public Object invoke(Message message)
{
RemotingMessage remotingMessage = (RemotingMessage) message;
// Merge input arguments
//
List mergedParameters = (List) _beanManager.merge(remotingMessage.getParameters());
remotingMessage.setParameters(mergedParameters);
// Call Java adapter
//
Object result = super.invoke(message);
// Clone result
//
return _beanManager.clone(result);
}
//-------------------------------------------------------------------------
// Internal methods
//-------------------------------------------------------------------------
/**
* @return the Entity Manager Factory
*/
protected Object getEntityManagerFactory()
{
// JNDI Lookup for Entity Manager Factory
//
Context context = new InitialContext();
Object entityManagerFactory = context.lookup("your_emf_jndi_name");
if (entityManagerFactory == null)
{
throw new RuntimeException("Unable to find EntityManagerFactory");
}
return entityManagerFactory;
}
}
It is pretty straightforward: the only difficulty is to set the Hibernate session factory or the entityManagerFactory to the HibernateBeanManager. In our case, we use the JNDI name of the EntityManagerFactory (declared in the EJB3 “persistence.xml” file), but you can inject easily it in another way depending on your code.
The “invoke” method is the heart of the adapter: before server call, the input arguments are merged to Hibernate entities, and the result of the process call is cloned before going to the server side.
Stateful/Stateless mode
Previously, I mentioned that the persistence information hold by proxies and collections is stored in the ‘clone’ operation and reused in the ‘merge’ one.
Basically, Hibernate4GWT provides 2 ways of storing such information: on the server (the stateful mode) or on the pojo (stateless mode).
Stateful mode:
The proxy is stored in the HTTP session. As a consequence, your entity beans do not have any technical inheritance. Magic, isn’t it B)?
To make it work, just edit the Hibernate Adapter below as follows :
- In the ‘init’ method, add the following line:
_beanManager.setPojoStore(new HttpSessionPojoStore()); - At the very beginning of the ‘invoke’ method, put the following line :
HttpSessionPojoStore.setHttpSession(FlexContext.getHttpRequest().getSession(true));
Stateless mode:
The entity beans store minimal persistence information, by inheriting from LazyPojo abstract class. No information is stored on server, which can be very helpful in clustered environments.
First, your entities must inherit from net.sf.hibernate4gwt.pojo.LazyPojo:
public class MyPojo extends LazyPojo
{…}
And here is the LazyPojo class in ActionScript (your value objects have to inherits from it, in a symmetric way to your entity hierarchy) :
package com.mypackage.client.vo
{
import com.adobe.cairngorm.vo.IValueObject;
import mx.collections.ArrayCollection;
[RemoteClass(alias="net.sf.hibernate4gwt.pojo.LazyPojo")]
public class LazyPojo implements IValueObject
{
/**
* The internal lazy properties collection
*/
private var _lazyProperties:ArrayCollection;
/**
* Constructor of Lazy Pojo abstract class.
*/
public function LazyPojo()
{
_lazyProperties = new ArrayCollection();
}
/**
* Getter for lazy properties collection
*/
public function get lazyProperties():ArrayCollection
{
return _lazyProperties;
}/**
* Setter for lazy properties collection
*/
public function set lazyProperties(value:ArrayCollection):void
{
_lazyProperties = value;
}
}
}
The future
BlazeDS is not currently supported by Hibernate4GWT : it means that it as not been tested in different configurations (remoting, messaging, etc…), and there is no dedicated distribution or documentation for Flex applications.
Nevertheless, Hibernate4GWT is currently under heavy refactoring : the code has been split in different jars, and an official new distribution and documentation is scheduled for the end of Q2. It will include GWT new tutorial, some more functionality… and probably BlazeDS support :D
Stay tuned !
7 comments
The LazyInitialisationException is a common problem when using remoting technologies like GWT and BlazeDS. I think your Hibernate4GWT can have indeed a much wider scope then only GWT. Maybe it's a good idea to position the project differently (name change?) so it will attract more people.
Cheers,
Marcel
No idea about OpenSessionInViewFilter issue : this (IMHO bad) pattern was made to prevent such exceptions...
For information, Flex support has been added to hibernate4gwt recently (the project has been renamed to gilead : http://gilead.sourceforge.net).
Regards
Bruno
Regards
Bruno