Tuesday, December 23, 2008

Convenient Disconnected Mode in your Rich Java Application with Hibernate Offliner

In EL4J, we have recently published the open-source Hibernate Offliner, a lightweight layer above Hibernate that supports 2 modes: online and offline (=disconnected) mode. In the online mode the application works on objects that lie in a remote database and that are served via an application server. In the offline mode, the application works on objects that lie in a local database that runs on the client node. The transitions between online and offline mode are handled almost transparently for the application.
A typical use-case of the Hibernate Offliner is a salesforce-support application that runs both when connected to the application server AND when the sales representative is disconnected from the network.

Under the hood
As in traditional database applications, we use the DAO pattern to abstract details of the database access. When switching from online to offline mode, we use a different DAO instance that uses the local database instead and records deleted objects (currently with an interceptor). A DAO registry (a factory for DAO instances) makes the right DAO selection transparent for the application. In order to make an application offline-capable as transparently as possible, a service-layer can be duplicated in the local application. This avoids that the service-layer needs to be fully aware of the offline-capability. All object-operations are done on the level of Hibernate. This means the Hibernate Offliner adds no dependency to the underlying database brand.

Going offline
When switching between the online and the offline mode, you use a set of hibernate queries to indicate what parts of your data you want to replicate on your local database. From this moment on, all future database accesses go to the local database.

Synchronizing data with the master database & going online again
Once you are reconnected to the application server, the local changes (detected via a newer object id/version pair and the recorded deletes (either delete locally only or delete in master db)) get committed in chunks (for scalability) to the master database. What if someone else has changed a same object instance in parallel (e.g. optimistic locking failures)? When going online again, the Offliner indicates conflicts in a data structure that can then be handled by the application (there is no generic conflict handling approach that works in all cases).

Transparency of the offline-capability for your application
For your application it can be to a large extent transparent that it supports online and offline mode: Data accesses work in exactly the same way, whether your application is offline or not. When you adapt an application to support offlining, it just needs to treat the 2 transitions: “go offline” and “go online” (this includes handling of potential conflicts). These two transitions typically require a few calls to the Offliner that can be nicely separated from the rest of the application. In other words, the concern “offlining” is nicely separated from other concerns.
The 2 pictures "Before" and "After" indicate a possible scenario:







Architecture constraints & open issues
What do you need to take into consideration when using this infrastructure? The following limitations exist:
  • Currently only support for primary keys (PKs) of type int, long and String are implemented. The PKs on entity objects are also used to identify whether they are local or remote: so the PKs of the local and the remote database must be distinct.
  • Between online and offline mode, the application mustn't keep objects (as their PKs change).
  • At the moment, the Hibernate Offliner intercepts DAO-access (for deleted objects) on the EL4J generic DAO infrastructure (this is a convenience support that provides DAO with basic CRUD operations without coding). The core of the Hibernate Offliner is independent of this choice.
  • The Offliner cannot guess missing „incoming“ links (so chose your offlining-queries judiciously). E.g., if you have a person class with a parent pointer and children set (1:n, one-way) and offline only the parent, fetching it from the local database and calling getChildren() will return a (wrong) empty set.
  • Hibernate cascading save and the Offliner may not run efficiently together
Acknowledgments
The bean browser was designed and implemented by David Bernhard, thanks for the good work!

More info
Have a look at the svn head of EL4J under framework/modules/offliner or contact the EL4J team.