5. Hello World Revisited (EJB 3 with Externalization)

Overview

This tutorial shows an evolution of the basic "Hello, world" sample application with some Hibernate persistence operations (EJB 3). When finished and deployed it should look like this picture:

In this picture, you see a small data grid that displays the history of all previous say hello operations. This history is persisted in a database by means of an EJB 3 entity bean and those objects are serialized back to the Flex client each time you enter a new name.

This sample also shows basic usage of the Granite Eclipse Builder and externalization configuration.

Requirements

In order to create, build, and deploy this sample application you need all these free tools:

  • Java 5+ (6+ working): Download Sun JDK and install it.
  • Eclipse 3.3+: Download Eclipse and unzip it somewhere.
  • Flex 3 SDK: Download Flex 3 SDK and unzip it somewhere. For example, /flex_3_sdk (for Windows users: C:\flex_3_sdk).
  • JBoss 4.2.3+: Download JBoss and unzip it somewhere. For example, /jboss-4.2.3.GA (for Windows users: C:\jboss-4.2.3.GA).
  • granite.jar, granite.swc, granite-hibernate.jar and granite-hibernate.swc: You may take them from GraniteDS source distribution in the build folder: download it here.
  • Granite Eclipse Builder: You may download it on SourceForge here.
     

Eclipse Project Creation

Java Entity Bean & Service:

Start Eclipse, and create a new Java project named helloworld2. You may just type in helloworld2 for Project name and accept all other default settings. Because we are going to have two types of sources, Java and Flex MXML/AS3, it is better to rename the default Java source folder src to java by right-clicking on the src source folder and selecting Refactor / Rename.

Create a new directory named lib at the root of this project and put granite.jar, granite.swc, granite-hibernate.jar and granite-hibernate.swc into it. Also add these two JBoss jars: ejb3-persistence.jar and jboss-ejb3x.jar, which you will find in the /jboss-4.2.3.GA/server/default/lib directory. Right-click on those JBoss jars and select Build Path / Add to Build Path.

You should now see something like that in your Eclipse Package Explorer:

Right-click on the java source folder, select New / Class and fill the New Java Class dialog as follows:

Copy and paste the following code in the Java editor:

Welcome.java
package org.test;

import java.io.Serializable;

import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Welcome implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id @GeneratedValue
    private Integer id;

    @Basic
    private String name;

    public Welcome() {
    }

    public Welcome(String name) {
        this.name = name;
    }
	
    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

This basic EJB 3 entity bean declares a read-only id field, auto incremented primary key in the database, and a name field, the name of the person that was entered in the Flex application when saying hello.

We are now about to create an EJB 3 session bean that will handle the say hello and persistence operations.

Create a new Java interface named HelloWorldService by right-clicking on the org.test package and choosing New / Interface. Then copy and paste the following code:

HelloWorldService.java
package org.test;

import java.util.List;

public interface HelloWorldService {

    public String sayHello(String name);
    public List<Welcome> findWelcomeHistory();
}

Create a new Java class named HelloWorldServiceBean that implements the HelloWorldService interface by right-clicking on the org.test package and choosing New / Class. Then copy and paste the following code:

HelloWorldServiceBean.java
package org.test;

import java.util.List;

import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

@Stateless
@Local(HelloWorldService.class)
public class HelloWorldServiceBean implements HelloWorldService {

    @PersistenceContext
    protected EntityManager manager;

