Tuesday, December 18, 2007

Flex, BlazeDS and Seam

Intro

I have seen this question asked few times but so far there hasn't been any real solution for this simple problem; is it possible to integrate Flex with Seam. And I'm not talking about some ajax-bridge-html-js-hack, or using seam to create webservices and consuming them on the flex , even thought this is possible as well and for some situations good option too, but a direct access to java methods without re-writing (at least that much) of the java code. So basically using remoting.

I needed to provide a bit more "nicer" view for few pages on a seam app we have already built, nothing fancy or complicated- just a page to display the data but without any extras the site normally has (like navigation...) and with a bit of extra functionality on the interface (plus maybe in the future even create AIR app for this data) .. The release of Adobes BlazeDS got me thinking this a bit more (eventhough this feature won't be needed anytime soon..), and finally got me working to find the solution..

Well, after learning about and fighting with EJB's, sessions, seam transactions, entitymanagers, Jndi and hibernate co-operation and factories and lazy-loading problems for few days (and finally bothering to actually RTFM:) I'm finally happy to tell you it is possible, and also the integration works perfectly using the new BlazeDS remoting components. Since BlazeDS is based on LiveCycle ES, this should work exactly the same way with LiveCycle ES as well, just the jars are different (however, this you have to find out yourself, since I haven't tried LiveCycle ES).

This article assumes you have Seam & Jboss already installed and running and you have some kind of understanding how Seam, EJB3 and Hibernate works if you happen to run into problems. And since the frontend is Flex, a knowledge of flex & AS3.0 of course helps.. Note also that I'm not using any authentications/ restricted access to any beans- there is a way to manage this as well (I haven't tested though) but this is out of the scope of this article.

This article isn't about creating apps with seam or installing jboss either, there are plenty of tutorials for doing those elsewhere on the net..

The setup I'm using is Seam 2.0 running on Jboss 4.2.1.GA and the latest BlazeDS (blazeds_b1_121307) all running on localhost (on OS X with jvm1.5 [I would like java6 on leopard as well btw...]). I'm using the standalone Flex builder 3 beta 2 for flex and eclipse for java. I had the java project already on eclipse workspace - so I'm assuming you have already somekind of project to test this on (if not, this would be the perfect time to create one..). For flex there is a good example app to test on in the samples that comes with the BlazeDS- more about this later.

The files, besides jboss and seam, you need are:

BlazeDS and Flex from labs.adobe.com

EJBFactory
by Ryan J. Norris

NOTE! this version has the EJB3-support, while the other one in adobe exchange hasn't (meaning for EJB3 it doesn't use the create() method for ejbhome and some other stuff- see the src/)

After dowloading & unzipping all of the above, we're ready to start.

Seam + BlazeDS

1. First follow the instructions how to install BlazeDS on Jboss at adobe labs, http://labs.adobe.com/wiki/index.php/BlazeDS:Release_Notes . For now the tag is not needed, so just ignore that and the part about context.xml (at least if youre running Jboss alone, I don't know about the tomcat embed-thing, maybe you need this then).

2. After installing the sample apps (ie. deploying the .wars on Jboss and 2 installing the jars) and making sure they work, continue to the next step... For BlazeDS to work, you don't need the .wars on the server, the jars are all that's needed, so you can safely remove them after this if you like.

3. Copy the jars for Flex FROM blazeds_b1_121307/blazeds-samples/WEB-INF/lib TO [path_to_your_seam_app]/lib, ie. include them on your project. The jars to copy:

backport-util-concurrent.jar
concurrent.jar
flex-messaging-common.jar
flex-messaging-opt.jar
flex-messaging-remoting.jar
flex-messaging-core.jar
flex-messaging-proxy.jar

the commons* jars you should already have in your projects lib, if not, copy them too.

4. Copy the FlexEJBFactory/flex-ejb-factory.jar TO [path_to_your_seam_app]/lib

