Showing posts with label BlazeDS. Show all posts
Showing posts with label BlazeDS. Show all posts

Thursday, January 1, 2009

Bare Bones BlazeDS Object Remoting

In this blog posting, I will demonstrate using BlazeDS for object remoting between a Flex client and a Java EE-based server. There are several good blog postings and articles on BlazeDS remoting out there already (see Additional Resources section below), but most of them either don't provide a complete example or apply FlexBuilder for some of the "magic." In this post, I will not use FlexBuilder and any IDE or text editor can be used. This allows one to see all the underlying details directly.


Tools Used in these Examples

For the examples in this blog posting, I will be using the following tools:

  • BlazeDS - I am using BlazeDS 3.2 and I am using the binary distribution (not Turnkey that includes Tomcat) to be more "bare bones."

  • Flex 3 SDK - I am using Flex 3.2. For convenience, I have set an environment variable FLEX_HOME to point to the main directory of my Flex 3 installation (where I unzipped it) and I have included %FLEX_HOME%\bin (or $FLEX_HOME/bin) on my PATH.

  • Java SE SDK - I am using Java SE 6 Update 10, but a newer or older version of Java SE should be fine as long as you are using at least J2SE 5.

  • Java EE web implementation - The BlazeDS Turnkey includes Tomcat, but I am using GlassFish for my examples. In theory, it should not matter which Java EE-compliant web server you use.

  • Ant (optional) - I provide a build.xml Ant build file in this posting that will build the Flex clients and will build the BlazeDS/JEE-powered server. However, these operations can be done individually as well. While I build a WAR file with my Ant build for the server-side deployment, one could simply edit directories and files in one's favorite server's deployment directory for the same effect. Similarly, one could build the Flex clients individually on the command line with the mxmlc command rather than using the build.xml file to do this. I am using Ant 1.7, but I am not aware of any features I am using specific to that version. Finally, I have my ANT_HOME environment variable set to my installation of Ant and have %ANT_HOME%/bin in my path for convenience in building.

  • Text Editor or IDE - You could use FlexBuilder, but do not need to for the examples in this posting. Other choices include NetBeans, Eclipse, JDeveloper, IDEA, JEdit, SpketIDE, vim, emacs, and even Notepad/Wordpad. If you do choose to use an IDE that supports XML, you can make things a little easier for yourself by associating MXML with XML as I demonstrate with NetBeans here and with JEdit here. Also, there are plug-ins for Flex for IDEs such as FlexBean for NetBeans. Also, it is my understanding that IDEA has built-in Flex support.



The following screen snapshot demonstrates the versions of these tools used as described above.




Setting Up BlazeDS for the Application

I won't go into any greater detail regarding installation of the Flex SDK, of the Java SDK, of a Java EE-compliant web server, of Ant, or of an IDE or text editor than that provided above. However, I will briefly look a little closer at installation of BlazeDS.

BlazeDS is open source and its source can be downloaded here, but the source code for BlazeDS is not needed for any of the examples in this blog posting.

The BlazeDS download page includes release builds and nightly builds. I am using a release build for the examples here. BlazeDS 3.2 Release Build 3978 is available as of this writing and I am using BlazeDS 3.2 Release Build 3978 for these examples.

An advantage of using the Turnkey download is that Tomcat web server is included with it. However, I intentionally want the examples in this posting to be "bare bones" and so will use a separate JEE implementation. With the non-Turnkey binary BlazeDS release downloaded (it will be called something like blazeds-bin-3.2.0.3978.zip), "installation" simply involves unzipping the ZIP file and extracting the WAR file (blazeds.war) from it. The next screen snapshot shows the contents of this binary BlazeDS download, including the WAR file just mentioned.



With access to the blazeds.war file, we now need to expand the blazeds.war's contents. We have two choices here. One popular choice is to expand this WAR file's contents in a web server's deployment directory. Many web servers will then automatically deploy the contents of that expanded directory. However, I will be taking the second approach here and will rebuild the WAR file for deployment with an Ant script. This provides greater portability between web servers and allows me to show better what needs to be included in the server-side BlazeDS-powered WAR file via the build.xml file. You should be able to use the direct deployment of expanded directory approach and not need Ant for the server-side if you prefer that approach.

The next screen snapshot shows the contents of the blazeds.war file that needs to be expanded.



For my examples, I will be expanding the blazeds.war file's contents into the C:\blazeDSRemotingExample) directory. I typically remove the META-INF directory and its contents from the expanded directory, so that it looks something like that shown in the next two screen snapshots. Note that the red lines through directories in the first screen snapshot are intentional because the entries that they cross out will eventually be built up, but will not be there when blazeds.war is initially expanded (in other words, your newly expanded directory should only have the WEB-INF directory if you deleted the META-INF directory as I did).





As the immediately preceding image shows, the expanded blazeds.war contents include some expected subdirectories beneath WEB-INF including classes and lib. Because we will rebuild our server's WAR file with this same expanded directory structure, we already have the necessary directories we need for the web application's classpath.


Tailoring BlazeDS Web Descriptor Configuration for Custom Application

Up until now, we've merely unzipped the downloaded binary BlazeDS file and then expanded the blazeds.war file into a directory. Now it is time to begin tweaking the configuration files included with the blazeds.war file to accommodate our application.

One of the first things I like to do when customizing my own BlazeDS-based application based on the provided blazeds.war file is to upgrade the web application descriptor file (web.xml) to reflect Servlet 2.5 rather than Servlet 2.3. I provide more detail on this process in my blog entry on OpenLaszlo with Java Servlets 2.5 (OpenLaszlo also provides an older web.xml file). The most important changes to note are that the DTD-described file becomes W3C XML Schema described instead, the version attribute is obviously updated from 2.3 to 2.5, and the <display-name> element is removed as a nested element under the <servlet> element. This particular change of versions of the web.xml file is probably not absolutely necessary in this case because I'm not using Java EE 5 annotations, but I make the change every time anyway.

