Data Management with GraniteDS

Introduction

Flex itself can’t access any backend Java logic. One could use HTTP Services to access backend systems but if you want to transfert data as quick as possible you should use something like Adobe BlazeDS or GraniteDS. BlazeDS is provided by Adobe and does not have several features the commercial product LiveCycle Data Services provides ( click here for a comparison chart ).

For our project we create at work two features were essential BlazeDS does not provide:

  1. handling lazy loading
  2. data management ( aka data-push)

LiveCycle Data Services is much to expensive so I looked around and found GraniteDS. GraniteDS is a open source solution which provides enough functionality to replace ( at least in our project ) Adobe Live Data Services.

Unfortunately the documentation for GraniteDS is not so comprehensive, one has to collect it’s information from various blogs and it is difficult for a beginner to get data management running ( at least it was for me ). Florian Müller wrote an good article about GraniteDS in the german Java Magazin June 2010 and July 2010 in which he gave an introduction to GraniteDS and data management for beginners.

But – I don’t like this but – Florian didn’t use my technology stack. He used J2EE, I use Spring and each backend technology needs it’s own configuration. So I decided to modify Florian’s example so it works with Spring on the server side. My example is not as stylish as Florian’s, but it works and should help anybody to get in touch with GraniteDS and Spring.

For my example I use GraniteDS 2.1.0, Flex 4.1.16076, Spring 3.0.3.RELEASE and Hibernate 3.5.4-Final.

Examples

Often it is better to see an example before reading the details. The Video shows what data management means in practice.

If you prefer to explore the examples by yourself, please follow this link. I advocate to open the application in two different browsers ( or even on two different computers ) so you can be shure the data management is done by the server and not faked in the browser.

Preparations on the client side

To get GraniteDS and DataManagement running there must be things done in the Flex client code and on the server side. Let’s start with the client. GraniteDS uses the Tide framework to sync objects on Flex client and Java server side.

In the Flex client application there must be done two major steps.

Initialise Tide Spring context

The Tide context has to be initialised. There are several contexts available, in this example the Spring context is used because Spring is the framework we use on the server side. The preinit phase of the application is ideal to run the initialisation method:
private function preinit():void
{
Spring.getInstance().initApplication();
Spring.getInstance().addComponent("myTopic", DataObserver);
Spring.getInstance().addComponentWithFactory("serviceInitializer", DefaultServiceInitializer, {contextRoot: "/granite-spring-tide"});
}

It is absolutely necessary to set the contextRoot of the application. In case of the example application this has to be “/granite-spring-tide” because the WAR file as the result of the maven build process is named granite-spring-tide.war and is automatically deployed in “/granite-spring-tide” (at least on a standard Tomcat installation).

Register to message channel

After the initialisation the tide spring context has to be register itself for messages on the message channel provided by the server side to be informed if a managed object changes. As the name of the method suggests this is done in the initialization phase of the application.

private function init():void
{
// client server synchronization init
tideContext.myTopic.subscribe();
}

Model Design

Model objects (aka ValueObjects aka Beans aka… whatever) used in the application like the Employee object have to extend an AbstractEntity object. This AbstractEntity contains some attributes which are required by Tide to recognize changes in the object. A generated AbstractEntityBase object generated by the GraniteDS Gas3 AS3 object generator could look like this:

/**
* Generated by Gas3 v2.1.0 (Granite Data Services).
*
* WARNING: DO NOT CHANGE THIS FILE. IT MAY BE OVERWRITTEN EACH TIME YOU USE
* THE GENERATOR. INSTEAD, EDIT THE INHERITED CLASS (AbstractEntity.as).
*/

package org.graniteds.test.spring.model {

import flash.events.EventDispatcher;
import flash.utils.IDataInput;
import flash.utils.IDataOutput;
import flash.utils.IExternalizable;
import mx.core.IUID;
import org.granite.collections.IPersistentCollection;
import org.granite.meta;
import org.granite.tide.IEntity;
import org.granite.tide.IEntityManager;
import org.granite.tide.IPropertyHolder;

use namespace meta;

[Managed]
public class AbstractEntityBase implements IExternalizable, IUID {

[Transient]
meta var entityManager:IEntityManager = null;

private var __initialized:Boolean = true;
private var __detachedState:String = null;

private var _id:int;
private var _uid:String;
private var _version:Number;

meta function isInitialized(name:String = null):Boolean {
if (!name)
return __initialized;

var property:* = this[name];
return (
(!(property is AbstractEntity) || (property as AbstractEntity).meta::isInitialized()) &&
(!(property is IPersistentCollection) || (property as IPersistentCollection).isInitialized())
);
}

[Id]
[Bindable(event="unused")]
public function get id():int {
return _id;
}

public function set uid(value:String):void {
_uid = value;
}
public function get uid():String {
return _uid;
}

[Version]
[Bindable(event="unused")]
public function get version():Number {
return _version;
}

meta function merge(em:IEntityManager, obj:*):void {
var src:AbstractEntityBase = AbstractEntityBase(obj);
__initialized = src.__initialized;
__detachedState = src.__detachedState;
if (meta::isInitialized()) {
em.meta_mergeExternal(src._id, _id, null, this, ‘id’, function setter(o:*):void{_id = o as int}, false);
em.meta_mergeExternal(src._uid, _uid, null, this, ‘uid’, function setter(o:*):void{_uid = o as String}, false);
em.meta_mergeExternal(src._version, _version, null, this, ‘version’, function setter(o:*):void{_version = o as Number}, false);
}
else {
em.meta_mergeExternal(src._id, _id, null, this, ‘id’, function setter(o:*):void{_id = o as int});
}
}

public function readExternal(input:IDataInput):void {
__initialized = input.readObject() as Boolean;
__detachedState = input.readObject() as String;
if (meta::isInitialized()) {
_id = input.readObject() as int;
_uid = input.readObject() as String;
_version = function(o:*):Number { return (o is Number ? o as Number : Number.NaN) } (input.readObject());
}
else {
_id = input.readObject() as int;
}
}

public function writeExternal(output:IDataOutput):void {
output.writeObject(__initialized);
output.writeObject(__detachedState);
if (meta::isInitialized()) {
output.writeObject((_id is IPropertyHolder) ? IPropertyHolder(_id).object : _id);
output.writeObject((_uid is IPropertyHolder) ? IPropertyHolder(_uid).object : _uid);
output.writeObject((_version is IPropertyHolder) ? IPropertyHolder(_version).object : _version);
}
else {
output.writeObject(_id);
}
}
}
}

