Monday, June 22, 2009

Flex + BlazeDS + Google App Engine + Java + Eclipse

Why?

Because you want to write a Flex application that requires that the server be able to push data to the client, you want a highly scalable server so you decided on the Google App Engine, when provided with the option of Python versus Java you found Java more accessible, and Eclipse is an easy way to link it all together.

  • Flex – used for the graphical user interface
  • BlazeDS – used for Flex to Server remote objects and provides the ability for the server to push data to the Flex client
  • Google App Engine – used for hosting the server portion of the application
  • Java – the language used for constructing the server potion of the application
  • Eclipse – an easy way to build, manage, write code, and deploy

There are lots of other options though, in general with Flex you are aiming for a way to deal with remote objects and/or server data push + some way to persist data on a server that ties to some language + some IDE. This is just one of many possible configurations with an emphasis on relatively free parts.

It is also apparently difficult to get all of things working together, so I thought it would help for others if I shared the steps.

In this example I basically combined the two following projects into one to use the Google App Engine to show basic remote objects and messaging:

If you want more details into the how remote objects and messaging works see those examples.

Requirements

1. Creating the Server

1.1 Press the “New Web Application” button in the Eclipse toolbar

image

1.2 In the “New Web Application Project” window specify a project name, the package, and for the rest use the default settings then press the “Finish” button.

image

1.3 If you are like me and already run another server on port 8080, then you need to change the project settings to use a different port. You can do this by opening the debug menu and selecting “Open Debug Dialog…”

image

1.4 Specify a new port to be used and save these settings.

image

2. Adding BlazeDS to the Server

2.1 Assuming you have obtained the BlazeDS WAR file, unzip it using some ZIP program

image

2.2 Open the BlazeDS web.xml file and copy the mappings into the web.xml in your Google test project.

The web.xml file is located in MyGoogleServer/war/WEB-INF

image

