Java EE and Flex, Part 2: Can we talk?

how-to
Feb 5, 200937 mins

Communicating between Flex clients and Java EE servers

There are a variety of ways a Flex client can communicate with your back-end Java server, so it’s good to explore your options. In this follow up to his introduction to rich-client development with Flex and Java EE, Dustin Marx shows you how to connect a Flex client and Java enterprise back-end via SOAP-based Web services and object remoting. He also introduces BlazeDS and demonstrates its support for proxied HTTPService and WebService connections and object remoting.

The first part of this two-article series covers some of the advantages that Flex offers Java developers who want to deliver rich-client experiences in the Web browser. That article introduces key pieces of Flex such as MXML (Flex’s XML grammar), ActionScript 3, Cascading Style Sheets (CSS) syntax support, some of Flex’s built-in components, and creation of custom Flex components that extend the built-in components. It concludes by introducing Flex’s out-of-the-box HTTPService, which enables a Flex client to communicate with a Java EE server via HTTP.

In this article I’ll introduce you to more advanced ways to comfortably integrate Flex clients into enterprise Java applications. You’ll learn about more out-of-the-box communication options, namely Flex’s WebService and RemoteObject classes. I’ll also discuss BlazeDS, an open source web messaging and Java remoting solution from Adobe that complements Flex.

Using Flex with Web services

The HTTPService is not the only mechanism that Flex provides for out-of-the-box communication with a remote server. Flex also supports communication with a SOAP-based Web service, via the WebService object.

Preliminary setup

You need the Flex SDK to build the examples in this article. If you don’t already have it, follow the download and installation instructions in “Java EE and Flex: A compelling combination, Part 1.” You can build all the examples with the command-line SDK and tools, but some readers may prefer to use Adobe’s Flex Builder.

To illustrate Flex communication with SOAP-based Web services, I need a Web Services Description Language (WSDL) file. For the example in this article, I’ll take advantage of Java EE 5’s ability to generate WSDL files from classes annotated with the appropriate Java API for XML-Based Web Services (JAX-WS) annotations. (In situations where top-down Web service design is preferred, a preexisting WSDL file can be used directly.) Listing 1 shows the annotated Java class, which will also provide the server-side functionality invoked by the Web service call.

Listing 1. AuthorServer.java: Web service implementation

package javaworld.dustin.endpoints;

import java.util.HashMap;
import java.util.Map;
import javaworld.dustin.common.Author;
import javax.jws.WebService;
import javax.jws.WebMethod;

/**
 * Simple class to be exposed as a Web service endpoint to illustrate using
 * Flex with Web services. This particular examples also happens to return
 * information about the requested JavaWorld author.
 * 
 * @author Dustin
 */
@WebService(name="AuthorServer",
            serviceName="AuthorService",
            targetNamespace="http://www.javaworld.com/articles/authors/dustin"
)
public class AuthorServer
{
   /** Authors information. */
   private static final Map<String, Author> authors =
      new HashMap<String, Author>();

   static
   {
      final Author marx = Author.newInstance(
         "Marx", "Dustin",
         "http://marxsoftware.blogspot.com/",
         "Author of 'JSP Best Practices' and 'More JSP Best Practices'");
      authors.put(marx.getFullName(), marx);

      final Author holub = Author.newInstance(
         "Holub", "Allen",
         "http://www.holub.com",
         "Author of 'Why Getter and Setter Methods are Evil' and 'Why Extends is Evil'");
      authors.put(holub.getFullName(), holub);

      final Author venners = Author.newInstance(
         "Venners", "Bill",
         "http://www.artima.com/consulting.html",
         "Author of 'Designing with Exceptions' and 'Inheritance Versus Composition'");
      authors.put(venners.getFullName(), venners);

      final Author hunter = Author.newInstance(
         "Hunter", "Jason",
         "http://www.servlets.com/jason/",
         "Author of 'New Features Added to Servlet 2.5'");
      authors.put(hunter.getFullName(), hunter);

      final Author sletten = Author.newInstance(
         "Sletten", "Brian",
         "http://www.nofluffjuststuff.com/conference/speaker/brian_sletten.html",
         "Author of 'REST for Java Developers, Part 1'");
      authors.put(sletten.getFullName(), sletten);
   }

   /**
    * Provide the biography of the author whose last name and first name are
    * provided.
    * 
    * @param authorLastName Last name of author for which biography information
    *    is sought.
    * @param authorFirstName First name of author for which biography information
    *    is sought.
    * @return Biography information for specified author.
    */
   @WebMethod
   public String obtainAuthorBiography(
      final String authorLastName, final String authorFirstName)
   {
      return authors.get(
         Author.buildFullName(authorFirstName, authorLastName)).getBiography();
   }

   /**
    * Provide the biography of the author whose name is provided.
    * 
    * @param authorFullName Full name of author for which biography is desired.
    * @return Biography information for specified author.
    */
   @WebMethod
   public String obtainAuthorBiographyWithFullName(final String authorFullName)
   {
      return authors.get(authorFullName).getBiography();
   }
}

The AuthorServer class in Listing 1 contains internal data intended only to demonstrate Flex calling Java on the back end. In a real application, this data would likely be retrieved from the database or other single data source.

Next I’ll use GlassFish to generate a WSDL file from the JAX-WS-annotated AuthorServer class. Note that you don’t need to generate the WSDL as a separate step. The act of deploying the JAX-WS annotated class leads GlassFish to generate the WSDL automatically with the appropriate URL. The generated WSDL file looks something like what’s shown in Listing 2.

