William has posted 6 posts at DZone. View Full User Profile

Flex for J2EE Developers: The Case for Granite Data Services

11.06.2008
| 18094 views |
  • submit to reddit


For developers having worked on J2EE web applications for many years, getting into Flex will seem both very fun and familiar for the simplicity and power of ActionScript and the UI framework, and quite tedious and frustating when it comes to developing the core application logic and the server integration. In some ways, developing Flex applications with widely used frameworks like Cairngorm and BlazeDS involves a lot of plumbing code (business delegates, service facades, conversions between JPA entities and value objects,...) and will remind you of the days of Struts and EJB2.

The Granite Data Services project was started with the (ambitious) goal of providing to Flex the same kind of development model we were used to with modern J2EE frameworks. The GDS remoting functionality has been designed from the beginning to support serialization of JPA/Hibernate detached entities and to easily connect to the most important J2EE frameworks (EJB3, Spring, Seam, Guice). In most cases, this removes the need to write and maintain service facades and value objects on the J2EE layer. In fact, that finally means that a Flex client can consume the exact same set of server services than a classic web application.

Another repetitive task is to build the ActionScript model classes. GraniteDS provides the Gas3 tool that can automatically generate the ActionScript model classes from the Java data model. With the latest GraniteDS 1.1 release, the process is still improved by the Granite Eclipse builder plugin, which regenerates on the fly the necessary ActionScript classes whenever JPA entities are created or modified in the Eclipse project. You just have to write your JPA data model, and you can directly make use of the generated AS3 classes in the Flex UI layer.

This is already a big step towards a more simple server integration in Flex, but GDS 1.1 brings an even simpler programming model. It targets only JBoss Seam for now but integration with Spring and EJB3 are already on the way. The Tide project aims at providing the same simplicity to build Flex/AIR applications than JBoss Seam has brought to J2EE. Tide requires almost no configuration during development and automates most of the plumbing code generally found for example in Cairngorm business delegates or service locators.

Contrary to other Flex frameworks which goal is to put all business logic on the client, Tide client/server interactions are exclusively done by method calls on exposed services, allowing to respect transaction boundaries, security and validation rules defined by these services.

Tide mainly consists in a Flex library that provides data oriented functionality :

  • An entity cache ensuring that all managed entity instances are unique in a Tide context. This allows in particular to maintain correct data bindings between calls to remote services.
  • A collection wrapping mechanism that enables transparent initialization of lazy collections.
  • A Flex collection component integrated with JBoss Seam’s paged query component that can be used as a data provider for Flex UI components and supports remote sorting and filtering.
  • A complete integration with Seam’s events, both synchronous and asynchronous, allowing a Flex client to observe events raised by the server.
  • Flex validators integrated with server-side Hibernate validator, allowing validation messages either on the fly, or after remote calls.
  • Client support for Seam conversations.
  • A lightweight component-based Flex framework that is deeply integrated with the other features and can advantageously replace Cairngorm or PureMVC.

Let's have a look at a very simple example to see how this works.

Seam component (simply extracted from the Seam booking sample):
@Stateful
@Name("hotelSearch")
@Scope(ScopeType.SESSION)
@Restrict("#{identity.loggedIn}")
public class HotelSearchingAction implements HotelSearching {

@PersistenceContext
private EntityManager em;

private String searchString;
private int pageSize = 10;
private int page;

@DataModel
private List hotels;

public void find() {
    page = 0;
    queryHotels();
}
...
  private void queryHotels() {
    hotels = em.createQuery(
"select h from Hotel h where lower(h.name) like #{pattern} " +
"or lower(h.city) like #{pattern} or lower(h.zip) like #{pattern} " +
"or lower(h.address) like #{pattern}")
         .setMaxResults(pageSize)
          .setFirstResult( page * pageSize )
          .getResultList();
}
...
public List getHotels() {
    return this.hotels;
}

public int getPageSize() {
    return pageSize;
}

public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}

@Factory(value="pattern", scope=ScopeType.EVENT)
public String getSearchPattern() {
    return searchString == null ? "%" : '%' + searchString.toLowerCase().replace('*', '%') + '%';
}

public String getSearchString() {
    return searchString;
}

public void setSearchString(String searchString) {
    this.searchString = searchString;
}

@Remove
public void destroy() {}
}


