Tutorial: Master-Detail screen in Flex, backed up by Grails application

Maarten Winkels

In this blog I will show how to create a simple Master-Detail screen in Flex, how to back it up by an application is Grails and how to publish changes via JMS to all Flash clients. Important topics will be binding and remote java-object invocation in Flex and configuring JMS.

A Master-Detail view is a view with a master list, showing a collection of items and a detailed view, most often consisting of a form, in which the a single item can be edited. Clicking an item in the master list will display the details in the detail view.
Master-Detail

The combination of a framework for building RIA's (Flex) and a Java based dynamic framework for building services (Grails) seems very promising. I think the high productivity that can easily be achieved by this combination will make it a very attractive choice for your next big project.


In this blog we will implement a "double" master detail, as can be seen in the image above. A person may have multiple addresses. The list is an address book and the view displays a number of attributes of the people in the address book (first name, last name and city and country of their first address). If an item is selected, it is displayed in the adjacent form and the list in that form shows all addresses of that person. If an address is clicked its details are shown in the form below.

Binding

In flex we will use an ArrayCollection to contain the list and this list will be displayed using a DataGrid.


  
  
    
      
      
      
      
    
  
...

The mechanisme that is used to get the data from the addressBook into the DataGrid is called "binding". This is done by using curly brackets around the expression, dataProvider="{addressBook}". Binding will make sure that if a change is made in the referenced object (in this case the addressBook or its content) the changes are reflected in the referrer (in this case the DataGrid). Binding works only in one direction! When the value in the referrer (DataGrid) is changed, the bound value (the addressBook) will not be updated. Some components (like the DataGrid) add this functionality and will update the underlying model. For this tutorial we will use the datagrid in editable="false" mode, since we want to edit the data by using the form.

The columns are filled by looking up the properties specified as dataField on the objects in the addressBook. But what are these objects? The are actionscript objects of the Person type.

package
{
  import mx.collections.ArrayCollection;

  [Bindable]
  public class Person
  {
    public var firstName:String;

    public var lastName:String;

    public var addresses: ArrayCollection = new ArrayCollection();

    public function get firstAddressCity():String {
      if (addresses.length == 0) {
        return "";
      }
      return addresses[0].city;
    }

    public function get firstAddressCountry():String {
      if (addresses.length == 0) {
        return "";
      }
      return addresses[0].country;
    }
  }
}

This class defines two simple properties and a collection. In addition it defines two getters that are also bound to in the DataGrid view. It is important to mark this class as [Bindable], so all properties of the class can be used in bind expressions.

Now when a row is selected in the list, we need to bind the values in the selected object to fields in a form. Then if the data in the form changes, we want to update the values in the selected item. Therefore we need Double Binding.

...
  
  
    
    
      
    
    
      
    
  
...

Now all we need to do is add some buttons and eventhandlers to the components...

...
  
...
  
  
  
  
    
...

... and the master detail view is done! The double binding will keep the fields in the form, the fields in the datagrid and the data in the model in synch.
The master-detail for the address works in much the same way and is a little different. A List is used to display the addresses in stead of a DataGrid and in the list the Address.toString() method is invoked to show the addresses. The eventhandling is also slightly different: A new Address is added to the list right away, instead of having an "Update Address" button.

Remoting

Now editing this data is surely fun, but they are not persistently stored nor shared between users. To do that, we'll nee to add some server component and remoting. Remoting from Flex can be done using BlazeDS from Adobe Labs. This is a "Java remoting and web messaging technology that enables developers to easily connect to back-end distributed data and push data in real-time to Adobe® Flex™". So we can write a Web application in Java to communicate to the backend using BlazeDS. In stead of starting to write a web.xml and a Servlet, let's look at Grails for a moment. This is a framework for creating web applications based on Java and Groovy and it has a flex plugin! That looks very promising.

First we create a new Grails application (grails create-app addresses) and download the flex-plugin (grails install-plugin flex). This takes some time to download and thus gives us some time to think. What do we really want to do in this Web application? We only need to domain classes (Person and Address) that should be persisted (this is standard with grails) and then have a service that exposes some methods to the flex front end. Saying this out loud takes almost as much time as writing it in Grails:

class Person {

  String firstName
  String lastName
	
  List addresses
	
  static hasMany = [addresses:Address]
  static fetchMode = [addresses:"eager"]
}