    @Override
    public String sayHello(String name) {
        manager.persist(new Welcome(name));
        return "Hello " + name + "!";
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Welcome> findWelcomeHistory() {
        Query query = manager.createQuery("from " + Welcome.class.getName());
        return query.getResultList();
    }
}

We now have a complete EJB 3 stateless session bean that will persist each name passed to the sayHello() method and return the list of all previous welcome operations; the findWelcomeHistory() method.

Your Java project should look like that after this step:

Flex Client Code:

Create a new folder named flex by right-clicking on the helloworld project and selecting New / Folder. Create a new File directly in this new folder and name it HelloWorld.mxml by right-clicking on the flex folder and select New / File. In the file editor, it may be FlexBuilder or a simple text editor depending on your Eclipse installation, copy and paste the following code:

HelloWorld.mxml
<?xml version="1.0" encoding="utf-8"?>

<mx:Application
    xmlns:mx="http://www.adobe.com/2006/mxml"
    backgroundGradientColors="[#0e2e7d, #6479ab]"
    layout="vertical"
    verticalAlign="middle"
    creationComplete="srv.findWelcomeHistory()">

    <mx:Style>
        .Panel {
            padding-left: 8px; padding-top: 8px;
            padding-right: 8px; padding-bottom: 8px;
        }
        .Result { font-size: 26px; color: white; }
    </mx:Style>

    <mx:RemoteObject id="srv" destination="helloWorldService" />

    <mx:Panel styleName="Panel" title="Hello World Sample">
        <mx:Label text="Enter your name:"/>
        <mx:TextInput id="nameInput" width="200" />
        <mx:Button label="Say Hello"
           click="srv.sayHello(nameInput.text);srv.findWelcomeHistory()"/>
        
        <mx:Label text="History:"/>
        <mx:DataGrid dataProvider="{srv.findWelcomeHistory.lastResult}"
          width="200" height="200"/>
    </mx:Panel>
        
    <mx:Label styleName="Result" text="{srv.sayHello.lastResult}"/>
    
</mx:Application>

Some explanations:

