Integrating Adobe AIR, Cairngorm, PureMVC, LiveCycle Data Services (LCDS), MySQL and Hibernate

This post is going to cover the use of Adobe AIR, PureMVC, cairngorm, MySQL, LiveCycle Data Services (LCDS), and Hibernate. It utilizes these tools to create a simple image management system. The focus is on the configuration of the server to integrate Hibernate with LCDS and access that configuration from an AIR client.


The Tools:

Flex Builder 3

You can use any IDE that you prefer, but I will be using Flex Builder 3 (with Java Development Tools installed).

LiveCycle Data Services [Download]

For the purposes of demonstration, and many production needs that don't require the additional processing power, the free version of LCDS with the single CPU limitation is more than adequate. Why not use BlazeDS? Well, BlazeDS doesn't have the RTMP support or the Hibernate adapters that are included with its big brother LCDS. My primary goal is to get Hibernate functioning with Flex/AIR, and LCDS is the shortest (easiest?) route that I am currently aware of. There are several ongoing projects to bring a hibernate adapter to BlazeDS, and it will be interesting to follow what comes about from those efforts.

Cairngorm Enterprise 2.2.1

Cairngorm is a versatile, lightweight MVC framework updated and maintained by Adobe. It is an open-source project, and extremely useful in a wide variety of use cases. That said, it doesn't provide me with the full feature set I desire in my MVC solution. It does, however, provide a most excellent service locator pattern that works extremely well with LCDS. The service locator is the only aspect of Cairngorm I am going to use in this exercise.

PureMVC Multicore 1.0.5

PureMVC is a robust open source MVC solution. It is a weightier than cairngorm in its approach, but that extra weight provides useful patterns for approaching view-centric RIA development. Simply put, I can't live without the Mediator's control over my view components. The Proxy pattern provides a highly controlled method for accessing, sorting, and maintaining my data. The Command is essentially the same as the system found in Cairngorm, but for the purposes of this project I will be using mostly PureMVC with cairngorm's EnterpriseServiceLocator providing the connectivity with LCDS on the backend.

Hibernate

Hibernate is an Object/Relational Management framework for Java. It provides seemingly magical functionality in this capacity. It isn't really magic, as digging through its open source will demonstrate, but it is highly configurable and generally easy to use. It abstract painful CRUD operations and solves the majority of the problems you encounter when combining relational databases with object oriented software systems.

Hibernate Tools

Hibernate tools is an Eclipse (Flex Builder) plugin that will reverse engineer Java objects and Hibernate config files from your database.

MySQL

MySQL is the relational database of choice for this series. It should be relatively painless to utilize your preferred relational database, but for the sake of simplicity, and my lack of experience with other systems, that is what I will be utilizing here.

This is a lot of ground to cover. It is a daunting task, but the results are worth the effort. Once you have a level of comfort with the tools, the resulting systems provide a maintainable structure that provides your users with a rich visual experience. Data is maintained and updated across all clients accessing the system. LCDS allows for users to interact with one another, provides offline synchronization capabilities, as well as a robust persistence framework via the LCDS Hibernate adapter. Flex provides a consistent cross-platform user experience and applications are easily brought to the desktop with the AIR runtime.

The Project:

This project is a simple image gallery that stores images in albums.

The Setup:

It is assumed that you can download and install all of the above tools, so I am not going to detail that process. There are some details in the process that I will cover here. These are annomolies, or frustrating problems that took some measure of significant time searching down solutions for.

MySQL

DROP TABLE IF EXISTS `album`;
CREATE TABLE `album` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(256) default NULL,
`description` varchar(1024) default NULL,
`is_active` tinyint(1) default NULL,
`display_order` int(11) NOT NULL default '0',
`updated_on` datetime default NULL,
`created_on` datetime default NULL,
`mp3_url` varchar(256) default NULL,
PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Table structure for table `image`
--