The Person class has the same two simple properties and the list of addresses. The list is declared as a hasMany relationship and the fetchmode is set to eager. We'll need to load the list of addresses for each person that we fetch, since otherwise the client will get LazyInitializationExceptions.

class Address {
  String street;

  int Number;

  String postalCode;	

  String city;

  String country;
}

The Address class is far simpler. It only contains the simple properties that the actionscript class contains.

class AddressBookService {
	
  static expose = ['flex-remoting']; 

  def List findAllPersons() {
    return Person.createCriteria().listDistinct {};
  }

  def Person get(id) {
    return Person.get(id);
  }
    
  def void update(Person p) {
    p.save();
  }
    
  def void remove(Person p) {
    p.delete(flush:true);
  }
}

The AddressBookService exposes itself as a flex-remoting service. This will make sure the Grails-Flex plugin will register the object with BlazeDS and it is made available to the Flex client.

The changes to the Flex code are pretty complex:

  1. We need to annotate the actionscript classes with the RemoteClass annotation, to indicate which ActionScript class must be used for which Java object.
    [RemoteClass(alias="Person")]

    Here the alias is the Java class name, in this case the Person class in the default package.

  2. We need to change our ActionScript classes to contain the generated domain class properties id and version. These properties are very important for persisting the data, since Hibernate (used by Grails) will find out by looking at the version whether this is a new or existing persistent object and by looking at the id what the identity of the existing object is. If we omit these fields from the action script classes, they will silently get lost during remoting and every object will look like a new object to Hibernate. It will thus do an insert for each new object.
        public var id:*;
    
        public var version:*;

    We don't care about the type of these properties from the ActionScript code, so we'll just use the wild cards.

  3. A RemoteObject is introduced, along with some event handling code.
      
        
        
        
        
      
      
        

    The destination of the RemoteObject maps directly on the class name of our grails service. The method declarations in the remote object map to its methods and the result event handler will be called asynchronously with the result of the method call when it is called from the Flex code.

  4. The eventhandling methods for our buttons have to be changed to use the new service. A "Refresh" button is also added to get the complete addressbook from the server.
      
      
        

    Notice that the handling of the result of a call to the service is not defined with the calling code: it is defined in the method declaration of the RemoteObject.
    Since we already structured the code with these changes in mind, the structural changes are fairly minimal. We only need to change the implementation of these three methods to make it work with the Grails service.

To get the application running, the .mxml and .as files need to be copied to the web-app directory of the Grails application. Here the Flex Webtier Compiler can pick up the source code and copile the application on request. if you go to http://localhost:8080/addresses/addresses.mxml, the interface will show up and it will be connected to the Grails application through BlazeDS.

That's really all there is to it! These 6 source files (addresses.mxml, Person.as, Address.as, AddressBookService.groovy, Person.groovy and Address.groovy) form a complete Rich GUI to an online AddressBook. None of the code is really complex and hardly any plumbing is needed.

While this is really great, I'm a little annoyed with this "Refresh" button and the fact that the user will only see changes made by others when he clicks this button. Lets see what we can do...

Server-push through JMS