The less important changes that I make to the provided web.xml file include removal of commented out section specifically intended for WebSphere (which I'm not using here) and other comments. The other change I make is to change the text for the high-level <display-name> and <description> elements. Indeed, when I have completed the transformation, the new web.xml file still looks very similar to the one provided with the blazeds.war file. Several of these changes are depicted in the next screen snapshot taken of a diff run against the two versions of web.xml using the ExamDiff tool.



The new and customized web.xml file is shown next.

New and Customized web.xml File

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
<display-name>Dustin's BlazeDS Hello World Example</display-name>
<description>An extremely simple BlazeDS Remoting Application</description>

<!-- 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>
<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>

<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>

</web-app>



Customizing Other BlazeDS Configuration Files

Besides the classes and lib subdirectories contained under WEB-INF in the directory expanded from the blazeds.war file, we also have subdirectories called src and flex. We will not customize a couple XML files inside the flex directory to tailor BlazeDS for our application's use.

The first file we'll look at is called services-config.xml. This file is central to all BlazeDS-based applications whether they are based on RPC or messaging. In fact, among other things, this file usually imports additional configuration files for things that are specific to proxying, to remoting, and to messaging.

I use the same approach with services-config.xml that I used with web.xml: I adapt the version provided by blazeds.war to work for the custom application. However, there is a twist in this one case. I want to have a slightly different version of this file for my Flex client than I have for my WAR that gets deployed on the server. Keeping the DRY principle in mind, my approach here is to create a services-config-template.xml file and use Ant's text substitution functionality to automatically generate the services-config.xml file to be used by both the Flex client and by the WAR that is deployed to the server. The next code listing shows the template file, which I placed in a newly created directory creatively called "templates".

templates/services-config-template.xml

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

<services>
<service-include file-path="remoting-config.xml" />
<service-include file-path="proxy-config.xml" />
<service-include file-path="messaging-config.xml" />
</services>

<security>
<login-command class="flex.messaging.security.TomcatLoginCommand" server="Tomcat"/>
</security>

<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://@host@:@port@/{context.root}/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
<channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel">
<endpoint url="https://@host@:@port@/{context.root}/messagebroker/amfsecure"
class="flex.messaging.endpoints.SecureAMFEndpoint"/>
<properties>
<add-no-cache-headers>false</add-no-cache-headers>
</properties>
</channel-definition>

<channel-definition id="my-polling-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://@host@:@port@/{context.root}/messagebroker/amfpolling" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>true</polling-enabled>
<polling-interval-seconds>4</polling-interval-seconds>
</properties>
</channel-definition>
</channels>

<logging>
<target class="flex.messaging.log.ConsoleTarget" level="Error">
<properties>
<prefix>[BlazeDS] </prefix>
<includeDate>false</includeDate>
<includeTime>false</includeTime>
<includeLevel>false</includeLevel>
<includeCategory>false</includeCategory>
</properties>
<filters>
<pattern>Endpoint.*</pattern>
<pattern>Service.*</pattern>
<pattern>Configuration</pattern>
</filters>
</target>
</logging>

<system>
<redeploy>
<enabled>false</enabled>
</redeploy>
</system>

</services-config>


The code for the template version of services-config-template.xml shown immediately above includes Ant token markers (@host@ and @port@) that will be replaced by Ant with an actual host name and a port number for the client's version of services-config.xml). These same tokens will be replaced with BlazeDS-recognized tokens {server.name} and {server.port} for the server-side version of services-config.xml. Note that I was able to leave the BlazeDS template {context.root} in the template for the client-side version of services-config.xml because the mxmlc compiler allows me to specify its value for the client as an option.

I left the reference in services-config.xml to all three previously referenced files (remoting-config.xml, proxy-config.xml, and messaging-config.xml), but really only need the reference to remoting-config.xml for this blog post examples. As with the web.xml file, my approach to creation of that referenced remoting-config.xml file is to simply adapt the one provided with the expanded blazeds.war file. The only necessary change is to specify the Java classes that will be used on the server side. We haven't looked at this Java class yet, but the remoting-config.xml file shown in the next code listing tips us off that it will be a class named HelloWorldRemotingServer in a package named dustin.

remoting-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service"
class="flex.messaging.services.RemotingService">

<adapters>
<adapter-definition id="java-object" class="flex.messaging.services.remoting.adapters.JavaAdapter" default="true"/>
</adapters>

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

<destination id="HelloWorld">
<properties>
<source>dustin.HelloWorldRemotingServer</source>
</properties>
</destination>
</service>



The above remoting-config.xml file specifies that the destination associated with this Java class on the server side can be referenced as "HelloWorld" (the 'id' attribute of the destination element).

Whew! That wraps up the BlazeDS configuration modifications. If you have not looked into this before, it looks worse than it really is. Most of the final content shown in the code listings above was already provided by the blazeds.war that we expanded. I only made some relatively small modifications to the web.xml and remoting-config.xml files. The services-config.xml file likewise only required some minor changes, but the more significant issue there was that I needed to prepare a different version of this file for the client than for the server. The server version of services-config.xml can resolve the BlazeDS tokens {host.name} and {host.port} based on the web server settings, but I specify a hard-coded host name and port for the client version of services-config.xml.


The Java Class

The contents of the remoting-config.xml file provided us with a sneak peek at some details about the Java class on the server. The definition of this simple Java class is shown next. Note that this file is placed in a subdirectory called dustin (because it is in the "dustin" Java package) under the src subdirectory under WEB-INF in our expanded directory.

HelloWorldRemotingServer.java (1st Version)

package dustin;

/**
* Simple "Hello World" example meant for use in BlazeDS remoting example.
* This is the server-side POJO that will be exposed to a Flex client via
* BlazeDS remoting.
*
* @author Dustin
*/
public class HelloWorldRemotingServer
{
/** No-arguments constructor. */
public HelloWorldRemotingServer() {}

/**
* Accept a String representing a name and return that name as part of a
* Hello message.
*
* @param nameToSayHelloTo Name to which Hello will be addressed.
* @return Hello address to provided name.
*/
public String processHello(final String nameToSayHelloTo)
{
return "Hello, " + nameToSayHelloTo + "!";
}
}


The important thing to note from the code listing for the Java class immediately above is that it is a regular Java class with no knowledge of Flex or ActionScript.


The Flex Client (Hello World Client)

The Java class we just looked at returns a provided String surrounded with a Hello World prefix and exclamation point. We'll now look at the Flex MXML code that can be used to invoke this Java object and this processHello(String) method via BlazeDS.

HelloWorldRemotingClient.mxml

<?xml version="1.0" encoding="UTF-8" ?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="1100" height="700">

<!-- Associate client with BlazeDS destination via RemoteObject. -->
<mx:RemoteObject id="remoteObject" destination="HelloWorld" />

<mx:Panel id="mainPanel" title="Simple BlazeDS Remoting Example - Hello World">
<mx:Form>
<mx:FormItem label="Provide Your Name">
<mx:TextInput id="textInput"
change="remoteObject.processHello(textInput.text)"/>
</mx:FormItem>
<mx:FormItem label="Server's Response">
<mx:Label text="{remoteObject.processHello.lastResult}"/>
</mx:FormItem>
</mx:Form>
</mx:Panel>
</mx:Application>


The MXML code above is very simple. The RemoteObject element (with an 'id' of "remoteObject") specifies a destination (via the 'destination' attribute) of "HelloWorld" (defined above in the remoting-config.xml file).

If you plan to use the build.xml file provided in this blog posting, this MXML file should be placed at parallel to the WEB-INF directory (as opposed to in the WEB-INF directory like the Java class and configuration files previously discussed).


Building it All

Even though I still need to show another Flex client, an ActionScript class, and another Java class whose instantiation is bound to the ActionScript class's instantiation, I am including the entire Ant build.xml file here for convenience. Some of the targets defined in this file will not work until the remaining classes discussed later in this blog posting are included.

build.xml (Ant)

<?xml version="1.0" encoding="UTF-8"?>
<project name="HelloWorldBlazeDSRemotingExample" default="all" basedir=".">
<description>Builds, tests, and runs the Dustin's Hello World BlazeDS Remoting Example.</description>

<!-- NOTE TO USERS: At least three properties set below may need to be
set for this build file to work with different deployment
environments. The three minimum properties to examine
are client.host, client.port, and app.dir.
-->

<!-- GENERAL PROPERTIES -->
<!--
*********************************************************************
Change server's host name and port (determined by web server
deployment of server side of code).
*********************************************************************
-->
<property name="client.host" value="localhost" />
<property name="client.port" value="8080" />
<!--
*********************************************************************
Change definition of app.dir if necessary (to where blazeds.war
has been uncompressed).
*********************************************************************
-->
<property name="app.dir" value="C:\\blazeDSRemotingExample" />
<property name="web.inf.dir" value="${app.dir}/WEB-INF" />
<property name="templates.dir" value="${app.dir}/templates" />
<property name="context.name" value="HelloWorldRemotingServer" />