MXML application:
<mx:Application creationComplete="init();">
 <mx:Script>
   [Bindable]
   private var tideContext:Context = Seam.getInstance().getSeamContext();

   // Components initialization in a static block
   {
      Seam.getInstance().addComponents([HotelsCtl]);
   }

   [Bindable] [In]
   public var hotels:ArrayCollection;

   private function init():void {
      tideContext.mainAppUI = this;   // Registers the application with Tide
   }

   private function search(searchString:String):void {
      dispatchEvent(new TideUIEvent("search", searchString));
   }
 </mx:Script>

 <mx:Panel>
   <mx:TextInput id="fSearchString"/>
   <mx:Button label="Search" click="search(fSearchString.text);/>
 
   <mx:DataGrid id="dgHotels" dataProvider="{hotels}">
     <mx:columns>
       <mx:DataGridColumn headerText="Name" dataField="name"/>
       <mx:DataGridColumn headerText="Address" dataField="address"/>
       <mx:DataGridColumn headerText="City" dataField="city"/>
     </mx:columns>
   </mx:DataGrid>
 </mx:Panel>
</mx:Application>


Tide Flex component:
import mx.collections.ArrayCollection;

[Name("hotelsCtl")]
[Bindable]
public class HotelsCtl {

  [In]
  public var hotels:ArrayCollection;

  [In]
  public var hotelSearch:Object;

  [Observer("searchForHotels")]
  public function search(searchString:String):void {
    hotelSearch.searchString = text;
    hotelSearch.find();
  }
}


Of course this is an overly simple example but there is close to no code which seems unnecessary, while having a clean separation of concerns between the UI, the client component, and the remote service. Building Flex applications could hardly be more easy.

There are a lot of choices out there today for creating rich Internet applications, each with its own set of advantages. When making the decision on which path to take, you want to get started easily but without sacrificing the ability to create a robust, scalable and maintainable application. GraniteDS maintains this balance.

More on GDS site: http://www.graniteds.org.

Published at DZone with permission of its author, William Draï.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Jacek Furmankiewicz replied on Thu, 2008/11/06 - 7:42am

I'm intrigued.

 How is the performance on this thing? Especially marshalling data back and forth between Java and ActionScript and converting it?

Otengi Miloskov replied on Thu, 2008/11/06 - 9:06am

JSR311 with Flex I like a lot. It is easy and still good performance. This solution looks pretty good too and it use the AMF3 protocol is the best performance.

But I have one qustion, which one is the recomended option: EJB3, Guice, POJO?.

William Draï replied on Thu, 2008/11/06 - 1:33pm in response to: Jacek Furmankiewicz

The AMF3 binary protocol is probably one of the most compact and efficient object encoding formats. It's certainly far better that XML or JSON serialization. There are lots of benchmarks on the Adobe Flex site.

Concerning the Tide library, most of its work consists to determine what data has to be transmitted at each remote call by intercepting data modifications on the client and the server. In most cases, this is comparable to simple remote calls, but clearly there is still room for improvement in some particular cases.

 

William Draï replied on Thu, 2008/11/06 - 1:35pm in response to: Otengi Miloskov

Personally I would recommend Seam or Spring. Of course it highly depends on your needs and your knowledge of these technologies.

 

 

cccccc mmmmmmm replied on Thu, 2008/11/06 - 1:57pm

I looked into this recently too, but choosing between graniteDS or BlazeDS, I went with BlazeDS.  I know that granite has a more impressive feature stack, but documentation , and examples are much better with BlazeDS, and getting up and running for someone that is just starting to look into flex, I found it much easier with all documentation on the Adobe site.  

Franck Wolff replied on Fri, 2008/11/07 - 6:08am

If it's only a matter of documentation and examples, play with BlazeDS and go back to GraniteDS when you'll need advanced features ;-)

We are currently working on documentation (that clearly needs improvements) and it should be really better soon.

Icaro Rezende replied on Fri, 2010/07/16 - 10:43am

No xml configurations?

I've been using BlazeDS, but now i got "LazyLoadInicializationException" on my aplication, so i'm moving to GraniteDS...but im seeing a lot of changes, and i looking for some help...William if u have some time to help a new Granite man, on stuffs like, how to use Externalizers in their best way, how to configure all necessary xml, and how to make Hibernate works without Lazy problems...please, send me a email -> icarorez@gmail.com ..i'll keep searching for information, but if u show me the way, i will get there soon and probally better...

Tanks for the Article!

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.