Flex can be configured to listen to a JMS destination and Grails has a JMS plugin. We can use this to push a new data set to all clients whenever data is changed on the back-end. If we are going to use JMS, we need a JMS-provider, preferably one that can be embedded in a Grails application. Browsing the internet I found ActiveMQ from Apache, that is open source and can be embedded easily in a Spring application. After installing the JMS Plugin, downloading ActiveMQ and adding the necessary libraries to lib folder of the Grails application, we're all set.

  1. We need to configure a connectionFactory bean in spring.
    
      
        
          
            
              vm://localhost
            
          
        
      
    

    This will create the connection factory and also start an embedded broker.

  2. We need to send the updates out to a topic whenever a change is made. This is done in the AddressBookService.
      def void update(Person p) {
        p.save();
        sendUpdate();
      }
      def void remove(Person p) {
        p.delete(flush:true);
        sendUpdate();
      }
      def private void sendUpdate() {
        try {
          sendPubSubJMSMessage("addresses", findAllPersons());
        } catch (Exception e) {
          log.error("Sending updates failed.", e);
        }
      }

    The sendPubSubJMSMessage method (added to all service classes by the Grails JMS plugin) is used to send a message to the "addresses" topic. The message will contain the new list of addresses.

  3. BlazeDS can be configured to lookup a JMS destination in JNDI. The services-config.xml needs to be changed to contain the JmsAdapter and the new destination for Flex clients to subscribe to.
        
    
          
            
          
    
          
            
              
                javax.jms.ObjectMessage
                ConnectionFactory
                addresses
                NON_PERSISTENT
                DEFAULT_PRIORITY
                AUTO_ACKNOWLEDGE
                false
                
                  
                    Context.PROVIDER_URL
                    vm://localhost
                  
                  
                    Context.INITIAL_CONTEXT_FACTORY
                    org.apache.activemq.jndi.ActiveMQInitialContextFactory
                  
                  
                    topic.addresses
                    addresses
                  
                
              
            
          
        

    Configuring the JNDI context for Flex is actually quite tricky. The org.apache.activemq.jndi.ActiveMQInitialContextFactory is a very basic JNDI context and it uses properties to register queues and topics. Normally the properties would reside in a jndi.properties file. Here, these properties are specified in the service-config.xml itself. The property topic.addressesaddresses to register the topic with the physical name "addresses" (the property-value) in JNDI as "addresses" (the property-key-suffix). This is really an ActiveMQ JNDI oddity.

  4. Now the Flex client can be changed to listen to the topic
    
    ...
      
    ...

    The Consumer is configured to listen to the "updatesJmsTopic", that is configured in the services-config.xml. Whenever a message comes in, its body is processed by the setAddressBook method, that will copy all the data to the ArrayCollection to be displayed in the DataGrid.
    Don't forget to subscribe the consumer at start up. We'll also load the initial data at start up.

This completes our RIA application. Updates will now be send through JMS to all Flex clients.

Conclusion

Creating a RIA with Flex and Grails is relatively simple. Developers can really focus on developing the GUI or the server side business logic. Most plumbing can be left to the sensible defaults and if not, changes can be made using "standard" frameworks like Spring or Hibernate. Whenever an advanced feature is necessary, developers can lean on their knowledge on Java or frameworks like Sping and Hibernate to quickly intergrate the functionality into the Grails application. I think these features make the Flex/Grails combo a very suitable candidate for developing RIAs on projects of all sizes.

A few remarks:

  1. It seems like duplication to have both the Grails domain classes and ActionScript classes. The Grails Flex plugin website hints at a feature that will generate the ActionScript classes on the fly. On the other hand, it might be usefull to separate the two, since in some occasions we need client-side business logic (like the firstAddressCity and firstAddressCountry properties) on the client-side domain objects. Nevertheless, this duplication is a bit brittle and errors (like typos in field names) are easily made.
  2. Currently the application is using an AMF channel and polling to send the updates to the clients. In Flex Data Services 2, a different type of channel (RTMPChannel) could be used to push data from the server to the client side. Unfortunately BlazeDS does not contain this functionality.

The sources of this application can be downloaded:

The package with the complete grails application with the plugins was almost 60 MB, so to test the application, you'll have to create a Grails application and add the plugins manually.