<!-- GENERAL JAVA PORTION PROPERTIES -->
<property name="src.dir" value="${web.inf.dir}/src" />
<property name="classes.dir" value="${web.inf.dir}/classes" />
<property name="lib.dir" value="lib" />
<property name="dist.dir" value="${app.dir}/dist" />
<property name="javadoc.dir" value="${dist.dir}/javadoc" />
<property name="serverside.war" value="${context.name}.war" />

<!-- Nothing special needed for classpath! -->
<path id="classpath" />


<!-- BLAZEDS RELATED PROPERTIES -->
<property name="services.config.server.name"
value="services-config.xml" />
<property name="services.config.client.name"
value="services-config-client.xml" />
<property name="services.config.template"
value="services-config-template.xml" />


<!-- GENERAL FLEX PORTION PROPERTIES (FLEX CLIENT) -->
<property name="app.flex.dir" value="${web.inf.dir}/flex" />
<property name="flex.debug" value="true" />
<property name="flex.warnings" value="true" />
<property name="flex.context.root" value="${context.name}" />
<property name="flex.services"
value="${app.flex.dir}/${services.config.client.name}" />
<property name="flex.source.path" value="${web.inf.dir}" />
<property name="flex.publisher" value="Dustin" />
<property name="flex.title"
value="'Hello World BlazeDS Remoting Example'" />
<property name="flex.description"
value="'Simple client for BlazeDS Remoting example'" />
<property name="flex.verbose.stacktraces" value="true" />
<property name="flex.actionscript.warnings" value="true" />
<property name="flex.app.helloworld.name.root"
value="HelloWorldRemotingClient" />
<property name="flex.app.helloworld.name.source"
value="${flex.app.helloworld.name.root}.mxml" />
<property name="flex.app.helloworld.name.swf"
value="${flex.app.helloworld.name.root}.swf" />
<property name="flex.app.complexobject.name.root"
value="ComplexObjectRemotingClient" />
<property name="flex.app.complexobject.name.source"
value="${flex.app.complexobject.name.root}.mxml" />
<property name="flex.app.complexobject.name.swf"
value="${flex.app.complexobject.name.root}.swf" />



<target name="-init">
<mkdir dir="${dist.dir}" />
<mkdir dir="${javadoc.dir}" />
</target>

<target name="-copy-respective-services-config-files"
depends="-clean-generated-blazeds-config-files">
<copy file="${templates.dir}/${services.config.template}"
tofile="${app.flex.dir}/${services.config.server.name}" >
<filterchain>
<replacetokens>
<token key="host" value="{server.name}"/>
<token key="port" value="{server.port}"/>
</replacetokens>
</filterchain>
</copy>
<copy file="${templates.dir}/${services.config.template}"
tofile="${app.flex.dir}/${services.config.client.name}" >
<filterchain>
<replacetokens>
<token key="host" value="${client.host}"/>
<token key="port" value="${client.port}"/>
</replacetokens>
</filterchain>
</copy>
</target>

<target name="compile"
depends="compileJavaBasedServer, compileFlexBasedClients"
description="Compile Java and Flex code." />

<target name="compileJavaBasedServer"
description="Compile Java source code used for server side.">
<javac srcdir="${src.dir}"
destdir="${classes.dir}"
classpathref="classpath" />
</target>

<target name="compileFlexBasedClients"
description="Compile the Flex clients"
depends="compileFlexBasedHelloWorldClient,
compileFlexBasedComplexObjectClient" />

<target name="compileFlexBasedHelloWorldClient"
description="Compile Flex Hello World client application into SWF file.">
<exec executable="mxmlc">
<arg line="-debug=${flex.debug}" />
<arg line="-context-root=${flex.context.root}" />
<arg line="-services=${flex.services}" />
<arg line="-warnings=${flex.warnings}" />
<arg line="-publisher=${flex.publisher}" />
<arg line="-title=${flex.title}" />
<arg line="-description=${flex.description}" />
<arg line="-verbose-stacktraces=${flex.verbose.stacktraces}" />
<arg line="-show-actionscript-warnings=${flex.actionscript.warnings}" />
<arg line="-output ${dist.dir}/${flex.app.helloworld.name.swf}" />
<arg line="-- ${flex.app.helloworld.name.source}" />
</exec>
</target>

<target name="compileFlexBasedComplexObjectClient"
description="Compile Flex Complex Object client application into SWF file.">
<exec executable="mxmlc">
<arg line="-debug=${flex.debug}" />
<arg line="-context-root=${flex.context.root}" />
<arg line="-services=${flex.services}" />
<arg line="-warnings=${flex.warnings}" />
<arg line="-publisher=${flex.publisher}" />
<arg line="-title=${flex.title}" />
<arg line="-description=${flex.description}" />
<arg line="-verbose-stacktraces=${flex.verbose.stacktraces}" />
<arg line="-show-actionscript-warnings=${flex.actionscript.warnings}" />
<arg line="-output ${dist.dir}/${flex.app.complexobject.name.swf}" />
<arg line="-- ${flex.app.complexobject.name.source}" />
</exec>
</target>

<target name="war"
description="Build server's web application archive (WAR)."
depends="-init,
-copy-respective-services-config-files,
compileJavaBasedServer">
<war webxml="${web.inf.dir}/web.xml"
destfile="${dist.dir}/${serverside.war}"
filesonly="true">
<zipfileset dir="${web.inf.dir}"
excludes="web.xml
flex/${services.config.client.name}"
prefix="WEB-INF" />
</war>
</target>

<target name="all" depends="war, compileFlexBasedClients"
description="Build it all; the default target." />