DROP TABLE IF EXISTS `image`;
CREATE TABLE `image` (
`id` int(11) NOT NULL auto_increment,
`image` longblob NOT NULL,
`album_id` int(11) NOT NULL,
`sequence` int(11) NOT NULL,
PRIMARY KEY  (`id`),
KEY `album_id` (`album_id`),
CONSTRAINT `image_ibfk_1` FOREIGN KEY (`album_id`) REFERENCES `album` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

This is the database schema that will be used in the project. It is a simple two table structure. Note that we are using the INNOdb engine for the tables. I use XAMPP for my localhost configuration, and INNOdb is actually disabled by default. It is important to use INNOdb, as it supports foreign key constraints. These constraints will be used by the Hibernate Tools reverse engineering scripts to determine relationships across our object classes. While it isn't strictly necessary to use these tools, it saves a lot of initial typing.

This database structure is simple on purpose. You could add many other tables/fields to make this a more robust system. For the purposes of demonstration we will be applying KISS principles. Some ideas for expantion would be user accounts, thumbnails, slideshows, tagging, rating, or any other features that you might find in a photo album.

Create a database called 'slideshow' and apply the above SQL to create the two tables we will use for this demonstration.

LCDS

The default installation of LCDS contains some sample projects as well as a template project called LCDS. Because of limitations with the free single-cpu license, you are not able to run multiple applications. I move the default samples project out and rename the lcds project to slideshow (or better yet, move the lcds folder out and place a renamed copy of it back in the webapps folder.) You will also need to rename the context file that initializes the transaction manager utilized by Hibernate located in the {lcds-root}/tomcat/conf/Catalina/localhost folder. I removed the lcds-samples.xml, as well as the lcds.xml files after first making a copy of lcds.xml and renaming it slideshow.xml to match our application.

NOTE: If you are using Flex Builder, you will need to install the Java Development Tools. If you are using Eclipse, you will need to install the Flex Builder plugin. In this demonstration, I am using Flex Builder 3 with the Java Development Tools added on.

In Flex Builder create a new Java Project named Slideshow. Select the Create project from existing source option and navigate to {lcds-root}/tomcat/webapps/slideshow/WEB-INF. Choose next, and ensure that the Default output path is set to Slideshow/classes. By default, this might be set to bin-debug, and this is not appropriate for an LCDS application. The server will not know where to look for the compiled classes! Click Finish.


Initial Slideshow project structure

Open the Hibernate perspective in Flex Builder. In the Hibernate Configurations tab, right-click and select Add Configuration... to open the configuration dialog. Name the configuration slideshow-hibernate, select your project, hit Setup... for your property file and select Create new... and place it in the root of your project. Click Setup... for your configuration file and select Create new... to create an initial hibernate.cfg.xml that we will use to connect to our database for reverse engineering of the tables into Java POJOs and Hibernate mapping files. Select MySQL 5(InnoDB) as the database dialect, org.gjt.mm.mysql.Driver as the Driver class, jdbc:mysql://localhost/slideshow as your connection URL, and enter your database username/password before clicking finish. The configuration should now be available in the Hibernate Configurations tab. Open the Database, and you should see your database and the tables contained within:


Hibernate Configuration Wizard results


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">org.gjt.mm.mysql.Driver</property>
<property name="hibernate.connection.password">mypassword</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/slideshow</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
</session-factory>
</hibernate-configuration>

The resulting hibernate.cfg.xml should look like the above.

From the Run menu, select Hibernate Code Generation, and select Open Hibernate Code Generation Dialog... to create a reverse engineering configuration to generate our POJOs and Hibernate mapping files. Click the "New launch configuration" icon and rename the configuration "slideshow-configuration". In the Console configuration dialog select our slidwshow-hibernate configuration that we created in the previous step. Brows to the src directory of our project in the Output directory selection. Click the "Reverse engineer from JDBC Connection" checkbox. Enter "slideshow.data" in the package text input area. Click the setup button for reveng.xml and choose "Create new..." and place the file in the root of our project. Move to the "Exporters Tab" and choose Domain code (.java), Hibernate XML Mappings (.hbm.xml), and Hibernate XML Configuration (.cfg.xml). Didn't we already create a hibernate.cfg.xml? Yes, but this generated version will be placed in the src directory, and will contain the appropriate references to our mapping files. The first configuration is simply to tell the generator where to connect to our database. Switch to the Refresh tab and select "Refresh resources upon completion." so that our project structure will be refreshed after we click Run. Click Run.

From the Package Explorer tab, you will see that our .java classes have been generated, as well as their appropriate sidecar .hbm.xml files. The album class containes a Set called images, and the Image class contains an Album property. Take a look at the .hbm.xml files and take note of the defined relationships. The Hibernate reverse engineering has successfully traversed our database structure and created the appropriate relationsips!

It is worth noting that the Hibernate reverse engineering tools could concievably take care of the following steps. This is on my list of things to explore, but for now we will define our LCDS destinations and Actionscript value objects the old fashioned way. By hand. Yick.

As a final step we will add cascading and a retrieve all query to our album.hbm.xml like this:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Aug 17, 2008 1:02:52 PM by Hibernate Tools 3.2.2.GA -->
<hibernate-mapping>
<class name="slideshow.data.Album" table="album" catalog="slideshow">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="identity" />
</id>
<property name="name" type="string">
<column name="name" length="256" />
</property>
<property name="description" type="string">
<column name="description" length="1024" />
</property>
<property name="isActive" type="java.lang.Boolean">
<column name="is_active" />
</property>
<property name="displayOrder" type="int">
<column name="display_order" not-null="true" />
</property>
<property name="updatedOn" type="timestamp">
<column name="updated_on" length="19" />
</property>
<property name="createdOn" type="timestamp">
<column name="created_on" length="19" />
</property>
<property name="mp3Url" type="string">
<column name="mp3_url" length="256" />
</property>
<set name="images" inverse="true" cascade="all">
<key>
<column name="album_id" not-null="true" />
</key>
<one-to-many class="slideshow.data.Image" />
</set>
</class>
<query name="all.albums">from Album</query>
</hibernate-mapping>


These two changes are the only changes neccessary. The generator has taken care of everything else.

Defining the LCDS Destinations

Now that we have set up the server side Java application, it is neccessary to create destinations for our client to communicate with. In the flex directory of our project, there are a number of xml configuration files. The default configuration will be sufficient in all cases except for the data-management-config.xml. This is the file that will contain our destinations.

This is the basic configuration for our destinations defining the relationships between our objects:

<?xml version="1.0" encoding="UTF-8"?>
<service id="data-service"
class="flex.data.DataService">

<adapters>
<adapter-definition id="actionscript" class="flex.data.adapters.ASObjectAdapter" default="true"/>
<adapter-definition id="java-dao" class="flex.data.adapters.JavaAdapter"/>
</adapters>

<default-channels>
<channel ref="my-rtmp"/>
</default-channels>

<destination id="AlbumHibernate">
<adapter ref="java-dao" />
<properties>
<use-transactions>true</use-transactions>
<source>flex.data.assemblers.HibernateAssembler</source>
<scope>application</scope>
<metadata>
<!--This is the unique identifier from the hibernate-entity bean -->
<identity property="id"/>
<one-to-many property="images" destination="ImageHibernate" lazy="true" />   
</metadata>
<network>
<session-timeout>0</session-timeout>
</network>
<server>
<hibernate-entity>slideshow.data.Album</hibernate-entity>
<fill-method>
<name>fill</name>
<params>java.util.List</params>
</fill-method>
<fill-configuration>
<use-query-cache>false</use-query-cache>
<allow-hql-queries>true</allow-hql-queries>
</fill-configuration>
</server>
</properties>
</destination> 

<destination id="ImageHibernate">
<adapter ref="java-dao" />
<properties>
<use-transactions>true</use-transactions>
<source>flex.data.assemblers.HibernateAssembler</source>
<scope>application</scope>
<metadata>
<!--This is the unique identifier from the hibernate-entity bean -->
<identity property="id"/>
<many-to-one property="album"  destination="AlbumHibernate" lazy="true" />
</metadata>
<network>
<session-timeout>0</session-timeout>
</network>
<server>
<hibernate-entity>slideshow.data.Image</hibernate-entity>
<fill-method>
<name>fill</name>
<params>java.util.List</params>
</fill-method>
<fill-configuration>
<use-query-cache>false</use-query-cache>
<allow-hql-queries>true</allow-hql-queries>
</fill-configuration>
</server>
</properties>
</destination>             
</service>



The Client

The client will be an Adobe AIR client. Why AIR? Well, for this particular demonstration, an image gallery, it is neccesary to pull images from the users harddrive and upload them as bytearrays to the server for storage in the database. With the current Flash Player Runtime (9 at the time of this writing) you cannot do this due to security sandbox restrictions. The good news is that this particular restriction is being relaxed in Flash Player 10, but for now it is not possible outside of AIR, or with roundtrip trickery via a web service.

"But wait, I thought we were applying KISS principles? Cairngorm AND PureMVC? What's up with that?"

Cairngorm is a nice framework, but PureMVC provides robust control over your view layer that Cairngorm lacks. Cairngorm's ServiceLocator (or EnterpriseServiceLocator in this case) is extremely convenient and provides easy access to our data services. In the context of this application, the ServiceLocator is the only piece of Cairngorm we are going to use. When I originally built this application, it was actually as a means to learn about the Cairngorm framework, but I found myself missing PureMVC the entire time I was working with it. So I am taking this opportunity to demonstrate the combination of these two frameworks as a means to create a robust MVC driven LCDS connected application.


Flex Project Structure

The project structure is typical PureMVC, with model, view and controller folders added under the slideshow package. The focus of this tutorial is the server side configuration. I'm not going to go into detail about constructing the client application, but I am going to summarize what the classes do:

Slideshow.mxml
This is our main application window. It calls a reference to the SlideshowServiceLocator component to initialize the EnterpriseServiceLocator singleton class. It also contains references to our two Value Object classes. This is neccesary to send typed objects back and forth over the wire. Until the VOs are called, they will be transfered as generic objects. Slideshow.mxml also initializes the PureMVC ApplicationFacade.

SlideshowServiceLocator.mxml
This component is an extension of the Cairngorm Enterpriese EnterpriseServiceLocator class. This is the place where you define your channels and services to facilitate communication with the LCDS rtmp end-point. If you want to change the end-point from localhost to reflect your personal configuration or a remote server, this is the place to do it.

*Command.as
These classes initialize the PureMVC proxy and view components.

*Events.as
These are custom events. They are very simple extensions of the Event class, but they carry references to value objects to make life easier.

*VO.as
The value objects mirror the POJO objects on our server side. They are [Managed], meaning they are monitored for changes by LCDS. Managed classes are [Bindable] by default. There is also additional metadata referencing the remote class so that our objects will be stongly typed over the wire.

ApplicationMediator.as
This is the mediator for the main application window. It registers the mediators that monitor and control the sub-components of the application.

*Mediator.as
The mediators monitor for changes in our view components and respond to notifications from commands, proxies, and other mediators. In this application, there are not many notifications being sent around, but I left the methods in place for future expansion.

AlbumListProxy.as

This proxy is where we grab the list of albums from the server. This proxy grabs an instance of the EnterpriseServiceLocator and calls the "albums.all" query to fill the data property of the proxy. When the list of albums is retrieved, we assign each album an individual proxy to lord over it.

AlbumProxy.as
AlbumProxy contains methods to manipulate albums. Add images, delete images, delete the album, etc. While it is wholly possible to place these methods inside the actual view components (ServiceLocator makes it very easy), it is better form to seperate the manipulation of our data model from our view layer. View components should be stupid, ignorant of anything outside of displaying the data passed to them. With this application, I actually did data manipulation in the view components, but then refactored to follow the proxied data methodology. This is how I approach these things a lot of the times. Create a monolithic view component, break it into smaller components, mediate those deserving of such treatment, and finally proxy the data manipulation methods contained in the objects. I've found that as I gain experience, I am able to plan the broader structure without having to use this approach, but "make it work, then make it work better" is still a valid approach.

In Conclusion:

This application is possibly too simple for this elaborate setup. I chose this particular project because it is actually a piece of a larger image management system that I have planned. You might also notice, that while I call it 'Slideshow", we never actually run a slideshow! It could, with some simple additions, and lists of images are only interesting when they move and have music thumping behind them. This technology stack is very exciting, and presents many possibilities for a wide variety of application development. For small scale development, the single cpu LCDS license is probably sufficient. One thing I have not done is stress test LCDS to see exactly what it can handle on a single CPU.

Below is the exported web application archive. It can be extraced to your {lcds-root}/tomcat/webapps/ folder and contains all of the Java libraries, as well as the SWCs that I used on the Flex side. The Flex project, AIR rather, is contained in the flex-src folder of the java project. To get these projects into Flex Builder, follow these steps:

In the Flex Development perspective choose Import>Flex Project and select Project folder. Navigate to the {lcds-root}/tomcat/webapps/slideshow/WEB-INF/flex-src/slideshow-tutorial folder, select it, and then hit finish.

In the Java perspective, choose file>new>Java Project and choose "Create project from existing source." Navigate to {lcds-root}/tomcat/webapps/slideshow/WEB-INF and select this folder.

Both the Flex and Java projects should now be properly imported.

If you find glaring errors, omissions, or any other problems with this project, please let me know so that I can improve upon it for others.

slideshow.zip (22.7mb)

[The zip is large. This is because of the JAR libs in the java project. Specifically those related to Acrobat. Here (1.7mb) is a version with no JARs.]

4 Responses to “Integrating Adobe AIR, Cairngorm, PureMVC, LiveCycle Data Services (LCDS), MySQL and Hibernate”


  1. 1 Simon

    Hey Joel.
    Great article. I’m an experienced webdeveloper but i’m totally new to java. I really want to dig into it - so your tutorial was very suitable.
    But I always get several Errors during the startup of LCDS-Tomcat:

    [1]
    log4j:ERROR setFile(null,true) call failed.
    java.io.FileNotFoundException: target\activemq-test.log (Das System kann den angegebenen Pfad nicht finden)

    [2]
    [LCDS] Error instantiating application scoped instance of type ‘flex.data.
    java.lang.UnsupportedClassVersionError: Bad version number in .class file

    Can you help me with that?

    Thanx!
    Simon

  2. 2 Joel

    The first error is a logging error that I have yet to figure out (and does not affect the application), the second is regarding the JRE version running under Tomcat.

    For the second, make sure JAVA_HOME and the PATH var to your javac.exe is pointing to JRE 1.6. I also point Flex Builder to JRE 1.6 as opposed to the JRE that ships with Flex (and perhaps this is the problem). It is definitely a Java version issue though, and it is hard for me to give you a specific answer because I am fairly new to Java myself.

    http://donneit.blogspot.com/2008/08/faultstringno-destination-with-id-is.html

    It is one of the errors he had in another application that I am writing using this same process. You can see his solution part of the way down, which was to compile the Java source files as JRE 1.4, but this is a hack and I don’t think he was using the compiler in Flex Builder.

  3. 3 Aaron

    Hi Jeol,

    Many thanks for the article. Your old django/pyamf articles also proved useful. I am currently deciding whether to use django/pyamf or go with lcds for flex remoting.

    What are your thoughts on the two? Do you find Java on lcds any productivity any slower than the python counterpart?

    Looking forward to hearing your thoughts.

    Aaron

  4. 4 Joel

    Hi Aaron,

    For my purposes Django and LCDS are filling the same roll. I’ve found that as my applications grew, the send/receive model of Django with PyAMF/DjangoAMF was inhibiting my users work flow and over complicating my development on the client side. Utilizing the [Managed] value objects with LCDS is really awesome, and has removed a vast amount of boiler plate calls to the service that I was having to write for my models.

    This is, of course, primarily because of Hibernate. Django in itself provides nice ORM, but you can’t fully utilize it over the AMF wire, and certainly not the same way you can with the LCDS Hibernate Adapter. The work Adobe has done in this regard is truly fantastic.

    I love Python, and the Django project is very nice. LCDS/Hibernate builds a cleaner stack, saves me time, and provides my users with a more responsive service. This is where my efforts are going to be focused for the foreseeable future.

  1. 1 Recent URLs tagged Rtmp - Urlrecorder

Leave a Reply