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

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.

1 2 3 4 5 Page 1