  • This Flex application uses a RemoteObject named srv that will be bound to the stateless session bean we have created earlier. The actual binding between the destination named helloWorldService and the Java service is specified in a services-config.xml we will create later.
  • When the Flex application is completely created, see the creationComplete attribute, it first calls the findWelcomeHistory() method of the Java service. The result of this call is displayed in the DataGrid, see the dataProvider="{srv.findWelcomeHistory.lastResult}" attribute.
  • When you click on the Say Hello button after entering a name in the TextInput field, it calls the sayHello() method on the server with the supplied name and then the findWelcomeHistory() method whose result is used to update the DataGrid content. See the click="srv.sayHello(nameInput.text);srv.findWelcomeHistory()" attribute.
  • The returned String of the sayHello() method, "Hello " + name + "!" in the Java service, is displayed in a Label just below the Panel. See the text="{srv.sayHello.lastResult}" attribute.

We are now going to configure the Granite Eclipse Builder that will generate the Welcome entity bean ActionScript3 equivalent. First download the builder and install it; just drop the jar in your Eclipse plugin directory and restart. In your package explorer, right-click on the helloworld2 project and select Add GraniteDS Nature:

In the configuration wizard, click on the Add Folder button and select the java source folder:

Select the Excluded subnode and click on the Edit button. In the following dialog, change the Output Directory to flex, instead of the default as3, and add the **/*Service*.java exclusion pattern, as we don't want code generation for services, just for entities:

After those short configuration steps, you may accept all other default options and click directly on the Finish button in the wizard. The generation process starts and produces two files as shown in the following picture:

There is no need to modify those files for our short example but, if you want to add specific methods to your ActionScript3 bean, you must put it in the Welcome.as class and not in the WelcomeBase.as class that may be overidden by subsequent generation processes. If you look at the WelcomeBase.as class, you will see that the generated code reproduces the Welcome.java fields and accesses (read-only id and read-write name). It also implements code required for externalization mechanisms with lazy loading support. See Externalizers documentation for details.

Configuration Files:

The rest is only a matter of configuration files. First, create a new file named granite-config.xml at the root of the helloworld2 project directory with this content:

granite-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE granite-config PUBLIC "-//Granite Data Services//DTD granite-config internal//EN"
    "http://www.graniteds.org/public/dtd/1.2.0/granite-config.dtd">

<granite-config>

    <classgetter type="org.granite.hibernate.HibernateClassGetter"/>

    <externalizers>
        <externalizer type="org.granite.hibernate.HibernateExternalizer">
            <include annotatedwith="javax.persistence.Entity"/>
        </externalizer>
    </externalizers>

</granite-config>

This configuration instructs GDS to externalize all Java classes annotated with the @Entity annotation (such as our Welcome.java entity bean).

Next we need a Flex services-config.xml as follows. Create it in at root of the project, as for granite-config.xml:

services-config.xml
<?xml version="1.0" encoding="UTF-8"?>

<services-config>

    <services>
        <service
            id="granite-service"
            class="flex.messaging.services.RemotingService"
            messageTypes="flex.messaging.messages.RemotingMessage">
            <destination id="helloWorldService">
                <channels>
                    <channel ref="my-graniteamf"/>
                </channels>
                <properties>
                    <factory>ejbFactory</factory>
                </properties>
            </destination>
        </service>
    </services>
    
    <factories>
        <factory id="ejbFactory" class="org.granite.messaging.service.EjbServiceFactory">
            <properties>
                <lookup>helloworld/{capitalized.destination.id}Bean/local</lookup>
            </properties>
        </factory>
    </factories>

    <channels>
        <channel-definition id="my-graniteamf" class="mx.messaging.channels.AMFChannel">
            <endpoint
                uri="http://{server.name}:{server.port}/{context.root}/graniteamf/amf"
                class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>

</services-config>

This is where the destination id HelloWorldService is bound to our stateless EJB 3 session bean service. When the RemoteObject in HelloWorld.mxml is used, the destination id is used in the EjbServiceFactory to lookup the EJB; helloworld/{capitalized.destination.id}Bean/local is resolved to helloworld/HelloWorldServiceBean/local, that is precisely the JNDI name used in JBoss to access the EJB.

We then need three additional configuration files: application.xml, persistence.xml and web.xml. All are standard J2EE configuration files and you will find detailed documentation on them on the Internet. Here is their contents. Again, create them at the root of the project:

application.xml
<?xml version="1.0" encoding="UTF-8"?>

<application>
    <display-name>GraniteDS HelloWorld</display-name>
    <module>
        <web>
            <web-uri>helloworld.war</web-uri>
            <context-root>/helloworld</context-root>
        </web>
    </module>
    <module>
        <ejb>helloworld.jar</ejb>
    </module>
</application>

 

persistence.xml
<?xml version="1.0" encoding="UTF-8"?>

<persistence>
    <persistence-unit name="ejb3">
        <jta-data-source>java:/DefaultDS</jta-data-source>
        <properties>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
        </properties>
    </persistence-unit>
</persistence>

 

web.xml
<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <!-- general information about this web application -->
    <display-name>Hello World</display-name>
    <description>Hello World Sample Application</description>

    <!-- read services-config.xml file at web application startup -->
    <listener>
        <listener-class>org.granite.config.GraniteConfigListener</listener-class>
    </listener>

    <!-- handle AMF requests ([de]serialization) -->
    <filter>
        <filter-name>AMFMessageFilter</filter-name>
        <filter-class>org.granite.messaging.webapp.AMFMessageFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AMFMessageFilter</filter-name>
        <url-pattern>/graniteamf/*</url-pattern>
    </filter-mapping>

    <!-- handle AMF requests (execution) -->
    <servlet>
        <servlet-name>AMFMessageServlet</servlet-name>
        <servlet-class>org.granite.messaging.webapp.AMFMessageServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>AMFMessageServlet</servlet-name>
        <url-pattern>/graniteamf/*</url-pattern>
    </servlet-mapping>

    <!-- default content for helloworld application -->
    <welcome-file-list>
        <welcome-file>HelloWorld.swf</welcome-file>
    </welcome-file-list>

</web-app>

You should now see something like this in your package explorer:

Building & Deploying the Sample Application

Create a new file named build.xml at the root of the project. Copy and paste the following content into it. You may have to modify FLEX_HOME/JBOSS_HOME properties to reflect your environment:

build.xml
<?xml version="1.0" encoding="UTF-8"?>

<project name="hello-world" default="deploy">

    <!-- Modify FLEX_HOME/JBOSS_HOME properties to reflect your environment -->
    <property name="FLEX_HOME" value="/flex_sdk_3"/>
    <property name="JBOSS_HOME" value="/jboss-4.2.3.GA"/>
    
    <!-- Declare Flex Ant tasks (such as mxmlc used below) -->
    <taskdef resource="flexTasks.tasks" classpath="${FLEX_HOME}/ant/lib/flexTasks.jar" />

    <!-- Compile MXML source code to SWF -->
    <target name="mxmlc">
        <mxmlc
            file="flex/HelloWorld.mxml"
            output="build/HelloWorld.swf"
            services="services-config.xml"
            context-root="helloworld">
            
            <source-path path-element="flex" />

            <!-- Make sure that the Welcome.as class is compiled into
                 our HelloWorld.swf (otherwise mxmlc doesn't include it
                 because there are no explicit reference to this class
                 in HelloWorld.mxml -->
            <includes symbol="org.test.Welcome" />
            
            <!-- Make sure that all GDS classes are included into
                 our HelloWorld.swf (otherwise mxmlc doesn't include them
                 because there are no explicit reference to all those classes
                 in HelloWorld.mxml and Welcome.as -->
            <compiler.include-libraries dir="lib" append="true">
                <include name="granite.swc" />
                <include name="granite-hibernate.swc" />
            </compiler.include-libraries>
        </mxmlc>
    </target>

    <!-- Build an ear suitable for JBoss -->
    <target name="ear" depends="mxmlc">
        <mkdir dir="build"/>
        <jar destfile="build/helloworld.jar">
            <fileset dir="bin" includes="**/*.class"/>
            <zipfileset file="persistence.xml" prefix="META-INF" />
        </jar>
        <war destfile="build/helloworld.war" webxml="web.xml">
            <zipfileset file="services-config.xml" prefix="WEB-INF/flex" />
            <zipfileset file="granite-config.xml" prefix="WEB-INF/granite" />
            <fileset dir="build" includes="*.swf"/>
        </war>
        <ear destfile="build/helloworld.ear" appxml="application.xml">
            <fileset dir="build" includes="*.jar,*.war"/>
            <zipfileset dir="lib" includes="granite*.jar" prefix="lib" />
        </ear>
    </target>

