Granite data services

From Wikipedia, the free encyclopedia
Jump to: navigation, search
Granite Data Services
Initial release 2007; 5 years ago (2007)
Stable release 2.2.0 SP2 / March 31, 2011; 11 months ago (2011-03-31)
Preview release 2.2.0 SP1 / January 17, 2011; 13 months ago (2011-01-17)
Development status Production
Written in Flex, Java, Groovy
Platform Platform independent
License LGPL 2
Website www.graniteds.org

Granite Data Services (GraniteDS or GDS) is a comprehensive development and integration solution for building Flex / JavaEE RIA applications. The entire framework is open-source and released under the LGPL v2 license.

It features:

  • A client development framework (Tide) that brings familiar JavaEE concepts to the Flex side: dependency injection, context management, authentication and secured access, bean validation, etc.
  • A comprehensive integration with major JavaEE application servers, frameworks and JPA engines: JBoss, GlassFish, WebLogic, WebSphere, Tomcat and Jetty; Hibernate, EclipseLink, OpenJPA and DataNucleus.
  • An efficient real-time module (Gravity), based on Comet implementations, allowing scalable data-push.
  • Code generation tools (Gas3) that help in replicating Java entity beans and services into their ActionScript3 equivalent. These tools are available as an Eclipse plug-in or an Ant task.
  • Simplified configuration and high performances for critical deployments: most of the configuration is automated by scanning deployment environments and standard libraries are optimized for scalability.

Contents

[edit] Integration and Features Stack

See integration & features stack image for a comprehensive overview of JPA engines, JavaEE frameworks and Java application servers supported by GraniteDS.

[edit] Connecting JavaEE Components with Dependency Injection

GraniteDS integrates with the major JavaEE frameworks:

Additionally, it comes with a basic Java component framework (called Pojo) that let you expose simple Java beans as remote destinations (with 3 possible scopes: request, session or application).

Accessing these frameworks from a Flex application is straightforward if you use:

[edit] Example

With a basic EJB3 like this one (it would work the same with other frameworks):

@RemoteDestination
public interface Hello {
 
    public String welcome(String name);
}
 
@Stateless
@Local(Hello.class)
public class HelloBean {
 
    public String welcome(String name) {
        return "Hello " + name + "!";
    }
}

You will get these ActionScript3 generated classes:

[RemoteClass(alias="path.to.java.Hello")]
public class Hello extends HelloBase {
    // put your custom code here...
}
 
public class HelloBase extends Component {
 
    public function welcome(arg0:String, resultHandler:Object = null, faultHandler:Function = null):void {
        // skipped generated code (actually calls the remote method)...
    }
}

And here is a sample remote call from an injected component:

[In]
public var hello:Hello;
 
hello.welcome("World", welcomeResult);
 
private function welcomeResult(event:TideResultEvent):void {
    trace(event.result); // print "Hello World!"
}

Note that the variable name "hello" is implicitly bound to the uncapitalized name of the EJB3 interface (if your EJB3 was named "MyService", you would use a variable named "myService").

The @RemoteDestination on the interface serves two purposes:

  • It tells the code generator to generate ActionScript3 classes for remote calls.
  • It restricts the EJB that may be called from Flex.

A type-safe annotation for dependency injection (Inject) is also available for the Spring and CDI frameworks.

[edit] JPA and Lazy-Initialization

GraniteDS integrates with the major Java Persistence API engines available in the JavaEE world:

This integration is again closely coupled with code generation tools available in GraniteDS that replicate on-the-fly JPA entities into an ActionScript3 model. The serialization of JPA entities from Java to Flex (or from Flex to Java) uses a custom mechanism called externalization, that preserves the entire state of the objects, instead of keeping only public properties.

With code generation and externalization, GraniteDS is able to retain JPA entities metadata in the ActionScript3 entities, such as their entire initialization states. Serializing partially uninitialized entities with GraniteDS guaranties that:

  • Uninitialized properties are not unexpectingly initialized during the serialization (for both single-valued associations and collections).
  • ActionScript3 beans keep internally informations about unitialized properties.
  • Sending partially uninitialized ActionScript3 beans back to Java (with these metadata) allows the recreation of JPA entities with the same uninitialized properties.

Thus, this mechanism prevents database and memory overloads (only expected references between entities are actually loaded end serialized) and unexpected database updates (an uninitialized property sent back from Flex to Java will not be considered as a null reference or an empty collection).

Together with the Tide client framework, you may even benefit from transparent initialization, when accessing a lazy-loaded property in your Flex code: this access will trigger an asynchronous initialization request to the server that will serialize back the initialized value.

[edit] Example

With a simple data model like this one (note the LAZY fetch type of the addresses set):

@Entity
public class Person {
 
    @Id @GeneratedValue
    private Integer id;
 
    @Version
    private Integer version;
 
    @Basic
    private String firstname;
 
    @Basic
    private String lastname;
 
    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, mappedBy="person")
    private Set<Address> addresses;
 
    public Integer getId() {
        return id;
    }
 
    public String getFirstname() {
        return firstname;
    }
    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }
 
    public String getLastname() {
        return lastname;
    }
    public void setLastname(String lastname) {
        this.lastname = lastname;
    }
 
    public Set<Address> getAddresses() {
        return contacts;
    }
    public void setAddresses(Set<Address> addresses) {
        this.addresses = addresses;
    }
}
 
@Entity
public class Address {
 
    @Id @GeneratedValue
    private Integer id;
 
    @ManyToOne(optional=false)
    private Person person;
 
    // skipped code...
}

You will get these ActionScript3 generated classes:

[Bindable]
[RemoteClass(alias="path.to.java.Person")]
public class Person extends PersonBase {
    // put your custom code here...
}
 