If your development process is to implement the Java objects first, then you can generate all AS3 objects automatically using the Gas3 object generator. In this example this was done using maven (see example sources).

GraniteDS server side

The main steps are done on client side, now the server side has to be prepared.

Implement Java backend

The Java backend can be implemented as usual using the Spring framework. Feel free to express yourself! ;)
The only thing you have to do is to extend the AbstractEntity Java object in all your model objects. The AbstractEntity may look like this:

package org.graniteds.test.spring.model;

import java.io.Serializable;
import java.util.UUID;

import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.Version;

import org.granite.tide.data.DataPublishListener;

/**
* The abstract entity contains all attributes which are necessary to persist
* objects in a JPA environment.
*
* The original file can be found in the granite-spring-tide examples delivered
* by GranitesDS.
*
* @author Franck WOLFF, Christoph Guse
*
*/
@MappedSuperclass
@EntityListeners({DataPublishListener.class})
public class AbstractEntity implements Serializable {

/**
* automatically generated serial
*/
private static final long serialVersionUID = 7923534286286379513L;

/**
* The database unique ID.
*
*/
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private int id;

/* “UUID” and “UID” are Oracle reserved keywords -> “ENTITY_UID” */
@Column(name = “ENTITY_UID”, unique = true, nullable = false, updatable = false, length = 36)
private String uid;

/**
* The version number is used by GraniteDS to check if the object has
* changed.
*/
@Version
private Integer version;

public Integer getId() {
return id;
}

public Integer getVersion() {
return version;
}

@Override
public boolean equals(Object o) {
return (o == this || (o instanceof AbstractEntity && uid().equals(
((AbstractEntity) o).uid())));
}

@Override
public int hashCode() {
return uid().hashCode();
}

public static class AbstractEntityListener {

@PrePersist
public void onPrePersist(AbstractEntity abstractEntity) {
abstractEntity.uid();
}
}

private String uid() {
if (uid == null)
uid = UUID.randomUUID().toString();
return uid;
}

public String getUid(){
return uid;
}

public AbstractEntity() {
super();
uid();
}

}

As you can see only the annotation “@EntityListeners({DataPublishListener.class})” is Tide specific, all the rest are standard Java persistence annotations.

In the service implementations (of course the services are implemented against interfaces in this example) need some special Tide annotations like theses:

@TideEnabled
@DataEnabled(topic = "myTopic", params = ObserveAllPublishAll.class, publish = PublishMode.ON_SUCCESS)

Personally I don’t like to have Tide dependencies in my backend Java logic but I didn’t find another working solution. Please leave a note if you have a more elegant approach which decouples Tide and the Java backend implementation.

Web Application

For the example I created a GraniteEmployeeService in the webapplication ( in hope not to be able to decouple Tide and the Java backend ). This service has the same Tide annotations like the Java services backend implementations and there is nothing special in this service.

Spring application context

To make the GraniteEmployeeService available in the Flex client you have to add this


in your spring application context file. I was not able to configure the flex-filter in this place so I was forced to add a services-config.xml in the /WEB-INF/flex webapplication folder (please see the content in the provided sources).

web.xml

In the web.xml you have to add a GraniteConfigListener beside the normal Spring ContextLoaderListener and you have to register a GravityServlet beside the Spring DispatcherServlet. For the example Tomcat was used so the GravityTomcatServlet listener.

Ready to run or where to find the sources

Well, you are almost done. But only almost because I left out some configuration or forgot to mention them. That’s why you really SHOULD have a look at the sources of this example.
Here you can find the SOURCECODE of this example.

Of couse you should have a look at the GraniteDS Documentation to get an better overview how GraniteDS work.

After reading this you should be able to run Flex applications using Spring backend services. If you have questions or ideas how to improve the example’s sourcecode, please leave a comment.

About these ads

4 thoughts on “Data Management with GraniteDS

    • Hi Francois,

      can you give some more information under which circumstances the datamanagement does not work? I tested my example application and datamanagement works.
      If you built your own version from the sources and deployed it in a tomcat 6.x server it is absolutely necessary to install the tomcat native library.

      Christoph

  1. Hi,
    fine article – Thanks for your work. I am looking for a tutorial on how to set up the basic development infrastructure. I found a lot of information about implementing the data management, but unfortunately no ressource what I have to install on either client side or server side. Do you know any source for that ?
    Regards

    • Hi Christian,

      unfortunately I don’t know a good resource where you can find a step to step tutorial how to setup a Data Management environment.

      The GraniteDS homepage gives a good overview over the GraniteDS architecture. As you can see you need on the server side a Java Server ( I often use Apache Tomcat ) into which you can deploy the WAR file generated by Maven in my Project.
      For my example that is all you need serverside.

      On client side you need nothing more than a webbrowser and the installed Adobe Flash plugin.

      I hope I could help.

      Regards,
      Christoph

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s