<target name="clean" description="Remove generated files."
depends="-clean-generated-blazeds-config-files">
<delete dir="${dist.dir}" />
<delete dir="${classes.dir}/dustin" />
</target>

<target name="-clean-generated-blazeds-config-files">
<delete file="${app.flex.dir}/${services.config.server.name}"
quiet="true" />
<delete file="${app.flex.dir}/${services.config.client.name}"
quiet="true"/>
</target>

<target name="javadoc" description="Generate Javadoc-based documentation">
<javadoc doctitle="Dustin's Spring BlazeDS Integration Example"
destdir="${javadoc.dir}"
sourcepath="${src.dir}"
classpathref="classpath"
private="true"
author="Dustin" />
</target>

</project>


In the above build.xml file, there are three properties defined near the top that would most likely need to be changed for a different deployment environment. They are client.host, client.port, and app.dir. The first two are used to appropriately build the client's version of services-config.xml to reference the host and port on which the server BlazeDS-powered application will be running. Because I am using GlassFish defaults for my server, I define these properties respectively to localhost and 8080. The third property, app.dir defines the directory into which I expanded the contents of the blazeds.war file for editing.

Targets of specific interest in the above build.xml file include the compileFlexBasedHelloWorldClient target for building the Flex client with the mxmlc compiler and the war target for building the WAR file to be deployed on the server.

When I build the WAR and the client SWF file (same name as its source MXML file but with an .swf extension rather than the .mxml extension), I can then deploy the WAR and run the SWF client. The deployment of the WAR file is done with whatever mechanism your web serve provides. The SWF client can easily be executed by clicking on it or typing its full name on the command line. The following two screen snapshots show the client running by first showing how it initially appears and then showing how it appears once a name has been entered into the name field.






Binding Java to ActionScript

The above example demonstrates the ability to remotely call Java on the server from MXML on the client via the RemoteObject and BlazeDS. However, the example up to this point has only used simple mappings (in this case Strings). It is more interesting to now move onto an example of binding a more complex objects. The good news is that it is all still pretty easy.

For this slightly more complex example, we will expand the HelloWorldRemotingServer class to feature a method that accepts a domain object that is neither a primitive, a boxed version of a primitive, or a String. The revised version of HelloWorldRemotingServer is shown next:

HelloWorldRemotingServer.java (Finished Version)

package dustin;

/**
* Simple "Hello World" example meant for use in BlazeDS remoting example.
* This is the server-side POJO that will be exposed to a Flex client via
* BlazeDS remoting.
*
* @author Dustin
*/
public class HelloWorldRemotingServer
{
/** No-arguments constructor. */
public HelloWorldRemotingServer() {}

/**
* Accept a String representing a name and return that name as part of a
* Hello message.
*
* @param nameToSayHelloTo Name to which Hello will be addressed.
* @return Hello address to provided name.
*/
public String processHello(final String nameToSayHelloTo)
{
return "Hello, " + nameToSayHelloTo + "!";
}

/**
* Accept a last name and a first name and return the concatendated name as
* part of a James Bond like introduction.
*
* @param firstName Provided first name of person.
* @param lastName Provided last name of person.
* @return The James Bond like introductory message.
*/
public String processName(final String firstName, final String lastName)
{
return "Your name is " + lastName + ". " + firstName + " " + lastName + ".";
}

/**
* Accept a Person and return that person's name as part of a James Bond like
* introduction.
*
* @param person Person whose name will be returned.
* @return The James Bond like introductory message.
*/
public String processName(final Person person)
{
return "Your name is " + person.getLastName() + ". "
+ person.getFirstName() + " " + person.getLastName() + "!";
}
}


One of the newly added methods is called processName(String,String). This wouldn't make a very exciting example because it still uses two separate Strings. The more interesting example will be binding a Flex client to the processName(Person) method that expects a Person instance. For our newly updated class to compile, we need to define a Person class in the same package. That is shown next:

Person.java

package dustin;

/**
* Encapsulates a person's information.
*
* @author Dustin
*/
public class Person
{
/** Last name of person. */
private String lastName;

/** First name of person. */
private String firstName;

/** No-arguments constructor. */
public Person() {}

/**
* Constructor accepting names for this person.
*
* @param newLastName Last name of the person.
* @param newFirstName First name of the person.
*/
public Person(final String newLastName,
final String newFirstName,
final int newAge)
{
this.lastName = newLastName;
this.firstName = newFirstName;
}

/**
* Provide person's first name.
*
* @return Person's first name.
*/
public String getFirstName()
{
return this.firstName;
}

/**
* Set/change the person's first name.
*
* @param newFirstName New first name for the person.
*/
public void setFirstName(final String newFirstName)
{
this.firstName = newFirstName;
}

/**
* Provide person's last name.
*
* @return Person's last name.
*/
public String getLastName()
{
return this.lastName;
}

/**
* Set/change person's last name.
*
* @param newLastName New last name for person.
*/
public void setLastName(final String newLastName)
{
this.lastName = newLastName;
}

/**
* String representation of me.
*
* @return My String representation.
*/
@Override
public String toString()
{
return this.firstName + " " + this.lastName;
}
}


There is nothing remarkable about the Person class and there is no sign of any association with ActionScript or Flex in this Java class. In fact, the Person class is downright boring.

For Strings, primitives, and object/boxed versions of primitives, BlazeDS can typically map these types to appropriate ActionScript types. Now that we've introduced the Person type, however, we need to instruct BlazeDS how to use this Java class on the Flex-based client side. This is easily done by simply creating a parallel Person class in ActionScript that explicitly associates itself with the Java version of Person. The code for the ActionScript version of Person is shown next.

Person.as

package dustin
{
[Bindable]
[RemoteClass(alias="dustin.Person")]
public class Person
{
/** First name of person. */
private var _firstName:String;

/** Last name of person. */
private var _lastName:String;

/**
* Constructor accepting names of this newly instantiated person.
* Because each parameter has a default of null, this can be used as a
* no-arguments constructor.
*
* @param newFirstName First name to be used for this person.
* @param newLastName Last name to be used for this person.
*/
public function Person(
newFirstName:String = null,
newLastName:String = null)
{
_firstName = newFirstName;
_lastName = newLastName;
}

/**
* WRITE (set/change) firstName property.
*
* @param newFirstName New first name.
*/
public function set firstName(newFirstName:String):void
{
_firstName = newFirstName;
}

/**
* READ firstName property.
*
* @return My first name.
*/
public function get firstName():String
{
return _firstName;
}

/**
* WRITE (set/change) lastName property.
*
* @param newLastName new last name.
*/
public function set lastName(newLastName:String):void
{
_lastName = newLastName;
}

/**
* READ lastName property.
*
* @return My last name.
*/
public function get lastName():String
{
return _lastName;
}
}
}