5. Make sure the new jars are included on your project when deploying- check build.xml and add them there (around line 133 ${war.dir}/WEB-INF/lib).

NOTE! Depending how you have setup your project and jboss, you might have different locations for the main lib and jars- on our project we try to keep all the jars on the projects own /lib/ and nothing on jboss, but if your projects are setup differently, use your paths- the main thing is to get all the necessary libs included.. (Jboss will give the classnotfound-errors on start if something is missing so don't worry)

6. Create a new dir: [path_to_your_seam_app]/resources/WEB-INF/flex

7. copy the *-config.xml files for BlazeDS FROM blazeds_b1_121307/resources/config/ TO [path_to_your_seam_app]/resources/WEB-INF/flex . I recommend these since they contain some useful instructions and are just the basic templates. You can check out more examples about the config-files in blazeds-sample and blazeds -dirs (under /WEB-INF/flex).

8. copying of files done! moving on...

Configuring the xml-files

1. in resources/WEB-INF/web.xml add the flex-listener and messagebroker servlet:

<!-- FLEX -->

<listener>
<listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>

<!-- context-param>
<param-name>flex.class.path</param-name>
<param-value>/WEB-INF/flex</param-value>
</context-param-->

<!-- MessageBroker Servlet -->
<servlet>
<servlet-name>MessageBrokerServlet</servlet-name>
<display-name>MessageBrokerServlet</display-name>
<servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
<init-param>
<param-name>services.configuration.file</param-name>
<param-value>/WEB-INF/flex/services-config.xml</param-value>
</init-param>
<init-param>
<param-name>flex.write.path</param-name>
<param-value>/WEB-INF/flex</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MessageBrokerServlet</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>



I added this last on my config-file. Note the commented-out flex.class.path context-param; everything seems to work fine without that but I kept it there just in case.

NOTE! Read the docs about configuring the BlazeDS-config files on adobes site, what follows is just the needed changes..

2. WEB-INF/flex/services-config.xml add line:

<factories>
<factory id="ejb3" class="com.adobe.ac.ejb.EJB3Factory">
</factories>


This refers to the EJB3Factory that is needed for flex to get own instance of the EJB3. Without the factory you can access just the methods in java classes without any kind of connection to entitymanager (and using dot syntax - see the next step), so basically nothing..

on the same file change the <security>login-command to
<login-command class="flex.messaging.security.TomcatLoginCommand" server="JBoss">


3. WEB-INF/flex/remoting-config.xml

Here is a bit I spent quite long figuring out. The format of the channels source (the java object we're calling) depends on the jndi pattern cofigured on the server. For many it seems to be with dots, like com.whatever.ClassName. However, for Seam the default jndi-pattern, as defined in component.properties.xml is in the form of "jndiPattern \#{ejbName}/local", so instead of dots, seam uses the form ejbName/local. Also notice the /local - this can be set either to remote or local depending of the java object (if the interface isn't annotated as @Remote, it's local) . And If you don't include this seemingly trivial /local on the path, nothing works (trust me, I got stuck with this for a while..).

Another important thing - for the destination ids to work correctly, Seam needs them to be real class/ejb names- otherwise it gives "value of context variable is not an instance of the component bound to the context variable"-errors when accessing the pages. But then on the other hand, why would you use something else than the class name you're accessing? (Yeah ok, I'll admit- Of course I was stuck with this for some time as well when using just short names like "user" instead of UserEditorAction which was the actual name of the bean...).

So an example destination looks like this (the scope is optional) - also notice the factory-tag which tells flex to use the EJB3Factory defined earlier. The "appname" is the name of your application/project.

<destination id="UserEditorAction">
<adapter ref="java-object">
<channels>
<channel ref="my-amf">
</channels>
<properties>
<factory>ejb3</factory>
<source>appname/UserEditorAction/local</source>
<scope>session</scope>
</properties>
</destination>


For this first test I recommend using a bean which has some kind of list/collection to return for flex app- this way it's easiest to see if everything works ok..

4. All done for the configuration!

Get hibernate, EJB's and Seam finally talking to flex

So now you should have the configuration done and files on the right places. The final step is to get the data to flex app. These are more like general instructions, since there is (at least for now) no example application to use..

First let's look at the java:

You should have one entity and a bean using it- for example entity User with some properties with stateful/statelss bean for accessing User, for example to list all the users (if you have collections on the entity, even better- since then you'll see lazy loading working as well!). I had a simple method like this:

public List<user> getUserList() {
userList = entityManager.createQuery("select U from User u").getResultList();
return userList;
}


Speaking about lazy loading- if you're using EJB3 entitymanager (@Persistencecontext annotation), you will run into problems with collections using lazy loading- the entitymanager gets just the data needed for the main entity but closes the connection before fetching of any data from the lazy loaded collections is completed. See more info about this on the resources-list on the bottom of the article.

The solution for this problem is simple- use the Seam's own entitymanager for handling the connection, and all your lazy loading problems are solved! So make sure you have in your bean the lines

@In
private EntityManager entityManager;


and that's all. This one took me quite a while to realize as well since in our project both entitymanagers were used depending on the bean, and I haven't really never before thought why...

If you need to use EJB3 entitymanager, I read something about using Jboss interceptors to strip the proxy objects from the sent object so they don't get triggered by flex but unfortunately lost the link...

On the flex side:

Open the blazeds-samples/testdrive-remoteobject project in flex builder (or eclipse, whichever you prefer) from the blazeds source files. Feel free to use any other project as well, or create your own, but this is the fastest way to get testing.

The project includes a datagrid and a button that loads the data from the remote object. Replace the "destination" in mx:remoteObject with your own destination from the remoting-config.xml. Then on the button and datagrid code change the methods to your own:

<mx:button label="Get Data" click="srv.getMyListOfStuff()">

<mx:datagrid dataprovider="{srv.getMyListOfStuff.lastResult}" width="100%" height="100%">


Now comes the important part: for remoting, flex needs to get access to the services-config.xml, since it will compile the info inside of the .swf. To set this, open the project properties and select compiler. Type the path to your services-config.xm file to the additional arguments and set the context-root as well. For services-ath you can use the path to your project in your workplace, this is only for the compiling of .swf. The whole arguments string looks something like this on OS X, assuming you have your workspace under Documents:

-locale en_US --services=/Users/[user]/Documents/workspace/[myProject]/resources/WEB-INF/flex/services-config.xml --context-root=/myProject

You're done!

Run the app and click the "get data" button (or however you're accessing the data on your own project). If everything went ok, the datagrid should be populated with the data from Seam/Hibernate!

Some useful links:

BlazeDS developers guide
http://livedocs.adobe.com/labs/blazeds/html/index.html

Adobe livedocs:
Using the factory mechanism
http://livedocs.adobe.com/flex/201/html/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Book_Parts&file=ent_services_config_097_26.html
Accessing Java objects in the source path
http://livedocs.adobe.com/flex/201/html/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Book_Parts&file=dataservices_099_25.html

Seam:
http://docs.jboss.com/seam/latest/reference/en/html/configuration.html
http://docs.jboss.com/seam/latest/reference/en/html/persistence.html

Great posts about flex and java integration:
http://weblogs.macromedia.com/pmartin/archives/2006/08/ejb_and_flex_in.cfm

Lazy loading
http://www.hibernate.org/162.html
http://forum.hibernate.org/viewtopic.php?t=947299&postdays=0&postorder=asc&start=0&sid=5b259f71c96ed0925c407ab926de3850

19 comments:

Kornél said...

Great tutorial.
I create an almost working flex+ejb3 application :)

Anonymous said...

Nice tutorial.

How did you solve the lazy loading problems?

Kristian said...

lazy loading got solved by using the Seam entitymanager- so use

@In
private EntityManager entityManager;

instead of

@PersistenceContext
private EntityManager em; (or similar)

this way seam handles the loading and it works. Of course you could also change the collections fetchtype to eager ie. @ManyToMany(fetch=FetchType.EAGER) if you can, but this of course is then no more lazy loading anyway..:) . Here is some useful info from Seams docs: Seam persistence

Anonymous said...

An excellent tutorial!! I am going to share this post with my friends who all going to start working on Flex/Seam projects.

Unknown said...

This looks like a great tutorial but I can't get Blaze to build :( the latest version (3.0) locks up on my osx, does someone have a link to the older builds?

Unknown said...

forget that.. downloaded a pre-built one.. this is awesome, I've been working on seam for a year now and pushing the limits the ajax libraries... can't wait to get my teeth into this!

Anonymous said...

Thanks for your post !
I can't get lazy loading.
Can you post your project ?

Anonymous said...

Have you tried Flamingo - http://code.google.com/p/exadel-flamingo ? Really easy integration and it was developed specially for Seam.

Anonymous said...

Hi,

I have the lazy loading problem with SEAM 2.0.1GA, MySQL 5, JBOSS 4.2.2GA, Adobe LiveCycle Data Services ES 2.5.1, Flex 3 App.


"org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.unikity.portal.entities.Member.godChilds, no session or session was closed"


Do you have a fix ?

I already use the seam managed context persistence.


components.xml:
persistence:managed-persistence-context name="entityManager" auto-create="true" persistence-unit-jndi-name="java:/UnikityPortalEntityManagerFactory"


and my EntityManager in injected with @In seam annotation.

Thanks for your help,

Jose Ferreiro said...

Thank you for sharing this post.
I am planning to play with a RIA application soon based on Flex 3 (client side). The server side will be using JBoss (no EJB is required, I will use the tomcat within JBoss, I do also plan to use Hibernate). At this stage I believe that installing BlazeDS will be enough to be make the web app working. In your post you are using Seam. I would like to ask you what why did you used Seam.
In your opinion do I need to use Seam in the context of my web app?
Thank you for your answer.

Anonymous said...

Hi,
I found that using Seam EM can only solve simple cases which has only one layer of collection relationship. For cases we have a multiple layer of collection relationships. We still got lazy-loading initialization exceptions. For example, if Entity A has a lazy loading collection of entity B, and entity B has a collection of lazy-loading entity C. It will throw exception with entity C.

BTW, by using Seam entityManager, I feel it's actually using eager-loading strategies because from jboss server log, I can see the values of collections.

I wonder if anyone has the same observation as me? Or It's just that I have missed something.

Craig said...

Would this work if i was using seam pojos rather than ejbs?

Thanks

Anonymous said...

I have created a tutorial for integrating Adobe Flex with JBOSS using BlazeDS with source include.
Check it out at:
http://simplyolaf.blogspot.com/2009/07/integrate-adobe-flex-and-jboss-using.html

javierpezmar said...

Great Tutorial, thanks a lot

About de lazy initialization problem, (asuming you got a valid hibernate session), did you try to fetch the HQL query?
For example, if the POJO User has a one-to-many lazy defined relation with Phone POJO, you could try this HQL:

Select u From User u JOIN FETCH u.phones

Martin Zoldano said...

here's an approach with a proxy bit.ly/4Z3AXL

cheers

Dan Osterrath said...

Unfortunately this only works for stateless session beans. If you need support for stateful session beans have a look at http://dev.3m5-extra.net/blog/2010/06/04/ejb3factory-for-blazeds/

Unknown said...

If you have to attract visitors to your website the first thing they attract is that blog or website look.
online sweepstakes
church software
blackjack software

link said...

Very nice post. i found it very useful.
thanks for spending time on it. it has better understanding.
i appreciate for you and very useful this your information


Web Development Dubai

Unknown said...

This is a very useful information. A lot more than thanks for spending time on it. Web Design Manchester