Listing 2. WSDL file generated from AuthorServer.java

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.2_01-hudson-189-. -->
<definitions targetNamespace="http://www.javaworld.com/articles/authors/dustin" name="AuthorService" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:tns="http://www.javaworld.com/articles/authors/dustin" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <types>
    <xsd:schema>
      <xsd:import namespace="http://www.javaworld.com/articles/authors/dustin" schemaLocation="AuthorService_schema1.xsd"/>
    </xsd:schema>
  </types>
  <message name="obtainAuthorBiography">
    <part name="parameters" element="tns:obtainAuthorBiography"/>
  </message>
  <message name="obtainAuthorBiographyResponse">
    <part name="parameters" element="tns:obtainAuthorBiographyResponse"/>
  </message>
  <message name="obtainAuthorBiographyWithFullName">
    <part name="parameters" element="tns:obtainAuthorBiographyWithFullName"/>
  </message>
  <message name="obtainAuthorBiographyWithFullNameResponse">
    <part name="parameters" element="tns:obtainAuthorBiographyWithFullNameResponse"/>
  </message>
  <portType name="AuthorServer">
    <operation name="obtainAuthorBiography">
      <input message="tns:obtainAuthorBiography"/>
      <output message="tns:obtainAuthorBiographyResponse"/>
    </operation>
    <operation name="obtainAuthorBiographyWithFullName">
      <input message="tns:obtainAuthorBiographyWithFullName"/>
      <output message="tns:obtainAuthorBiographyWithFullNameResponse"/>
    </operation>
  </portType>
  <binding name="AuthorServerPortBinding" type="tns:AuthorServer">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <operation name="obtainAuthorBiography">
      <soap:operation soapAction=""/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
    <operation name="obtainAuthorBiographyWithFullName">
      <soap:operation soapAction=""/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
  </binding>
  <service name="AuthorService">
    <port name="AuthorServerPort" binding="tns:AuthorServerPortBinding">
      <soap:address location="REPLACE_WITH_ACTUAL_URL"/>
    </port>
  </service>
</definitions>

When the application is deployed on GlassFish, the REPLACE_WITH_ACTUAL_URL in Listing 2 will be replaced by the URL on the GlassFish-hosted server: http://localhost:8080/jw-jee-flex/AuthorService. This URL also implies that the context name of the deployment is jw-jee-flex. When the Web service has been appropriately deployed, we can check its WSDL using the appropriate URL: http://localhost:8080/jw-jee-flex/AuthorService?WSDL in this case.

With the Web service deployed, we can focus on the Flex client that will invoke the obtainAuthorBiographyWithFullName(String) method. As with most Flex classes, you can use the WebService class in MXML, in ActionScript, or in a combination of both. In this example I’ll declare WebService in MXML but use the WebService instance in its ActionScript form to invoke a Web service and handle the Web service response via ActionScript code. Listing 3 shows the MXML used to declare the WebService.

Listing 3. WebService applied in MXML

<mx:WebService id="authorBiographyService"
               wsdl="http://localhost:8080/jw-jee-flex/AuthorService?WSDL"
               load="authorBioTextArea.visible=true;"
            useProxy="false">
   <mx:operation name="obtainAuthorBiographyWithFullName"
                 result="displayAuthorBiography(event);"
                 fault="faultHandler(event);"/>
</mx:WebService>

As Listing 3 shows, the WebService MXML declaration is similar to that used in the HTTPService example covered in Part 1. However, the WebService uses a wsdl attribute rather than a url attribute. Another difference from HTTPService is the nested operation element in WebService. The useProxy attribute is explicitly set to false in Listing 3, but this is unnecessary because false is its default value. The text area identified as authorBioTextArea (where author biography information will be displayed) will not be visible until the Flex application has successfully loaded the Web service’s WSDL.

The operation element nested within the WebService element includes a name attribute that references the name of the operation defined in the associated WSDL. The result handler and fault handler are also specified via attributes on the operation element. These handlers reference methods defined in ActionScript elsewhere in the Flex application. The faultHandler(FaultEvent) is the same function used in the HTTPService example in “Java EE and Flex: A compelling combination, Part 1.”

With the WebService connection information specified, we can now invoke that Web service. This occurs when the user clicks on any row in the DataGrid showing JavaWorld article information. When a row in the DataGrid is clicked on (selected), the author’s full name is sent to the obtainAuthorBiographyWithFullName operation. This is implemented by adding an itemClick attribute to the DataGrid. The itemClick event handler is specified to invoke the ActionScript requestAuthorBio(event) function, shown in Listing 4.

Listing 4. Web service called in response to click event on DataGrid

public function requestAuthorBio(clickEvent:ListEvent):void
   {
     const authorName:String = dataGrid.selectedItem.author;
     authorBiographyService.obtainAuthorBiographyWithFullName.send(authorName);
   }

The ActionScript requestAuthorBio() function called when a row is selected on the author DataGrid retrieves the author’s name from that selected row and invokes the Web service with that full name as an argument. The authorBiographyService in the function in Listing 4 is the name of the WebService described by its id attribute. The obtainAuthorBiographyWithFullName portion is the operation defined in MXML as a nested element of the WebService element. The send method on that operation instructs Flex to make the actual Web service invocation.

As you can see in the MXML code in Listing 3, the WebService designates the displayAuthorBiography(ResultEvent) function as its result handler. Therefore, this function will be invoked when the Web service call completes. Listing 5 shows the definition of this ActionScript function.

Listing 5. Displaying author biography as result of Web service ResultEvent

/**
    * Display selected author's biography.
    *
    * @param event The event associated with a response to a Web service call.
    */
   public function displayAuthorBiography(event:ResultEvent):void
   {
      authorBioTextArea.text =
         authorBiographyService.obtainAuthorBiographyWithFullName.lastResult;
   }

The result-handling function simply accesses the last result from the Web service call, which should be the author’s biography that was returned from the Java class as a single String. This String is assigned to the text area using ActionScript’s property binding. This property binding is implemented by setting authorBioTextArea.text to the String value returned by the Web service call.

Now that the WebService example is complete, I’ll add it to the example demonstrated in the first half of this article. The Flex application at this point, with the HTTPService and WebService examples in place, appears in Listing 6.

Listing 6. Main Flex source file with HTTPService and WebService examples