Arguably the most interesting point about the ActionScript version of Person is the use of RemoteClass metadata tag ([RemoteClass(alias="dustin.Person")]) to explicitly associate this ActionScript class with the Java class. By naming the property write/set and read/get methods to match those in the Java class, the remaining binding of ActionScript object to Java object is handled by BlazeDS.

The ActionScript Person.as file should be placed in a newly created subdirectory named dustin (for the "dustin" package) parallel to the WEB-INF directory. We now need to move onto building a new Flex client to use this new method on the server-side.

I want to make one final observation regarding the Person class in ActionScript. The code would have been even more concise if I had made the data members public. In that case, they could be accessed directly and the set and get methods would not be necessary. Many of the online examples take this approach.


A New Flex Client (Complex Object Client)

The code for ComplexObjectRemotingClient.mxml is shown next. It should be placed in the same location as our previous Flex client (in expanded directory and parallel to the WEB-INF subdirectory).

ComplexObjectRemotingClient.mxml

<?xml version="1.0" encoding="UTF-8" ?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="1100" height="700">

<mx:Script>
import dustin.Person;

/**
* Prepare provided name information and send to server.
*
* @param firstName First name of person.
* @param lastName Last name of person.
*/
public function processAndSubmitName(firstName:String, lastName:String):void
{
const person:Person = new Person(firstName, lastName);
remoteObject.processName(person);
}
</mx:Script>

<!-- Associate client with BlazeDS destination via RemoteObject. -->
<mx:RemoteObject id="remoteObject" destination="HelloWorld" />

<mx:Panel id="mainPanel" title="BlazeDS Remoting Example - Complex Object Mapping">
<mx:Form>
<mx:FormItem label="Your First Name">
<mx:TextInput id="firstNameInput" />
</mx:FormItem>
<mx:FormItem label="Your Last Name">
<mx:TextInput id="lastNameInput" />
</mx:FormItem>
<mx:FormItem>
<mx:Button label="Submit Name!"
click="processAndSubmitName(firstNameInput.text, lastNameInput.text);" />
</mx:FormItem>
<mx:FormItem label="Server's Response">
<mx:Label text="{remoteObject.processName.lastResult}" />
</mx:FormItem>
</mx:Form>
</mx:Panel>
</mx:Application>


The new Flex client uses a little ActionScript to prepare the call to the server and instantiates one of our newly written Person objects in the process. The build.xml file shown previously already included the target for building this new client ("compileFlexBasedComplexObjectClient"). Note that the Java code also needs to be rebuilt and the WAR reassembled and redeployed to the web server because of the addition of methods to the server class.

The following two screen snapshots show the initial and final state of the new client when it is executed as the Hello World client was earlier.





As the above output shows, the method accepting a Person object (note the exclamation point in the returned message) has been contacted on the Java side from a remote Flex client.


Pictorial Overview of Directory/File Structure

One of the things that can be a little tricky about using BlazeDS or the mxmlc compiler if you have very little experience using them is the placement of files. Fortunately, most of this was already setup correctly by the blazeds.war file that we expanded. However, I did add some directories and files. In an effort to make it more clear where these directories and files are in relation to one another, I include the next screen snapshot of my final directory structure. I tried in the text above to describe where within these various directories each file should go.



The image above shows where to place MXML source files, ActionScript source files, and Java source files for the build.xml file described above to work properly. I did not provide descriptions of the nbproject subdirectory (used for NetBeans specifically), the images subdirectory (used to store the screen snapshots shown in this blog posting), or the WEB-INF subdirectory (it was provided by the expanding of blazeds.war). The only thing to note here is that I added the Java source code files (but not ActionScript source code files) into the src directory under WEB-INF.


Conclusion

This has turned out to be a rather lengthy blog entry. However, this is to be expected considering that I've intentionally tried to make this a complete and thorough introduction to BlazeDS/Java remoting and because I have included the code directly within the blog posting rather than as a separate attachment. Also, I've tried to avoid use of anything specific to FlexBuilder so that anyone with any text editor should be able to use my examples.

Despite the length of this blog entry, use of the BlazeDS remoting is pretty straightforward once you have tried it a couple times. It does get more complex when security constraints and other details are added back in, but one can choose how much one implements at each step and the examples above are nice for confirming that client-to-server communication is working properly.


Additional Resources

Friday, May 9, 2008

Bare Bones BlazeDS HTTPService Example

This blog entry is intended to demonstrate an extremely basic example of the BlazeDS HTTPService. I have discussed the BlazeDS HTTPService at a high level in a previous blog entry. In this entry, I'll show it in action with code and screen snapshots. If you're looking for a similarly simple example of using BlazeDS's JMS Messaging support, see Michael Martin's blog entry Simplified BlazeDS and JMS.

Perhaps the easiest way to start working with BlazeDS is to have the BlazeDS Developer Guide handy for reference and to modify the sample files included with the BlazeDS download to one's own application. In this blog entry, I'll show BlazeDS configuration files that were modified from files available in the samples. I have removed much of the extraneous details that are not relevant to HTTPService to make the files clearer to understand.

The following steps are required to build a first BlazeDS-powered example.

1. Download Flex 3 (or Flex 2 will work).

2. Unzip/expand the downloaded Flex file to a directory. For example, I have Flex 3 installed at C:\flex_sdk_3 for this example.

3. Download BlazeDS (it is still separate even from Flex 3). There are multiple versions of BlazeDS (nightly build or release builds, turnkey or binary or source, etc.). For my example here, I'm using the binary download. I didn't need Tomcat (which comes with the Turnkey version) because I already had it installed. However, I downloaded the Turnkey edition because it has the same blazeds.war file needed for my example (or for any BlazeDS-powered application). In addition, it has a ds-console.war and the sample files to learn from and adapt. The ds-console.war file is useful because it provides Flash-based view into the BlazeDS server once we have it up and running.