public class PersonBase implements IExternalizable {
 
    private var __initialized:Boolean = true;
    private var __detachedState:String = null;
 
    private var _id:Number;
    private var _version:Number;
    private var _firstname:String;
    private var _lastname:String;
    private var _addresses:ListCollectionView;
 
    public function get id():Number {
        return _id;
    }
 
    public function get fisrtname():String {
        return _firstname;
    }
    public function set firstname(value:String):void {
        _firstname = value;
    }
 
    // other getters/setters...
 
    public override function readExternal(input:IDataInput):void {
        __initialized = input.readObject() as Boolean;
        __detachedState = input.readObject() as String;
        if (meta::isInitialized()) {
            _id = function(o:*):Number { return (o is Number ? o as Number : Number.NaN) } (input.readObject());
            _addresses = input.readObject() as ListCollectionView;
            _firstname = input.readObject() as String;
            _lastname = input.readObject() as String;
            _version = function(o:*):Number { return (o is Number ? o as Number : Number.NaN) } (input.readObject());
        }
        else {
            _id = function(o:*):Number { return (o is Number ? o as Number : Number.NaN) } (input.readObject());
        }
    }
 
    public override function writeExternal(output:IDataOutput):void {
        // ...
    }
}

The important things to notice here are:

  • The version field (used with optimistic locking) is purely opaque (no getter or setter) and remains as is in the generated code.
  • The id field is read only (no setter) and remains as is in the generated code.
  • The generated code add two meta fields (__initialized and __detachedState) that keep information about the initialization state of the entity.
  • When a Person entity is uninitialized, only its id is serialiazed (with the meta fields), so only this information is read (see the readExternal method in the ActionsScript3 bean).
  • Since the addresses set is lazily fetched, its content will not be serialized unless you explicitly initialize it in the Java code (or if you use Tide). The collection in the ActionScript3 code will be empty, with a meta information stating that the collection is unitialized.

[edit] Real-time messaging (Comet/Servlet 3.0)

Real-time messaging lets a Flex application publish to and receive from other clients data in so-called real-time. Messages may also be sent from the JavaEE server itself (known as data push), such as special alerts or progress events.

The implementation of real-time messaging in GraniteDS is based on the Comet model, now standardized in the Servlet 3.0 specification (JSR-303). Specifically, the transport protocol relies on a partial implementation of the standard Bayeux protocol.

On the server side, this implementation relies on specific servlet containers capabilities (such as Tomcat's CometProcessor or Jetty's Continuations) or on standardized Servlet 3.0 implementations when available. These implementations allow scalable real-time messaging by providing a way of releasing servlet threads when the HTTP connections are idle: the standard thread-per-connection model is replaced by a much more efficient thread-by-request model (see explanations in Jetty documentation).

GraniteDS uses a long polling model for managing HTTP requests between Flex client applications and the server: when a request for incoming data stays idle for a given amount of time (usually 30 s), it is closed and immediately reopened. This model prevents unexpected closing of idle HTTP connections traversing Internet proxies.

Real-time messaging with GraniteDS is based on a consumer–producer model and lets you define topics and subtopics for partitioning groups of messages. You may also filter topic's messages by using specific selectors.

[edit] Example

Implementing a basic chat application with GraniteDS requires configuring a specific servlet in the web.xml file of your application:

<servlet>
    <servlet-name>GravityServlet</servlet-name>
    <servlet-class>org.granite.gravity.servlet3.GravityAsyncServlet</servlet-class>
    <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
    <servlet-name>GravityServlet</servlet-name>
    <url-pattern>/gravity/*</url-pattern>
</servlet-mapping>

The server configuration requires also the definition of a specific destination (services-config.xml):

<services-config>
 
    <services>
        <service id="messaging-service"
            class="flex.messaging.services.MessagingService"
            messageTypes="flex.messaging.messages.AsyncMessage">
            <adapters>
                <adapter-definition id="default" class="org.granite.gravity.adapters.SimpleServiceAdapter" default="true"/>
            </adapters>
 
            <destination id="gravity">
                <channels>
                    <channel ref="my-gravityamf"/>
                </channels>
            </destination>
        </service>
    </services>
 
    <channels>
        <channel-definition id="my-gravityamf" class="org.granite.gravity.channels.GravityChannel">
            <endpoint
                uri="http://{server.name}:{server.port}/{context.root}/gravity/amf"
                class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>
 
</services-config>

In a Flex application, you[who?] may then use the Consumer and Producer classes in order communicate with other clients:

private function connect(nickname:String):void {
    consumer = new Consumer();
    consumer.destination = "gravity";
    consumer.topic = "discussion";
    consumer.subscribe();
    consumer.addEventListener(MessageEvent.MESSAGE, messageHandler);
 
    producer = new Producer();
    producer.destination = "gravity";
    producer.topic = "discussion";
}
 
private function messageHandler(event:MessageEvent):void {
    var msg:AsyncMessage = event.message as AsyncMessage;
    var message:String = msg.body as String;
 
    trace('You have received a message: ' + message);
}
 
private function send(message:String):void {
    var msg:AsyncMessage = new AsyncMessage();
    msg.body = message;
    producer.send(msg);
}

In this quick code snippet, all Flex applications subscribe and publish to the same topic ("discussion"), so each message sent by a client is dispatched to other clients.

Together with the Tide framework, one may quickly configure a JavaEE server to automatically dispatch an entity's modifications in a specific topic: updates of shared data are then instantly visible in all clients (see GraniteDS documentation for more details).

[edit] Bean Validation

TODO

[edit] Advanced Features

TODO

[edit] External links

Personal tools
Namespaces

Variants
Actions
Navigation
Interaction
Toolbox
Print/export