The resulting web.xml file should look like the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "
http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>MyGoogleServer.html</welcome-file>
  </welcome-file-list>
  <!-- Servlets -->
  <servlet>
    <servlet-name>greetServlet</servlet-name>
    <servlet-class>com.example.myproject.server.GreetingServiceImpl</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>greetServlet</servlet-name>
    <url-pattern>/mygoogleserver/greet</url-pattern>
  </servlet-mapping>
  <!-- Begin BlazeDS Stuff -->
  <!-- Http Flex Session attribute and binding listener support -->
    <listener>
        <listener-class>flex.messaging.HttpFlexSession</listener-class>
    </listener>

    <!-- 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>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>MessageBrokerServlet</servlet-name>
        <url-pattern>/messagebroker/*</url-pattern>
    </servlet-mapping>

</web-app>

2.3 Copy the BlazeDS flex directory into MyGoogleServer/war/WEB-INF

image

2.4 Open the services-config.xml file and change the three instances of {server.name}:{server.port}/{context.root} to your server name, which in the case of this example is localhost:9000. When you deploy this thing to Google you will of course need to change it.

image 

2.5 Open the appengine-web.xml file and add the node <sessions-enabled>true</sessions-enabled>

image

2.6 Copy all of the JAR file in the BlaseDS lib directory to your lib directory, which in this example is MyGoogleServer/war/WEB-INF/lib

Before copy:

image

After copy:

image

2.7 If your server will be at a different location than your client, then you need to create a crossdomain.xml file to grant the client access.

image

In this case I like to share, so I am granting the entire internet access to the web services here. Normally you wouldn’t want to do this for reasons that should be obvious:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "
http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
    <allow-access-from domain="*" />
</cross-domain-policy>

3. Creating server-side logic

3.1 Create a class that will be used as a remote object that will just return “Hello”

package com.example.myproject.hello;

public class Hello {
    public String sayHello() {
        return "Hello World";
    }
}

3.2 Open the remoting-config.xml file and add the following node to map the Hello remote object:

<destination id="Hello">
        <properties>
            <source>com.example.myproject.hello.Hello</source>
        </properties>
</destination>

3.3 Open the messaging-config.xml file and add the following node to be able to use the chat variable for messaging:

<destination id="chat"/>

4. Creating the Flex client

4.1 Run the Google Server project

image

4.2 From the File menu create a new Flex Project

image

4.3 Specify the project name and set it to be a J2EE application server and don’t use WTP.

image

4.4 Uncheck the box to use the default LiceCycle Data services server and specify the location of the war directory of the Google Server project, and change the URL to reflect the the location of the server. Since I am planning on running the client on a different machine than the server, specify the Output folder to be bin-debug.

image

4.5 Specify the output folder URL to match the location on your local test server.

image

4.6 Run two instance of the Flex application, and notice how the sayHello button tests the Hello.java class, and you can use the Send B button to send message back and forth the the “chat” variable.

image

4.7 The Flex Code

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="
http://www.adobe.com/2006/mxml" creationComplete="consumer.subscribe()">
    <mx:RemoteObject id="blazeService" fault="faultHandler(event)" source="hello.Hello" destination="Hello">
        <mx:method name="sayHello" result="resultHandler(event)" />
    </mx:RemoteObject>
     <mx:Consumer id="consumer" destination="chat" message="messageHandler(event.message)"/>
    <mx:Producer id="producer" destination="chat"/>

    <mx:Script>
        <![CDATA[
            import mx.rpc.events.ResultEvent;
            import mx.rpc.events.FaultEvent;
            import mx.messaging.messages.AsyncMessage;
            import mx.messaging.messages.IMessage;
            private function send():void{
                var message:IMessage = new AsyncMessage();
                message.body.chatMessage = msg.text;
                producer.send(message);
                msg.text = "";
            }
            private function messageHandler(message:IMessage):void{
                log.text += message.body.chatMessage + "\n";
            }

            private function faultHandler(fault:FaultEvent):void {
                result_text.text = "code:\n" + fault.fault.faultCode + "\n\nMessage:\n" + fault.fault.faultString + "\n\nDetail:\n" + fault.fault.faultDetail;
            }
            private function resultHandler(evt:ResultEvent):void {
                result_text.text = evt.message.body.toString();
            }
        ]]>
    </mx:Script>
    <mx:Button x="10" y="132" label="sayHello" width="79" click="blazeService.getOperation('sayHello').send();"/>
    <mx:TextArea x="10" y="27" width="319" height="100" id="result_text"/>
    <mx:Panel title="Chat" width="100%" height="100%">
        <mx:TextArea id="log" width="100%" height="100%"/>
        <mx:ControlBar>
            <mx:TextInput id="msg" width="100%" enter="send()"/>
            <mx:Button label="Send B" click="send()"/>
        </mx:ControlBar>
    </mx:Panel>

</mx:Application>

5. Last Thoughts

I haven’t tried it, but apparently if you want to get remoting-only BlazeDS to work you have to do the following hack: http://martinzoldano.blogspot.com/2009/04/appengine-adobe-blazeds-fix.html

15 comments:

James Ward said...

Just a friendly reminder... Be careful with those crossdomain.xml files. A wildcard "*" policy should never be used on a site which uses cookies for authentication.

-James

Sekhar Ravinutala said...

Unfortunately, BlazeDS doesn't work with GAE yet (at least there's no official working release I know of). Will run on local development server, but not when you deploy it. I tried BlazeDS, WebORB, and GraniteDS and found GraniteDS to work really well. Check out my tutorial at http://blog.allurefx.com/2009/05/cloud-to-ria-accessing-google-app.html if your're interested.

John Valentino said...

Unfortunately you have to further hack BlazeDS to get it to deploy: http://martinzoldano.blogspot.com/2009/04/appengine-adobe-blazeds-fix.html
It is interesting that GraniteDS works right out of the box. I am checking it out right now.

Rex Kilian said...

Thanks for the tutorial, extremely helpful. I need to mock-up similar chat interaction. Could I use a local java engine [i.e. apache] and get the same type of example running?

John Valentino said...

The communication uses servlets so Java on the server is required.

David Daniel said...

Hi! Thanks for your useful post.

Chris said...

Thank you for this post !

Shiv Nikum said...

Hello,

I am using Flex Builder 3.
Unfortunately I am unable to run the application, the following message is displayed by browser

"Internet Explorer cannot display the webpage"

I think there is some issue with flex root url and output url

I am using sigle pc for both server and client.

John Valentino said...

Yes, that is an indication that your URL for the Flex application is not correct. Make sure that your server is running.

David said...

Have you actually tried deploying your app to the Google cloud? I have and the remoting part works well, but messaging doesn't since there is a limit on the instance run time in the cloud, the only work around I've found is to persist the chat info in the bigtable and serve the info to the client once it polls for the data. If you have managed to get the messaging working in the cloud, please post some more information.

Olewka said...

Dear John, Loved your post. Could you please describe in biger depth how to use GAE (Java) more complex DB creation (with stuff like many to one, many to many relations and tables retrival process), and If you'd cover it with Spring+BlazeDS+GAE It'd be grate!

duduzerah said...

Correcting a information in your post:

Actually is impossible to push data from server to client in AppEngine using BlazeDS. To do this you have to implement some bridge between Google Pub/Sub Api and your Flex Application.

allen said...

This looks absolutely perfect. All these tinny details are made with lot of background knowledge. I like it a lot. This was a useful post and I think it is rather easy to see from the other comments as well that this post is well written and useful.
20110118 pilipalagaga
office 2010

Nery said...

Parabéns pelo post! Excelente material para quem esta começando a programar com cloud computer!

Nery said...

Parabéns pelo post! Excelente material para quem esta começando a programar com cloud computer!