<?xml version="1.0" encoding="UTF-8" ?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                xmlns:jw="components.*"
                width="1100" height="400"
                applicationComplete="articles.send();">
   <mx:Style>
   .articleTitle
   {
      font-weight: bold;
   }
   .gridLabel
   {
      font-weight: bold;
      font-size: 14;
   }
   </mx:Style>

   <mx:Script>
   import mx.controls.Alert;
   import mx.events.ListEvent;
   import mx.rpc.events.FaultEvent;
   import mx.rpc.events.ResultEvent;
   import mx.rpc.Fault;
   import mx.utils.ObjectUtil;

   public function requestAuthorBio(clickEvent:ListEvent):void
   {
      const authorName:String = dataGrid.selectedItem.author;
      authorBiographyService.obtainAuthorBiographyWithFullName.send(authorName);
   }

   public function faultHandler(event:FaultEvent):void
   {
      const fault:Fault = event.fault;
      const faultString:String =
           "FAULT CODE: " + fault.faultCode + "nn"
         + "FAULT DETAIL: " + fault.faultDetail + "nn"
         + "FAULT STRING: " + fault.faultString + "nn"
         + "FAULT CODE: " + fault.faultCode + "nn";
      Alert.show( "FAULT!nn" + faultString );
   }

   public function resultHandler(event:ResultEvent):void
   {
      trace("Response from service received.");
      // The ResultEvent includes a property "message" that contains
      //   the actual payload/contents of the result.  This is useful
      //   when no fault is occurring, but no data is being displayed
      //   in the component either.
   }

   /**
    * Display selected author's biography.
    *
    * @param event The event associated with a response to a Web service call.
    */
   public function displayAuthorBiography(event:ResultEvent):void
   {
      authorBioTextArea.text =
         authorBiographyService.obtainAuthorBiographyWithFullName.lastResult;
   }
   </mx:Script>

   <mx:HTTPService id="articles"
                   url="http://localhost:8080/jw-jee-flex/articles"
                   method="GET"
                   resultFormat="e4x"
                   fault="faultHandler(event);"
                   result="resultHandler(event);" />

   <mx:WebService id="authorBiographyService"
                  wsdl="http://localhost:8080/jw-jee-flex/AuthorService?WSDL"
        load="authorBioTextArea.visible=true;"
        useProxy="false">
      <mx:operation name="obtainAuthorBiographyWithFullName"
                    result="displayAuthorBiography(event);"
                    fault="faultHandler(event);"/>

   </mx:WebService>

   <mx:Panel id="mainPanel"
             title="Some of Dustin's Favorite JavaWorld Articles"
             width="{application.width-50}" height="{application.height-50}">
      <mx:Label text="A Small Sample of Favorite JavaWorld Articles"
                styleName="gridLabel" />
      <mx:DataGrid id="dataGrid"
                   width="{mainPanel.width*0.9}"
                   rowCount="5" dataProvider="{articles.lastResult.article}"
                   editable="false"
         itemClick="requestAuthorBio(event);">
            <mx:columns>
                <mx:DataGridColumn id="titleColumn"
                                   dataField="title"
                                   headerText="Article Title"
                                   width="{dataGrid.width*0.25}"
                                   editable="false" />
                <mx:DataGridColumn id="authorColumn"
                                   dataField="author"
                                   headerText="Article Author"
                                   width="{dataGrid.width*0.25}"
                                   editable="false" />
                <mx:DataGridColumn id="urlColumn"
                                   dataField="url"
                                   headerText="Article URL"
                                   editable="true">
                  <mx:itemRenderer>
                     <mx:Component>
                        <jw:UrlLabel />
                     </mx:Component>
                  </mx:itemRenderer>
                </mx:DataGridColumn>
            </mx:columns>
      </mx:DataGrid>
      <mx:TextArea id="authorBioTextArea" visible="false"
                   width="{dataGrid.width}" height="100" />
   </mx:Panel>
</mx:Application>

Recall that Part 1’s HTTPService example required a crossdomain.xml file in the server’s root directory to allow a remote Flex client in a different domain to access the servlet hosted on GlassFish. The WebService example requires that another entry be made to this crossdomain.xml file to allow for SOAP headers to be handled correctly. Listing 7 shows the crossdomain.xml file that contains both elements (for HTTPService and for WebService).

Listing 7. crossdomain.xml file for HTTPService and WebService examples

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">

<cross-domain-policy>
   <allow-access-from domain="*" />
   <allow-http-request-headers-from domain="*" headers=" SOAPAction"/>
</cross-domain-policy>

Figure 1 shows the final output of this application after an author has been selected.

Figure 1. Flex application displaying result of Web service invocation (Click to enlarge.)

In Figure 1, the text in the bottom box demonstrates that the Web service has been invoked successfully.

Communicating via BlazeDS

The Flex SDK provides both the HTTPService and WebService approaches I’ve illustrated so far. In both cases, we either implicitly (taking advantage of default settings) or explicitly (setting the useProxy attribute to false) used HTTPService and WebService without server proxies. This meant that Flex communicated directly with the exposed Java servlet or exposed Web service. BlazeDS provides additional mechanisms Flex can use to communicate with a server, beyond the non-proxied HTTPService and non-proxied WebService. These include support for a proxied HTTPService, support for a proxied WebService, object remoting support, and support for messaging via Java Message Service (JMS).

One advantage of using BlazeDS is that the presence of the proxy service eliminates the need for a crossdomain.xml file on the server. Another advantage of using HTTPService in conjunction with BlazeDS is that you can use HTTP methods beyond GET and POST (the only two supported in HTTPService without the proxy service). Also, a proxy service provides access to the Flex request to perform operations such as logging or authentication. Finally, BlazeDS allows you to transmit binary formats over HTTP for potential performance improvements.

Obtaining and setting up BlazeDS

You can download BlazeDS from Adobe. It is licensed with the open source GNU Lesser General Public License, Version 3. Three types of downloads are available: the source code, a binary version, and a turnkey download that comes with Tomcat bundled for one-stop shopping. In this article, we’ll use the binary download so that it can be used with any Java EE-compliant Web server. I use GlassFish for my examples, but you could use Tomcat or any other server.

Unzip the downloaded BlazeDS ZIP file and place its contents in a directory with a name such as C:blazeds-bin-3.2.0.3978. The contents of the ZIP file for the non-turnkey binary version consist of an HTML README file and a blazeds.war file. The easiest approach to begin working with BlazeDS from this point is to open the contents of the blazeDS.war file, modify the expanded contents to fit the application’s needs, and reassemble the WAR with the modified files. This WAR can then be deployed on any enterprise Java Web server.

Figure 2 shows what the WEB-INF directory looks like when I have expanded the blazeds.war so that I can edit the appropriate files. The web.xml shown in Figure 2 is one of the files we will be editing.

Figure 2. Contents of WEB-INF directory when blazeds.war is expanded (Click to enlarge.)