4. Unzip/expand the downloaded BlazeDS file into a directory. I expanded the download file into a temporary directory and then expanded the blazeds.war into a directory of its own called C:\blazeds-expanded. I expanded this so that I could have my own web application (WAR file) be built with the necessary pieces of this included. For example, the expanded contents include important JAR files in the C:\blazeds-expanded\WEB-INF\lib directory (which we will bundle into our WAR's WEB-INF/lib) and the BlazeDS XML-based configuration files in the C:\blazeds-expanded\WEB-INF\flex directory that we will edit for this example and place in our generated WAR's WEB-INF/flex directory.

5. Download Tomcat. I chose to use a separate Tomcat rather than the one included with BlazeDS turnkey.

6. Unzip/expand the Tomcat download. Instructions for installing/setting up Tomcat are available here. I installed Tomcat at C:\apache-tomcat-6.0.16.

7. Build the server-side servlet for supporting HTTPService. In most realistic cases, this is likely an already existing servlet. The code for this example's very basic servlet is shown next.


package dustin;

import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

/**
* Simple example intended to demonstrate BlazeDS with HttpService.
*/
public class BlazeHttpExample extends HttpServlet
{
/**
* Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
* @param request servlet request
* @param response servlet response
*/
protected void processRequest(
HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
final String xmlProlog = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
final String lineSeparator = System.getProperty("line.separator");
response.setContentType("text/html;charset=UTF-8");
final PrintWriter out = response.getWriter();
try
{
final String user = request.getParameter("user");
out.print( xmlProlog + lineSeparator
+ "<Messages><Message>Hello, " + user
+ "!</Message></Messages>");
}
finally
{
out.close();
}
}

/**
* Handles the HTTP <code>GET</code> method.
* @param request servlet request
* @param response servlet response
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
processRequest(request, response);
}

/**
* Handles the HTTP <code>POST</code> method.
* @param request servlet request
* @param response servlet response
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
processRequest(request, response);
}

/**
* Returns a short description of the servlet.
*/
@Override
public String getServletInfo()
{
return "Simple intended to illustrate BlazeDS HttpService support.";
}
}


The highlighted section of code above shows the most important piece of the servlet. This simple servlet accepts a URL parameter "user" and places that in a Hello World-inspired String to return to the client.

8. Generate or edit an appropriate web.xml file. You can copy the web.xml file from the downloaded and expanded blazeds.war file and edit that. However, it comes with a Servlet 2.2-compliant web.xml file and I prefer a Servlet 2.5-compliant web.xml file (see this blog entry for the reason). So I prefer to paste the relevant portions from the BlazeDS-provided web.xml file into my own web.xml file that is servlet 2.5 based. This is often the case anyway because you often have your web application already. The next code listing shows the web.xml file for this example.


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>Dustin's BlazeDS Example</display-name>
<description>Example Using BlazeDS</description>

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

<!-- 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>
<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>
<servlet-name>BlazeDSExampleServlet</servlet-name>
<servlet-class>dustin.BlazeHttpExample</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>BlazeDSExampleServlet</servlet-name>
<url-pattern>/blazeDS</url-pattern>
</servlet-mapping>

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

<welcome-file-list>
<welcome-file>index.htm</welcome-file>
</welcome-file-list>

<login-config>
<auth-method>BASIC</auth-method>
</login-config>

</web-app>


9. Adapt the services-config.xml file and the proxy-config.xml files from the BlazeDS sample to work for this example. The next code listing shows the contents of the services-config.xml after it has been adapted and simplified for this example.


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

<services>
<service-include file-path="proxy-config.xml" />
<default-channels>
<channel ref="my-amf"/>
</default-channels>
</services>

<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>false</polling-enabled>
</properties>
</channel-definition>
<channel-definition id="my-http" class="mx.messaging.channels.HTTPChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/http"
class="flex.messaging.endpoints.HTTPEndpoint"/>
</channel-definition>
</channels>

<logging>
<!-- You may also use flex.messaging.log.ServletLogTarget -->
<target class="flex.messaging.log.ConsoleTarget" level="Error">
<properties>
<prefix>[BlazeDS] </prefix>
<includeDate>false</includeDate>
<includeTime>false</includeTime>
<includeLevel>true</includeLevel>
<includeCategory>false</includeCategory>
</properties>
<filters>
<pattern>Endpoint.*</pattern>
<pattern>Service.*</pattern>
<pattern>Configuration</pattern>
</filters>
</target>
</logging>

<system>
<redeploy>
<enabled>true</enabled>
<watch-interval>20</watch-interval>
<watch-file>{context.root}/WEB-INF/flex/services-config.xml</watch-file>
<watch-file>{context.root}/WEB-INF/flex/proxy-config.xml</watch-file>
<watch-file>{context.root}/WEB-INF/flex/remoting-config.xml</watch-file>
<watch-file>{context.root}/WEB-INF/flex/messaging-config.xml</watch-file>
<touch-file>{context.root}/WEB-INF/web.xml</touch-file>
</redeploy>
</system>

</services-config>


The services-config.xml file showed above is necessary for any BlazeDS-based server support. As shown in the listing above, the services-config.xml file points to another file, proxy-config.xml for HTTPService-specific configuration details. It could have been a section directly within the services-config.xml, but BlazeDS externalized the specific details and that feels more modular and easier to manage to me as well. Note that BlazeDS Developer Guide points out that HTTPService and WebService are configured as proxy services (often configured as in this example in an a proxy-config.xml file) while BlazeDS Remote Objects are configured in remoting services (often in an external file remoting-config.xml) and BlazeDS Messaging is configured as messaging services (often in an external file messaging-config.xml). Because the example covered here only uses HTTPService, only the proxy-config.xml file is needed in addition to the services-config.xml file.

The next code listing shows the contents of the simplified proxy-config.xml file.


<?xml version="1.0" encoding="UTF-8"?>
<service id="proxy-service" class="flex.messaging.services.HTTPProxyService">

<properties>
<connection-manager>
<max-total-connections>100</max-total-connections>
<default-max-connections-per-host>2</default-max-connections-per-host>
</connection-manager>

<allow-lax-ssl>true</allow-lax-ssl>
</properties>

<default-channels>
<channel ref="my-http"/>
<channel ref="my-amf"/>
</default-channels>

<adapters>
<adapter-definition id="http-proxy"
class="flex.messaging.services.http.HTTPProxyAdapter"
default="true"/>
</adapters>

<destination id="BlazeDSHTTP">
<properties>
<url>/{context.root}/blazeDS</url>
</properties>
</destination>

</service>


10. Create the Flex client that will "talk" to the BlazeDS server. A simple Flex client is shown next. It enables communication with the servlet described above both via the standard HTTPService that does not use BlazeDS and via the BlazeDS-based HTTPService that has been the central part of this blog entry.


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="750" height="500">

<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.ItemClickEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

private const radioButtonFieldSize:int = 250;
private const resultsTextAreaHeight:int = 150;
private const resultsTextAreaWidth:int = 300;
private const userServiceNoBlazeDsUrl:String =
"http://localhost:8080/dustin-blazeds/blazeDS";

protected function invokeHttpService(event:ItemClickEvent):void
{
const selectedService:String = event.currentTarget.selectedValue;
if ( selectedService == "httpNoBlazeDS")
{
userServiceHttpSansBlazeDS.send();
}
else if ( selectedService == "httpYesBlazeDS" )
{
userServiceHttpWithBlazeDS.send({user: 'Someone Else'});
}
else
{
Alert.show("An unknown Service was requested");
}
}

protected function resultHandlerSansBlazeDS(event:ResultEvent):void
{
serviceResultsTextArea.text =
"Success withOUT BlazeDS!\n"
+ userServiceHttpSansBlazeDS.lastResult.Message;
}

protected function resultHandlerWithBlazeDS(event:ResultEvent):void
{
serviceResultsTextArea.text =
"Success with BlazeDS!\n"
+ userServiceHttpWithBlazeDS.lastResult.Message;
}

protected function faultHandler(event:FaultEvent):void
{
serviceResultsTextArea.text =
"Failure trying to access service.\n"
+ event.fault.faultString + "\n" + event.fault.faultDetail;
}
]]>
</mx:Script>