    <!-- Deploy the ear in JBoss -->
    <target name="deploy" depends="ear">
        <copy todir="${JBOSS_HOME}/server/default/deploy" file="build/helloworld.ear"/>
    </target>

</project>

Basically, this Ant build compiles our Flex code in a swf and package everything in an ear suitable for JBoss deployment.

Read the comments in the above build.xml about including AS3 classes/SWC libraries into the compiled SWF! Missing this point leads to a very common Flex runtime error because of unexpected mxmlc compiler optimizations!

You may now right-click on the build.xml file and select "Run As / Ant Build. This will launch the build process, compile the MXML code to an SWF, create an ear (Enterprise ARchive) and copy it into your JBoss deploy directory.

Start JBoss & Play with the Application

To start JBoss, go to the directory bin just under your JBoss installation directory (/jboss-4.2.3.GA/bin for example) and double-click on run.bat, or run.sh for Unix/Mac users. After a short while, you should see in the console that JBoss has started. You may now point your Internet browser to http://localhost:8080/helloworld/.


Browse Space

- Pages
- Blog
- Labels
- Attachments
- Bookmarks
- Mail
- Advanced

Explore Confluence

- Popular Labels
- Notation Guide

Your Account

Log In

Other Features

Add Content

- Add Comment


SourceForge.net Logo
Copyright © 2007-2008 Adequate Systems. All Rights Reserved.