The other files that need to be edited are in the flex subdirectory, whose contents appear in Figure 3.

Contents of WEB-INF/flex when blazeds.war is expanded
Figure 3. Contents of WEB-INF/flex when blazeds.war is expanded (Click to enlarge.)

The four XML files shown in Figure 3 are used for configuration of BlazeDS. All BlazeDS-based server solutions use the services-config.xml file. To use proxied HTTPService or proxied WebService with BlazeDS, you use the proxy-config.xml file in addition to the services-config.xml file. To use BlazeDS’s remoting capabilities, you use the messaging-config.xml file in conjunction with services-config.xml. Finally, you use remoting-config.xml with services-config.xml for object remoting with BlazeDS.

HTTPService with and without BlazeDS

Listing 8 shows the HTTPService used in the example in “JavaEE and Flex: A compelling combination, Part 1” (with the context adjusted here to be jw-jee-flex).

Listing 8. Adapted version of HTTPService

<mx:HTTPService id="articles"
                url="http://localhost:8080/jw-jee-flex/articles"
                method="GET"
                resultFormat="e4x"
                fault="faultHandler(event);"
                result="resultHandler(event);" />

In that example, a URL was specified for the HTTPService to use to connect to the server. With a BlazeDS server proxy, this needs to be changed to use a destination rather than a URL. The destination is specified in the HTTPService and so is defined in a BlazeDS configuration file on the server side. We also need to set the useProxy attribute explicitly to true when using HTTPService with BlazeDS.

To take advantage of BlazeDS’s proxy-related benefits, the WebService example from earlier in this article needs only slight modifications similar to those implemented for HTTPService. The destination attribute is used instead of the wsdl attribute, and the useProxy attribute is set to true.

BlazeDS configuration in detail

The HTTPService used directly against a Java servlet required only the configuration in the MXML file and the web.xml Web descriptor file, as I described Part 1. Similarly, the WebService example in this article did not need any specific configuration on the server other than what one would normally need for deploying a Web service. For BlazeDS, additional configuration is necessary. This extra configuration effort is often justified by the benefits obtained from using a proxy service.

With the contents of the blazeds.war file expanded as shown in Figures 2 and 3, it’s easy to modify the configuration files as needed. The first file to modify is services-config.xml, used for specifying general BlazeDS configuration. It includes references to files with more granular configuration information. The services-config.xml file initially references three other configuration files: remoting-config.xml, messaging-config.xml, and proxy-config.xml. Because we often need only one of these (proxy, remoting, or messaging), we can remove or comment out the inclusion of the other two referenced files. To make the examples simpler, I’ll also remove all the portions of the services-config.xml file that are commented out. In practice, one would often uncomment the necessary configuration information and change the values appropriately.

The web.xml file provided with the BlazeDS download is based on the Servlet 2.3 specification as shown by its reference to the Servlet 2.3 Document Type Definition (DTD). I prefer to use Servlet 2.5, so I often change the provided web.xml file to reference the 2.5 XML Schema .xsd definition file. I also typically remove the commented-out portion intended for WebSphere use.

Because the changes to the Flex MXML application necessary for HTTPService and WebService to use BlazeDS are minor, and because I’ve already explained these changes, I won’t show that code here. (See Resources for a link to an entry in my blog that shows use of HTTPService with BlazeDS.) The next example uses BlazeDS-provided object remoting to illustrate another way to implement remote procedure call (RPC) communication between a Flex client and Java on the server.

Object remoting with BlazeDS

An advantage of using HTTPService or WebService is the loose coupling they enable by allowing XML to be used as the message format and by allowing for Flex clients to connect to any viable Web service or HTTP connection. However, in some situations loose coupling is less valuable than the ability to work directly with ActionScript objects. The object-remoting capability BlazeDS provides lets Flex developers work with ActionScript objects in the Flex code that are bound to Java objects on the server side. You can interact with these ActionScript objects as you would with any ActionScript object, but the actions on these ActionScript objects result in a behavior occurring on the server side with the bound Java object.

I’ll now add object remoting to the Flex application we’ve built so far. To do so, I copy the expanded contents of the blazeds.war file into my project’s directory structure so that I can edit them directly. Specifically, I copy the WEB-INF contents of the expanded blazeds.war (except for web.xml) into the WEB-INF of my application directory structure. They will be reassembled into the WAR that is already built for the HTTPService and WebService examples. Rather than copying the web.xml file provided by blazeds.war over the web.xml file written for the HTTPService example, the necessary contents of the blazeds.war web.xml file will be copied into the application’s web.xml file. Listing 9 shows the consolidated web.xml file, which includes both the servlet that the HTTPService example talks to and the BlazeDS-inspired entries.

Listing 9. Web descriptor with HTTP servlet and BlazeDS specified

<?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>JavaWorld Flex/Java EE Part 2 Article</display-name>
   <description>Server-side functionality for JavaWorld Flex/Java EE Part 2 Article</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>
      <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>
      <servlet-name>ArticleServer</servlet-name>
      <servlet-class>javaworld.dustin.servlets.ArticleServer</servlet-class>
   </servlet>

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

   <servlet-mapping>
      <servlet-name>ArticleServer</servlet-name>
      <url-pattern>/articles</url-pattern>
   </servlet-mapping>
   
   <session-config>
      <session-timeout>35</session-timeout>
   </session-config>
   <mime-mapping>
      <extension>html</extension>
      <mime-type>text/html</mime-type>
   </mime-mapping>
   <mime-mapping>
      <extension>txt</extension>
      <mime-type>text/plain</mime-type>
   </mime-mapping>
</web-app>

It is important to make sure that the expanded WEB-INF contents from the blazeds.war file get reassembled back into the example application’s WAR file, because BlazeDS JAR files will be needed. We also need to ensure that the services-config.xml and remoting-config.xml files that we edit for our application get assembled into our WAR file.

Before moving on to editing the BlazeDS configuration files for the object remoting, it is appropriate to create the Java objects that will be remotely accessed. Listing 10 shows the Java class (TopArticlesServer) that the Flex client will interact with.

Listing 10. TopArticlesServer.java: Java class to be remotely accessed by Flex

package javaworld.dustin.remoting;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import static javaworld.dustin.remoting.TopArticleConstants.*;

/**
 * Simple Java class to be used in Flex/Java remoting example. Represents the
 * top articles on JavaWorld.
 * 
 * @author Dustin
 */
public class TopArticlesServer
{
   /** Map of year+rank key to article corresponding to that year and rank. */
   private static final Map<String, TopArticle> topArticles =
      new HashMap<String, TopArticle>();

   static
   {
      final TopArticle article200801 =
         new TopArticle(
            "Hello, OSGi, Part 1: Bundles for Beginners",
            "Sunil Patil",
            2008,
            "http://www.javaworld.com/javaworld/jw-03-2008/jw-03-osgi1.html",
            1);
      topArticles.put("2008_01", article200801);

      final TopArticle article200802 =
         new TopArticle(
            "Eclipse 3.3 or NetBeans 6.0?",
            "Andrew Binstock",
            2008,
            "http://www.javaworld.com/javaworld/jw-03-2008/jw-03-java-ides0308.html",
            2);
      topArticles.put("2008_02", article200802);

      final TopArticle article200803 =
         new TopArticle(
            "Is Tomcat Enterprise Ready?",
            "Jeff Hanson",
            2008,
            "http://www.javaworld.com/javaworld/jw-01-2008/jw-01-tomcat6.html",
            3);
      topArticles.put("2008_03", article200803);

      final TopArticle article200804 =
         new TopArticle(
            "iBATIS, Hibernate, and JPA: Which is Right for You?",
            "K. L. Nitin et al",
            2008,
            "http://www.javaworld.com/javaworld/jw-07-2008/jw-07-orm-comparison.html",
            4);
      topArticles.put("2008_04", article200804);

      final TopArticle article200805 =
         new TopArticle(
            "Merging and Branching in Subversion 1.5",
            "John Ferguson Smart",
            2008,
            "http://www.javaworld.com/javaworld/jw-01-2008/jw-01-svnmerging.html",
            5);
      topArticles.put("2008_05", article200805);

      final TopArticle article200806 =
         new TopArticle(
            "Understanding JPA, Part 1",
            "Aditi Das",
            2008,
            "http://www.javaworld.com/javaworld/jw-01-2008/jw-01-jpa1.html",
            6);
      topArticles.put("2008_06", article200806);

      final TopArticle article200807 =
         new TopArticle(
           "Four Harmful Java Idioms, and How to Fix Them",
           "John O'Hanley",
           2008,
           "http://www.javaworld.com/javaworld/jw-07-2008/jw-07-harmful-idioms.html",
           7);
      topArticles.put("2008_07", article200807);

      final TopArticle article200808 =
         new TopArticle(
            "Asynchronous HTTP Comet Architectures",
            "Gregor Roth",
            2008,
            "http://www.javaworld.com/javaworld/jw-03-2008/jw-03-asynchhttp-test.html",
            8);
      topArticles.put("2008_08", article200808);

      final TopArticle article200809 =
         new TopArticle(
            "Introduction to Hibernate Search",
            "Dr. Xinyu Liu",
            2008,
            "http://www.javaworld.com/javaworld/jw-07-2008/jw-07-hibernate-search.html",
            9);
      topArticles.put("2008_09", article200809);

      final TopArticle article200810 =
         new TopArticle(
            "Open Source Java Projects: Java Native Access",
            "Jeff Friesen",
            2008,
            "http://www.javaworld.com/javaworld/jw-02-2008/jw-02-opensourcejava-jna.html",
            10);
      topArticles.put("2008_10", article200810);
   }

   /** No-arguments constructor necessary for object binding. */
   public TopArticlesServer() {}

   /**
    * Provide the article corresponding to the provided year and rank desired.
    * 
    * @param year Publication year of article.
    * @param rank Rank of desired article.
    * @return Article matching provided year and rank; the article will have a
    *    rank of zero (0) if no actual matching article was found.
    * @throws IllegalArgumentException Thrown if the publication year or rank
    *    are not valid.
    */
   public TopArticle getRankedArticle(final int year, final int rank)
   {
System.err.println("getRankedArticle() received ranking of " + rank);
      TopArticleUtil.validateArticleRankAndYearInput(year, rank);
      final String extraDigit = rank < 10 ? "0" : "";
      final String key =
         String.valueOf(year) + "_" + extraDigit + String.valueOf(rank);
      TopArticle topArticle = topArticles.get(key);
      if (topArticle == null)
      {
         final Calendar today = Calendar.getInstance();
         topArticle =
            new TopArticle(
               DEFAULT_TITLE + " (No Match!)",
               DEFAULT_AUTHOR,
               DEFAULT_YEAR,
               DEFAULT_URL,
               NO_MATCHING_ARTICLE_RANK);
      }
      return topArticle;
   }
}

The getRankedArticle(int,int) method in TopArticlesServer returns an instance of TopArticle. Listing 11 shows the definition of TopArticle.

Listing 11. TopArticle.java: Encapsulates data to be returned to the Flex client

package javaworld.dustin.remoting;

import java.io.Serializable;

/**
 * Class representing a popular article published on JavaWorld.
 * 
 * @author Dustin
 */
public final class TopArticle implements Serializable
{
   /** Title of article. */
   private String title;

   /** Name of article's author. */
   private String author;

   /** Year in which this article was published. */
   private int yearPublished;

   /** Article's URL. */
   private String url;

   /** Article's rank in terms of page hits in publication year. */
   private int rank;

   /** No-arguments constructor. */
   public TopArticle()
   {
      title = TopArticleConstants.DEFAULT_TITLE;
      author = TopArticleConstants.DEFAULT_AUTHOR;
      yearPublished = TopArticleConstants.DEFAULT_YEAR;
      url = TopArticleConstants.DEFAULT_URL;
      rank = TopArticleConstants.NO_MATCHING_ARTICLE_RANK;
   }

   /**
    * Constructor accepting arguments to populate my state.
    * 
    * @param newTitle Title of the article being instantiated.
    * @param newAuthor Author of the article being instantiated.
    * @param newYearPublished Publication year of this article; should be a four
    *    digit year.
    * @param newUrl URL/URI pointing to this article.
    * @param newRank Rank of this article in terms of page hits in publication
    *    year; should be between 1 and 10.
    * @throws IllegalArgumentException Thrown if the year or rank do not fall
    *    into their respective allowable ranges.
    */
   public TopArticle(
      final String newTitle,
      final String newAuthor,
      final int newYearPublished,
      final String newUrl,
      final int newRank)
   {
      TopArticleUtil.validateArticleRankAndYearInput(newYearPublished, newRank);

      this.title = newTitle;
      this.author = newAuthor;
      this.yearPublished = newYearPublished;
      this.url = newUrl;
      this.rank = newRank;
   }

   /**
    * Provide the article's author's full name.
    * 
    * @return Full name of the article's author.
    */
   public String getAuthor()
   {
      return this.author;
   }

   /**
    * Set/change the author of this article.
    * 
    * @param newAuthor New full name of this article's author.
    */
   public void setAuthor(final String newAuthor)
   {
      this.author = newAuthor;
   }

   /**
    * Provide the title of the article.
    * 
    * @return Article title.
    */
   public String getTitle()
   {
      return this.title;
   }

   /**
    * Set/change the title of this article.
    * 
    * @param newTitle New title for this article.
    */
   public void setTitle(final String newTitle)
   {
      this.title = newTitle;
   }

   /**
    * Provide the URL of this article.
    * 
    * @return Article's URL.
    */
   public String getUrl()
   {
      return this.url;
   }

   /**
    * Set/change the URL/URI pointing to this article.
    * 
    * @param url Uniform Resource Locator (URL) or URI pointing to this article.
    */
   public void setUrl(final String url)
   {
      this.url = url;
   }

   /**
    * Provide the year this article was published.
    * 
    * @return Year in which this article was published.
    */
   public int getYearPublished()
   {
      return this.yearPublished;
   }

   /**
    * Set/change the year this article was published.
    * 
    * @param yearPublished Year that this article was published.
    */
   public void setYearPublished(final int yearPublished)
   {
      this.yearPublished = yearPublished;
   }

   /**
    * Provide the rank of this article in its year of publication.
    * 
    * @return Rank of this article in its year of publication.
    */
   public int getRank()
   {
      return this.rank;
   }

   /**
    * Set/change the ranking that this article received in its publication year.
    * 
    * @param newRank Ranking for this article in its publication year.
    */
   public void setRank(final int newRank)
   {
      this.rank = newRank;
   }

   /**
    * Provide String representation of this article.
    * 
    * @return Article's string representation.
    */
   @Override
   public String toString()
   {
      final StringBuffer buffer = new StringBuffer();
      buffer.append("Title: ").append(this.title).append("; ");
      buffer.append("URL: ").append(this.url).append("; ");
      buffer.append("Author: ").append(this.author).append("; ");
      buffer.append("Year Published: ").append(this.yearPublished).append("; ");
      buffer.append("Rank: ").append(this.rank).append("; ");
      return buffer.toString();
   }
}

In Listing 11, the “get” and “set” methods, and the constructor that does not accept arguments, are all necessary for BlazeDS to associate the ActionScript object with the Java object.

We need an ActionScript class, shown in Listing 12, to bind to the Java version of TopArticle that can be manipulated by the Flex client code. Notice that although the TopArticle Java class in Listing 11 has no obvious sign of any binding to the ActionScript class, the ActionScript class does have an explicit binding to the Java class.

Listing 12. ActionScript.as: ActionScript version of TopArticle

package javaworld.dustin
{
   [Bindable]
   [RemoteClass(alias="javaworld.dustin.remoting.TopArticle")]
   public class TopArticle
   {
      /** Title of Article. */
      private var _title:String;

      /** Author of Article. */
      private var _author:String;

      /** Article's URL. */
      private var _url:String;

      /** Year of publication. */
      private var _year:int;

      /** Rank of article in publication year. */
      private var _rank:int;

      /** No-arguments constructor. */
      public function TopArticle(
         newTitle:String = "None Specified",
         newAuthor:String = null,
         newYearPublished:int = 0,
         newUrl:String = null,
         newRank:int = 0)
      {
         _title = newTitle;
         _author = newAuthor;
         _year = newYearPublished;
         _url = newUrl;
         _rank = newRank;
      }

      /**
       * WRITE (set/change) title property.
       *
       * @param newTitle New title.
       */
      public function set title(newTitle:String):void
      {
         _title = newTitle;
      }

      /**
       * READ title property.
       *
       * @return My title.
       */
      public function get title():String
      {
         return _title;
      }

      /**
       * WRITE (set/change) author property.
       *
       * @param newAuthor Author of article..
       */
      public function set author(newAuthor:String):void
      {
         _author = newAuthor;
      }

      /**
       * READ author property.
       *
       * @return Author of article..
       */
      public function get author():String
      {
         return _author;
      }

      /**
       * WRITE (set/change) url property.
       *
       * @param newUrl New URL.
       */
      public function set url(newUrl:String):void
      {
         _url = newUrl;
      }

      /**
       * READ url property.
       *
       * @return My URL..
       */
      public function get url():String
      {
         return _url;
      }

      /**
       * WRITE (set/change) year property.
       *
       * @param newYear Publication year of article.
       */
      public function set yearPublished(newYear:int):void
      {
         _year = newYear;
      }

      /**
       * READ year property.
       *
       * @return Year of publication.
       */
      public function get yearPublished():int
      {
         return _year;
      }

      /**
       * WRITE (set/change) rank property.
       *
       * @param newRank Rank of article.
       */
      public function set rank(newRank:int):void
      {
         _rank = newRank;
      }

      /**
       * READ rank property.
       *
       * @return Rank of publication.
       */
      public function get rank():int
      {
         return _rank;
      }

      /**
       * Provide String representation of me.
       *
       * @return String representation of this article.
       */
      public function toString():String
      {
         return  "[TopArticle: Title: " + this.title + "; Author: " + this.author
               + "; URL: " + this.url + "; Year: " + this.yearPublished
               + "; Ranking: " + this.rank + "]";
      }
   }
}

Now we need to edit the remoting-config.xml file to indicate the use of the TopArticlesServer Java class. Listing 13 shows the edited version.

Listing 13. Modified remoting-config.xml BlazeDS remoting configuration file

<?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="TopArticlesDestination">  
      <properties>  
         <source>javaworld.dustin.remoting.TopArticlesServer</source>  
      </properties>  
   </destination> 