<mx:HTTPService id="userServiceHttpSansBlazeDS"
useProxy="false"
resultFormat="e4x"
url="{userServiceNoBlazeDsUrl}?user=Dustin"
fault="faultHandler(event)"
result="resultHandlerSansBlazeDS(event)" />

<mx:HTTPService id="userServiceHttpWithBlazeDS"
useProxy="true"
resultFormat="e4x"
destination="BlazeDSHTTP"
fault="faultHandler(event)"
result="resultHandlerWithBlazeDS(event)" />

<mx:Panel id="mainPanel" title="Output from HTTP Service Calls">
<mx:RadioButtonGroup id="serviceCallType"
itemClick="invokeHttpService(event);" />
<mx:RadioButton groupName="serviceCallType"
id="httpWithoutBlazeDS"
value="httpNoBlazeDS"
label="HTTPService without BlazeDS"
width="{radioButtonFieldSize}" />
<mx:RadioButton groupName="serviceCallType"
id="httpWithBlazeDS"
value="httpYesBlazeDS"
label="HTTPService with BlazeDS"
width="{radioButtonFieldSize}" />
<mx:TextArea id="serviceResultsTextArea"
width="{resultsTextAreaWidth}"
height="{resultsTextAreaHeight}" />
</mx:Panel>

</mx:Application>


I intentionally created this Flex client to use both standard HTTPService and BlazeDS-based HTTPService to demonstate how alike they are, but also to show the subtle differences. The most significant difference is that one specifies a url attribute for the non-BlazeDS HTTPService while one specifies a destination attribute for the BlazeDS-based HTTPService. This destination corresponds with the defined destination in the configuration XML files previously examined.

11. Build the Flex client (compile the MXML source into a SWF file); build the servlet; and package the client, servlet, and all the configuration files and BlazeDS JAR files into a WAR to be deployed. This is most easily done with a build script. I use Ant for this example and the build.properties and build.xml files are shown next.

build.xml

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

<project name="JavaBlazeDS" default="default" basedir=".">

<property file="build.properties" />

<path id="classpath">
<pathelement location="${lib.servlet}"/>
</path>

<target name="initialize">
<mkdir dir="${dir.build}" />
<mkdir dir="${dir.classes}" />
</target>

<target name="clean"
description="Clean all generated/compiled files.">
<delete dir="${dir.build}" />
</target>

<target name="default"
depends="buildWar"
description="Default target for this build." />

<target name="compileFlex"
description="Compile Flex application into SWF file.">
<exec executable="mxmlc">
<arg line="-debug=${flex.debug}" />
<arg line="-context-root=${flex.context.root}" />
<arg line="-services=${flex.services}" />
<arg line="-publisher=${flex.publisher}" />
<arg line="-title=${flex.title}" />
<arg line="-description=${flex.description}" />
<arg value="${dir.flex.src}/BlazeDSInterface.mxml" />
<arg line="-output ${dir.build}/BlazeDSInterface.swf" />
</exec>
</target>

<target name="compileJava" depends="initialize"
description="Compile server-side Java code.">
<javac srcdir="${dir.src.java}"
classpathref="classpath"
destdir="${dir.classes}"/>
</target>

<target name="buildWar"
depends="compileFlex,compileJava"
description="Generate WAR file with Flex and server-side Java.">
<war destfile="${dir.build}/${war.dustin-blazeds-example}"
webxml="${dir.web-inf}/${file.web.xml}">
<lib dir="${dir.blazeds.lib}" includes="*.jar" />
<classes dir="${dir.classes}" includes="**/*.class" />
<zipfileset dir="${dir.flex.config}" includes="*.xml" prefix="WEB-INF/flex" />
<fileset dir="${dir.build}" includes="*.swf" />
</war>
</target>

</project>


build.properties

dir.build=build
dir.classes=${dir.build}/classes
dir.flex.config=WEB-INF/flex
dir.flex.src=web
dir.src.java=src
dir.web-inf=WEB-INF

file.web.xml=web.xml

dir.blazeds=C:\\blazeds-expanded
dir.blazeds.lib=${dir.blazeds}\\WEB-INF\\lib

dir.j2ee.home=C:\\apache-tomcat-6.0.16\\lib
lib.servlet=${dir.j2ee.home}/servlet-api.jar

flex.context.root=dustin-blazeds
flex.debug=true
flex.description="Example of using HTTPService with BlazeDS"
flex.publisher=Dustin
flex.services=C:\\NetBeansProjects\\JavaBlazeDS\\WEB-INF\\flex\\services-config.xml
flex.title="BlazeDS with HTTPService Example"

war.dustin-blazeds-example=dustin-blazeds.war


One vital observation to make from the Ant script above is the importance of passing two mxmlc compiler options to the mxmlc application compiler when working with BlazeDS. The -context-root and -services application compiler options are significant and required. These are also documented in the feedback section of the blog entry BlazeDS: Open Sourcing Remoting and Messaging.

12. Deploy the WAR that contains the Flex client and the BlazeDS JARs and configuration XML. Run the example by going to the appropriate Tomcat URL.

When this last step is followed, the simple Flex client shown above will look something like that shown in the next two screen snapshots.

Results from Using HTTPService without BlazeDS


Results from Using HTTPService with BlazeDS


The simple Flex client shown in these snapshots demonstrates using HTTPService to contact that same servlet both with and without BlazeDS. There are advantages associated with BlazeDS that may justify its significant additional effort. I discussed these in the previously mentioned BlazeDS RPC blog entry. They include the ability to perform authentication, the ability to log on the server-side, and the removal of the requirement to employ a crossdomain.xml file.

In this blog entry, I showed how the BlazeDS-provided samples could be modified to one's own examples to run HTTPService with and without BlazeDS. A developer could then add back in certain portions of the configuration files to add in security, logging, etc.

There are several useful BlazeDS references that provide significantly greater detail than that provided here. I referred to some of them above as well. These resources include the following:

* BlazeDS Developer Guide - Referenced multiple times in this blog entry.

* BlazeDS 30-Minute Test Drive - Useful for seeing BlazeDS in action by using samples directly with turnkey version of BlazeDS. Adds explanation to what the sample code and configuration is actually doing.

* BlazeDS Installation Guide

* BlazeDS for Java-Flex Communication

* BlazeDS: Open Sourcing Remoting and Messaging

* Simplified BlazeDS and JMS

Monday, April 21, 2008

A First Look at BlazeDS: RPC Mechanisms