Comments (23)

  1. Marcel Overdijk - Reply

    February 21, 2008 at 10:16 am

    Very good article! As the creator of the Grails plugin it's nice to see it's usage!

    Note that in the future I hope to extend the plugin with some scaffolding generation as well.

    Cheers,
    Marcel

  2. Will Rogers - Reply

    April 11, 2008 at 8:43 am

    This looks very handy. Thanks for your work. Incidentally, BlazeDS includes ActiveMQ, so it seems like you should not need a separate install of ActiveMQ.

  3. dersteppenwolf - Reply

    April 16, 2008 at 1:35 am

    great tutorial!!!

  4. Ryan Bard - Reply

    June 10, 2008 at 4:53 pm

    Great tutorial. Always nice to see practical examples. I use Flex with Python/Zope for the most part, so the Grails exposure was a plus as well.

    I noticed that the BlazeDS docs on Adobe Open Source indicate that "Real-time server push over standard HTTP" based on AMF is a part of the open source product. Is this a new development, maybe when Flex 3 was launched? Maybe this is the includion of ActiveMQ mentioned by Will Rogers above? Just curious as how this might change the tutorial.

    Thanks again!

  5. Rollo Tomazzi - Reply

    July 16, 2008 at 5:12 pm

    Kinda works once you go through the painful process of debugging without debuggers...
    Several things would be worth mentioning like, for example, the declaration of the remote object (
    ) in the mxml file which must come *before* using it, meaning at the top of the file. If you don't, you'll spend hours trying to figure out what's wrong because of course, no error will pop up.
    Thx,
    Rollo

  6. Ron Lane - Reply

    July 17, 2008 at 4:36 pm

    Maarten,
    It's been 5 months since you posted this very helpful piece. Have you gone further since then in our thoughts about integrating Grails and Flex. Have you found a way to eliminate the JMS to have the Flex changes be reflected back into the Grails underlying data?
    Ron

  7. Jonathan Morales - Reply

    October 18, 2008 at 6:42 pm

    HI,
    this is a really helpful article as we all know, but what about using this technologies in a production project.
    I have no problems whit grails nor flex nor even blazeDS but i have my doubts about the flex plugin. As you can see in the plugin page "It's not sure if the plugin works in production mode as it is only tested in development mode".
    what advise can i get from you.

  8. Jonathan Morales - Reply

    October 18, 2008 at 6:44 pm

    did i say thank you a lot?

  9. Len - Reply

    November 20, 2008 at 7:20 pm

    Thank you for tutorial. It certainly helps to understand how Flex remoting works.

    I downloaded sources and noticed that it is slightly different from the one in the text.

    Groovy to Flex binding is not quite clear. How does it marshal groovy domain objects to flex [bindable] ones. Is it all handled by groovy plug-in?

  10. Anatoliy - Reply

    January 14, 2009 at 11:36 am

    It's a really a good tutorial!
    There is a new plug-in available ActiveMQ (it's in develop mode now, I suppose it will not die)

    It'll be great to update this tutorial.
    Before installing jms run command:
    grails install-plugin activemq

    There are some errors in addresses.mxml
    Tags
    Contain space char.

  11. Bens Blog » Flex/Java tutorials - Reply

    February 24, 2009 at 12:44 pm

    [...] Flex with Grails/BlazeDS [...]

  12. Luis - Reply

    May 31, 2009 at 1:28 am

    Has anyone tried this with Livecycle Data Services (DS) instead of BlaseDS? Should be about the same but with support for RTMPChannel.

  13. Luis - Reply

    May 31, 2009 at 1:37 am

    The Sources "Downloads" are not working. Thanks.

  14. Luis - Reply

    May 31, 2009 at 5:42 am

    What would be the steps in making an AIR App?

  15. David - Reply

    July 8, 2009 at 4:33 pm

    I am not even able to get working the medium variant with refresh button. I am using grails 1.1.1 with hibernate 3.3.3.1 and I always get:
    ERROR hibernate.LazyInitializationException - failed to lazily initialize a collection of role: Person.addresses, no session or session was closed

  16. Rag - Reply

    September 16, 2009 at 2:36 am

    Wonderful information and implementation.
    Unfortunately I can't test the example because none of the zip file is downloaded.
    It seems that the files are deleted from the server.
    It will be nice if you can upload it again.

  17. kwame - Reply

    October 18, 2009 at 2:05 pm

    sorry download files don't exist.
    Please upload them again.
    Thank you.
    Kwame

  18. Fedor - Reply

    November 4, 2009 at 2:52 pm

    To David:
    if you have LazyInitializationException try wrap your service method with DomainClass.withTransaction {}

  19. Fred - Reply

    January 19, 2010 at 11:55 pm

    A couple of other folks also asked for this so I'll add my voice to the request, please make the three zip files available to us (or just let us know that you no longer plan to do that so that we stop asking). Thanks for doing that and thanks for the good posting.

  20. Fred - Reply

    January 20, 2010 at 12:08 am

    Here is the correct base URL for downloading the three files mentioned above. Just add each of the three zip file to this URL and you'll be able to download this example.

    http://blog.xebia.com/wp-content/uploads/2008/02/

  21. Maarten Winkels - Reply

    January 23, 2010 at 4:44 am

    Hi Fred et. all.,

    I've updated the links for the download to work from whichever URL your using, so that should be fine now.

    Cheers,

    -Maarten

  22. [...] Learn Master Detail Adobe Flex Application  Simple, Remoting with grails [...]

  23. Arilson - Reply

    May 24, 2010 at 2:06 am

    I couldn't make it to work.
    Did anybody attempted in NetBeans?

Add a Comment