</service>

The remoting-config.xml file in Listing 13 demonstrates the assigning of a destination that the Flex client can call, and associating that destination with the Java TopArticlesServer class. This remoting-config.xml file is specified in the more general BlazeDS services-config.xml configuration file, shown in Listing 14.

Listing 14. Modified services-config.xml BlazeDS configuration file

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

    <services>
        <service-include file-path="remoting-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://localhost:8080/{context.root}/messagebroker/amf"
                 class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>

        <channel-definition id="my-http" class="mx.messaging.channels.HTTPChannel">
            <endpoint url="http://localhost:8080/{context.root}/messagebroker/http"
                 class="flex.messaging.endpoints.HTTPEndpoint"/>
        </channel-definition>

    </channels>

    <logging>
        <target class="flex.messaging.log.ConsoleTarget" level="Error">
            <properties>
                <prefix>[BlazeDS-JavaWorld] </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>

When built with the mxmlc compiler, a Flex client that will call a BlazeDS destination must specify the location of the services-config.xml file with the -services mxmlc compiler option. It must also specify the context root of the deployed server application with the -context-root mxmlc compiler option.

The final Flex application

Listing 15 shows the final MXML code for the example Flex client.

Listing 15. Final version of main MXML file

<?xml version="1.0" encoding="UTF-8" ?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                xmlns:jw="components.*"
                width="1100" height="400"
                applicationComplete="uponInitialization();">
   <mx:Style>
   .articleTitle
   {
      font-weight: bold;
   }
   .formLabel
   {
      font-weight: bold;
   }
   .gridLabel
   {
      font-weight: bold;
      font-size: 14;
   }
   </mx:Style>

   <mx:Script>
   <![CDATA[
   import flash.events.MouseEvent;
   import mx.collections.ArrayCollection;
   import mx.controls.Alert;
   import mx.core.ContainerCreationPolicy;
   import mx.events.ListEvent;
   import mx.rpc.events.FaultEvent;
   import mx.rpc.events.ResultEvent;
   import mx.rpc.Fault;
   import mx.utils.ObjectUtil;
   import javaworld.dustin.TopArticle;

   [Bindable]
   private var years:ArrayCollection = new ArrayCollection(
      [ {label:"2008", data:2008}, 
        {label:"2009", data:2009}, 
        {label:"2010", data:2010} ]);

   [Bindable]
   private var ranks:ArrayCollection = new ArrayCollection(
      [ {label:"1", data:1}, 
        {label:"2", data:2}, 
        {label:"3", data:3},
        {label:"4", data:4}, 
        {label:"5", data:5},
        {label:"6", data:6}, 
        {label:"7", data:7},
        {label:"8", data:8}, 
        {label:"9", data:9},
        {label:"10", data:10} ]);

   [Bindable]
   private var selectionsMade:Boolean = false;

   private const unselectedColor:int = 0x0000FF;

   public function uponInitialization():void
   {
      articles.send();
   }

   public function requestAuthorBio(clickEvent:ListEvent):void
   {
      const authorName:String = dataGrid.selectedItem.author;
      authorBiographyService.obtainAuthorBiographyWithFullName.send(authorName);
   }

   public function faultHandler(event:FaultEvent):void
   {
      const fault:Fault = event.fault;
      const faultString:String =
           "FAULT CODE: " + fault.faultCode + "nn"
         + "FAULT DETAIL: " + fault.faultDetail + "nn"
         + "FAULT STRING: " + fault.faultString + "nn"
         + "FAULT CODE: " + fault.faultCode + "nn";
      Alert.show( "FAULT!nn" + faultString );
      trace( "FAULT!nn" + faultString );
   }

   public function resultHandler(event:ResultEvent):void
   {
      trace("Response from service received.");
      // The ResultEvent includes a property "message" that contains
      //   the actual payload/contents of the result.  This is useful
      //   when no fault is occurring, but no data is being displayed
      //   in the component either.
   }

   /**
    * Display selected author's biography.
    *
    * @param event The event associated with a response to a Web service call.
    */
   public function displayAuthorBiography(event:ResultEvent):void
   {
      authorBioTextArea.text =
         authorBiographyService.obtainAuthorBiographyWithFullName.lastResult;
   }

   /**
    * Retrieve best articles.
    *
    * @param event Event associated with mouse click.
    */
   public function retrieveBestOf(event:MouseEvent):void
   {
      remoteObject.getRankedArticle(
         yearSelection.selectedItem.label, rankSelection.selectedItem.label); 
   }

   /**
    *
    */
   public function checkSelectionsMade(event:ListEvent):void
   {
      if (yearSelection.selectedIndex != -1)
      {
         yearItem.setStyle("color", "black");
      }
      if (rankSelection.selectedIndex != -1)
      {
         rankItem.setStyle("color", "black");
      }
      selectionsMade =   yearSelection.selectedIndex != -1
                      && rankSelection.selectedIndex != -1;
   }
   ]]>
   </mx:Script>

   <mx:HTTPService id="articles"
                   url="http://localhost:8080/jw-jee-flex/articles"
                   method="GET"
                   resultFormat="e4x"
                   fault="faultHandler(event);"
                   result="resultHandler(event);" />

   <mx:WebService id="authorBiographyService"
                  wsdl="http://localhost:8080/jw-jee-flex/AuthorService?WSDL"
        load="authorBioTextArea.visible=true;"
        useProxy="false">
      <mx:operation name="obtainAuthorBiographyWithFullName"
                    result="displayAuthorBiography(event);"
                    fault="faultHandler(event);" />
   </mx:WebService>

   <mx:RemoteObject id="remoteObject"
                    destination="TopArticlesDestination"
          showBusyCursor="true"
          fault="faultHandler(event);" /> 

   <mx:TabNavigator id="tabNavigator"
                    creationPolicy="{ContainerCreationPolicy.ALL}">
      <mx:Panel id="mainPanel"
                label="Select All-Time Articles"
                title="Some of Dustin's Favorite JavaWorld Articles"
                width="{application.width-100}" height="{application.height-150}">
         <mx:Label text="A Small Sample of Favorite JavaWorld Articles"
                   styleName="gridLabel" />
         <mx:DataGrid id="dataGrid"
                      width="{mainPanel.width*0.9}"
                      rowCount="5" dataProvider="{articles.lastResult.article}"
                      editable="false"
            itemClick="requestAuthorBio(event);">
               <mx:columns>
                   <mx:DataGridColumn id="titleColumn"
                                      dataField="title"
                                      headerText="Article Title"
                                      width="{dataGrid.width*0.25}"
                                      editable="false" />
                   <mx:DataGridColumn id="authorColumn"
                                      dataField="author"
                                      headerText="Article Author"
                                      width="{dataGrid.width*0.25}"
                                      editable="false" />
                   <mx:DataGridColumn id="urlColumn"
                                      dataField="url"
                                      headerText="Article URL"
                                      editable="true">
                     <mx:itemRenderer>
                        <mx:Component>
                           <jw:UrlLabel />
                        </mx:Component>
                     </mx:itemRenderer>
                   </mx:DataGridColumn>
               </mx:columns>
         </mx:DataGrid>
         <mx:TextArea id="authorBioTextArea" visible="false"
                      width="{dataGrid.width}" height="25" />
      </mx:Panel>

      <mx:Panel id="bestOfPanel" label="Best of (Yearly)"
                title="Best of JavaWorld Articles">
         <mx:Form id="bestOfForm">
       <mx:FormItem id="yearItem" label="Select Year"
                    fontWeight="bold" color="{unselectedColor}">
          <mx:ComboBox id="yearSelection" dataProvider="{years}"
                       prompt="Select Year of Publication"
             change="checkSelectionsMade(event);" />
       </mx:FormItem>
       <mx:FormItem id="rankItem" label="Select Rank"
                    fontWeight="bold" color="{unselectedColor}">
          <mx:ComboBox id="rankSelection" dataProvider="{ranks}"
                       prompt="Select Ranking of Publication"
             change="checkSelectionsMade(event);" />
       </mx:FormItem>
       <mx:FormItem id="titleItem" label="Article Title" fontWeight="bold"
          enabled="{selectionsMade}">
          <mx:Label id="titleLabel"
                    text="{remoteObject.getRankedArticle.lastResult.title}" />
       </mx:FormItem>
       <mx:FormItem id="authorItem" label="Author"
                    fontWeight="bold" enabled="{selectionsMade}">
          <mx:Label id="authorLabel"
                    text="{remoteObject.getRankedArticle.lastResult.author}" />
       </mx:FormItem>
       <mx:FormItem id="urlItem" label="URL"
                    fontWeight="bold" enabled="{selectionsMade}">
          <mx:Label id="urlLabel"
                    text="{remoteObject.getRankedArticle.lastResult.url}" />
       </mx:FormItem>
       <mx:Button id="submitButton" label="Retrieve Article"
                  click="retrieveBestOf(event);" enabled="{selectionsMade}" />
    </mx:Form>
      </mx:Panel>

   </mx:TabNavigator>
</mx:Application>

The source code in Listing 15 includes the object remoting and provides additional ActionScript examples. The portion of the Flex application that uses the object remoting to obtain information from the server regarding the best articles on JavaWorld gets its own tab, demonstrating the Flex TabNavigator component. This new version of the client application also demonstrates the ComboBox component.

The Flex application shows new concepts other than just object remoting. It also demonstrates more ActionScript logic and the need to include ActionScript logic within CDATA tags due to the presence of the “&&” (and operator), which can be confusing to an XML parser. The enabled attribute is also applied to several components in this part of the example. This attribute is initially set to false, disabling the component. The component’s enable attribute only becomes true when a certain condition occurs. In this case, the selection of a year and of a rank lead to enabling of the components.

Executing the example

Figures 4 through 9 show the Flex application being executed.

Figure 4 demonstrates the Flex client loading up. The data to populate the DataGrid with article information is retrieved via HTTPService, as you saw in “Java EE and Flex: A compelling combination, Part 1.”

Figure 4. Flex client populated by HTTPService calls (Click to enlarge.)

Figure 5 demonstrates that the WebService call works and provides the biography information for the author of the selected article.

Figure 5. Web service returning biography information about selected article author (Click to enlarge.)

Figure 6 demonstrates the tab that is focused on the object remoting to provide information about the most popular articles on JavaWorld in 2008. The button and the fields that will display retrieved data are disabled until the desired ranking and year of publication are selected.

Figure 6. TabNavigator, Form, ComboBox, and Button components demonstrated (Click to enlarge.)

Figure 7 demonstrates an active ComboBox component used to provide publication years that could be selected. (Of course, only 2008 is supported on the back end, but multiple years make the ComboBox demonstration more interesting.)

Figure 7. Flex ComboBox in action (Click to enlarge.)

Figure 8 demonstrates the ComboBox again, this time in conjunction with selection of a desired article ranking. Once selected, the ComboBox‘s text color changes from blue to black.

Figure 8. Selected ComboBox color has changed (Click to enlarge.)

With the publication year and ranking selected, the user can click the Retrieve Article button, shown in Figure 9. When this button is clicked, the selected year and rank are submitted to the server via object remoting, and the matching article information provided by the Java-based back end is displayed.

Figure 9. Article results obtained via remoting and displayed

I’m using the Retrieve Article button here to demonstrate another Flex component. However, it’s common in Flex applications not to use a button at all, but instead have the client automatically request the information as soon as all required fields have been selected.

There is much more to Flex

Flex offers you an opportunity to create rich interactive clients that make the most of your Java enterprise back end. The Flex client example covered in the two parts of this article has demonstrated Flex communication with a Java EE-based server using standard HTTP (HTTPService), using SOAP-based Web services (WebService), and using object remoting. The client example also demonstrates several aspects of Flex, including some of its components, its powerful property binding and data binding, its ability to be extended with custom components, and its support for CSS.

There’s much more to learn about Flex. BlazeDS also supports messaging, and GraniteDS (an open source third-party product) offers other remote communication mechanisms. Flex’s other attractive features include fluid animation and other effects, audio and video support, skinning, accessibility features, and much more. To see some of these features, try out the examples at the Flex Showcase.

Dustin Marx is a principal software engineer and architect at Raytheon Company. His previous published work for JavaWorld includes “More JSP best practices” (July 2003) and “JSP Best Practices” (November 2001).