Many of us were excited to hear about Adobe's releasing of BlazeDS in late 2007. However, there seemed to be a relative lack of documentation on how to use BlazeDS as compared to other Flex approaches. However, since that announcement, there have been several good resources explaining BlazeDS. These include Andrew Powell's blog entry BlazeDS in Plain English, Wade Arnold's blog entry BlazeDS: Worth Figuring Out! Hello World Example, Christophe Coenraets's article BlazeDS 30-Minute Test Drive, BlazeDS Developer's Guide, BlazeDS Public APIs, BlazeDS Features list, BlazeDS: Remoting != Messaging and the BlazeDS and LCDS Feature Difference table.

HTTPService: With and Without BlazeDS

BlazeDS supports HTTPService. This can be a little confusing at first because HTTPService has been available in the free Flex SDK since Flex 2 and before BlazeDS was made available. In fact, it is the same HTTPService in both cases. Without BlazeDS, a Flex developer is limited to using HTTPService with its useProxy attribute set to false and the connection is made directly to the HTTP service specified by the url attribute.

So, why is it useful to be able to set HTTPService.useProxy=true to take advantage of BlazeDS's proxy server support for HTTPService? The advantage is that BlazeDS's support for the Flex proxy server allows for use of HTTP methods other than POST and GET (including DELETE and TRACE). This is more REST-friendly and is described in the Flex 3 Language Reference documentation for HTTPService.

Another advantage of using BlazeDS in conjunction with HTTPService is that doing so removes the need to use a crossdomain.xml file when accessing a remote server from a Flex application. Besides removing the need for a crossdomain.xml file, the use of BlazeDS server proxy also enables more granular control over access of a server-side service. This means that useful additions like service authentication and service access logging can be performed.

WebService: With and Without BlazeDS

Like HTTPService, the Flex WebService is supported both with and without BlazeDS. The differences between using WebService without BlazeDS as compared to with BlazeDS are similar to those for HTTPService. Instead of the url attribute (used by HTTPService), a WebService used without BlazeDS will use the value of the wsdl attribute as the location of the server-side SOAP-based web service to invoke and, as with HTTPService, the useProxy attribute must be set to false when BlazeDS (or other Flex server implementation) is not used.

As with HTTPService, there are advantages to using WebService in conjunction with a BlazeDS-provided Flex server. These advantages are strikingly similar to those for HTTPService with BlazeDS: no need for a crossdomain.xml when remotely accessing a web service on a remote machine and greater ability to log access or perform authentication checks on potential web service access.

RemoteObject: Only Available with BlazeDS

The previously mentioned two RPC approaches (HTTPService and WebService) can be used with or without BlazeDS. BlazeDS supports a third RPC mechanism, RemoteObject, that is not available in standard Flex without BlazeDS (or other Flex server proxy product).

Besides being only available with BlazeDS, another difference with RemoteObject (as compared to HTTPService and WebService) is the encoding used with RemoteObject. While HTTPService and WebService both make use of XML/text over HTTP, RemoteObject uses Action Message Format (AMF) encoding. Also, RemoteObject allows Flex developers to write ActionScript code as if they were accessing server-side Java classes directly rather than handling XML as is often the case with HTTPService and WebService.

The three RPC mechanisms supported by BlazeDS are highly useful and each has its own advantages. I hope to devote some future blog entries to more in-depth details on each of these three RPC mechanisms and to demonstrate them with code examples. I also plan to include future blog entries on the Flex-to-server communication support offered by BlazeDS outside of the RPC mechanisms (Messaging mechanisms).

Friday, December 14, 2007

BlazeDS: More Free Flex to Java EE Integration Options

Up until now, I have primarily used Flex's HTTPService and WebService to communicate between my Flex clients and the Java EE server-side business logic. With Adobe's recent announcement of the new free (as in "free beer") BlazeDS, there are more and highly productive techniques available at no charge for communicating between a Flex client and the Java EE server. Adobe has also announced plans to move portions of BlazeDS to open source (as in "free speech"). In other words, some highly useful Java EE to Flex communication mechanisms that used to have a license cost and were not open source (Flex Data Services or Adobe LiveCycle Data Services ES) will now be, at least in part, both freely available and open source.

The Adobe announcement and BlazeDS web page, along with seemingly countless blogs, talk about the basics of BlazeDS. Therefore, instead of rehashing the features of BlazeDS in detail, I'll focus in this blog entry on listing some useful resources for additional information on BlazeDS.

As part of this announcement, Adobe also announced release of the Action Message Format (AMF) specification. Besides greater development ease, other advantages of the new approaches available to developers for Java EE-to-Flex communication include potentially significantly greater performance with binary formats compared to the text/XML approaches used inherently with HTTPService and WebService. That being said, there are advantages to the HTTPService and WebService such as extremely loose coupling and high degree of standards orientation.

Adobe is releasing BlazeDS under the LPGL (version 3). There is a beta version available now and the open source version is anticipated to be available in early 2008. I was curious about Adobe's reasoning for employing the GNU Lesser General Public License (LGPL) for BlazeDS when they have already announced Flex's open source release under the Mozilla Public License (MPL), but the BlazeDS FAQ addresses this question and states that BlazeDS may be used outside of Flex and thus had different open source licensing needs than those of Flex. I find this only slightly concerning because the FAQ recommends (more than once) that lawyers (or "legal experts") be consulted if distributing BlazeDS. However, the benefits of using open source BlazeDS may be worth it to some organizations to justify the legal review and advice. Besides, it is probably best to conduct such reviews anyway with all licenses. That being said, I have really come to appreciate licenses like the Apache Foundation's Apache License Version 2 software license. They just seem easier to understand and adhere to.

Speaking of the LGPL, there are some interesting sites with more information on this. The Free Software Foundation provides an overview of licenses and, related to the GNU project, includes an article Why You Shouldn't Use the Lesser GPL for Your Next Library. Note that this last article is oriented to software library developers rather than software library users. My understanding is that the LGPL has significantly weaker copyleft protections than the ordinary GPL and that organizations are typically allowed to include LGPL-licensed libraries in proprietary products. However, I'm not an attorney and all these licenses muddy the situation and make things confusing. Popular libraries using the LGPL license include JFreeChart, JBoss Application Server (and most of its related projects), and Hibernate. This doesn't seem like too bad of company for BlazeDS. JBoss's justification for LGPL is interesting reading as well.

The BlazeDS FAQ addresses what constitutes remoting and web messaging. Remoting allows Flex developers to treat server-side Java objects as if they were local to Flex objects. Because it uses the binary AMF format, it will often be significantly faster than using text/XML. As the Web in Web Messaging implies, this form of communication occurs over HTTP. The thing that appeals most to me about Web Remoting is that Java Message Service (JMS) can be used as part of this.

It is important to note that BlazeDS does not have all the features of the LifeCycle product.

Here are some useful resources for additional details on BlazeDS: