The Source for Java Technology Collaboration
User: Password:



Ryan Heaton's Blog

January 2008 Archives


Web Service Programming for the Masses, Part II: Developing the RIA

Posted by stoicflame on January 09, 2008 at 11:41 AM | Permalink | Comments (0)

The first part of this tutorial walked through the development of a Web service API that exposes its endpoints via SOAP, REST/XML, JSON, GWT-RPC, and AMF. In this second part of the tutorial, we intend to prove out this API. We will walk through building a rich AJAX application with an embedded Flash component. The AJAX application will use GWT-RPC to access the API while the Flash movie will use AMF.

For those of you who would like to see the example code on your local filesystem, you can check it out of SVN at http://svn.codehaus.org/enunciate/trunk/enunciate/src/samples/addressbook/.

Step 1: Set up the GWT application.

We first have to create our GWT application. We will use the name “net.java.apps.addressbook.AddressBookApp” to identify our application to Enunciate and GWT.

Before we continue, we have to add a project dependency on the main GWT client-side library. The added dependency declaration will go in the “dependencies” section of our pom.xml file:


<dependency>
  <groupId>com.google.gwt</groupId>
  <artifactId>gwt-user</artifactId>
  <version>1.4.60</version>
</dependency>

Then, we create our main GWT application structure. For more information, see the GWT documentation:

addressbook/src/main/gwt/net/java/apps/addressbook/AddressBookApp.gwt.xml:

<module>

  <!--inherit from the main GWT client-side library.-->
  <inherits name='com.google.gwt.user.User'/>

  <!--inherit from our Enunciate-generated GWT-RPC client-side code.-->
  <inherits name='net.java.ws.addressbook.AddressBookGWT'/>

  <!--define the entry point for the address book app.-->
  <entry-point class='net.java.apps.addressbook.client.AddressBookApp'/>

</module>
addressbook/src/main/gwt/net/java/apps/addressbook/public/addressbook.html:

<html>
<head>
  <title>AddressBook Application</title>
  <meta name='gwt:module' content='net.java.apps.addressbook.AddressBookApp'>
</head>
<body>
  <script language="javascript" src="gwt.js"></script>
  <iframe id="__gwt_historyFrame" style="width:0;height:0;border:0"></iframe>
  <h1>AddressBook Application</h1>
</body>
</html>
addressbook/src/main/gwt/net/java/apps/addressbook/client/AddressBookApp.java:

package net.java.apps.addressbook.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.*;

/**
 * Basic entry point for the GWT address book application.
 *
 * @author Ryan Heaton
 */
public class AddressBookApp implements EntryPoint {
  public void onModuleLoad() {
  }
}

Finally, we edit the Enunciate configuration file to point to our application so it will be compiled during the normal compile process.

addressbook/enunciate.xml:

<enunciate>
  <modules>
    <gwt disabled="false" gwtHome="/path/to/gwt/home" rpcModuleName="net.java.ws.addressbook.AddressBookGWT">
      <app srcDir="src/main/gwt">
        <module name="net.java.apps.addressbook.AddressBookApp"/>
      </app>
    </gwt>
    <amf disabled="false" flexHome="/path/to/flex/sdk/home"/>
  </modules>
</enunciate>

Step 2: Set up the Flex Application

Having created the skeleton for our AJAX application using GWT, we can now set up the skeleton for developing our Flex application. This takes only a single MXML file and editing the Enunciate configuration file to point to our Flex source directory. For more information, see Adobe's documentation on developing a Flex application.

addressbook/src/main/flex/net/java/apps/addressbook/bytype.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" backgroundColor="#FFFFFF">
</mx:Application>
addressbook/enunciate.xml:

<enunciate>
  <modules>
    <gwt disabled="false" gwtHome="/path/to/gwt/home" rpcModuleName="net.java.ws.addressbook.AddressBookGWT">
      <app srcDir="src/main/gwt">
        <module name="net.java.apps.addressbook.AddressBookApp"/>
      </app>
    </gwt>
    <amf disabled="false" flexHome="/path/to/flex/sdk/home">
      <app srcDir="src/main/flex" name="bytype" mainMxmlFile="src/main/flex/net/java/apps/addressbook/bytype.mxml"/>
    </amf>
  </modules>
</enunciate>

*At this point, it might be convenient to rebuild our project file using Maven. The new project will contain references to the new generated code and to our GWT application.

Step 3: Write the Flex Application

We can now write our Flash component that will lookup contacts by type. Our application has only three components: a DataGrid to display the information, a ComboBox used to select the contact type, and a Button that will invoke the find and populate the DataGrid with the data. The Script tag is where we put our logic to invoke the AMF Web service method:

addressbook/src/main/flex/net/java/apps/addressbook/bytype.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" backgroundColor="#FFFFFF" creationComplete="initApp()">
  <mx:Script><![CDATA[
     import mx.controls.Alert;
     import mx.rpc.events.FaultEvent;
     import mx.rpc.events.ResultEvent;
     import mx.rpc.remoting.RemoteObject;
     import net.java.ws.addressbook.services.AddressBook;
     import net.java.ws.addressbook.services.AddressBook.FindContactsByTypeResultEvent;
     import net.java.ws.addressbook.domain.ContactType;
     import net.java.ws.addressbook.domain.ContactList;
     import net.java.ws.addressbook.domain.Contact;

     //instantiate our address book service.
     var book:AddressBook = new AddressBook();

     //array of contact types; used to popluate the combo box.
     [Bindable]
     public var contactTypes: Array = [ ContactType.friend, ContactType.family, ContactType.professional ];

     private function initApp():void {
       //initialize our address book with our event listeners.
        book.addEventListener("fault", myFaultHandler);
        book.addEventListener("findContactsByType", myEventHandler);
     }

     private function myFaultHandler(event:FaultEvent):void {
       //if a fault occurs, display a fault message.
       Alert.show(event.fault.message);
     }

     private function myEventHandler(event:FindContactsByTypeResultEvent):void {
       //the event listener is a strongly-typed listener for the findContactsByType method.
       dg.dataProvider = event.result.contacts;
     }

  ]]></mx:Script>

  <mx:DataGrid id="dg" width="100%" height="100%"/>

  <mx:ComboBox id="ct" dataProvider="{contactTypes}"/>

  <mx:Button label="Find By Type" click="book.findContactsByType(ct.selectedItem as String)"/>

</mx:Application>

You'll notice that the remote interface that we're working with in our Flex application is strongly typed (inasmuch as Flex allows it). This is because Enunciate generates the client-side ActionScript classes that are used to access the AMF endpoints (these can be found in addressbook/target/enunciate-generate/amf/client). This is a big advantage over using an ActionScript RemoteObject directly since you get compile-time type safety.

Step 4: Write the GWT Application

Our GWT Application will be a tab-based UI, with two tabs. The first tab will be a lookup of contacts by name via suggestion box. The second tab will display our Flash component that looks up contacts by type. Because we are using GWT, we have the advantage of writing the AJAX UI in Java. We'll let Enunciate handle the process of invoking GWT to compile our Java source code into JavaScript. Since this isn't a tutorial on GWT, we'll let the code do the talking.

addressbook/src/main/gwt/net/java/apps/addressbook/client/AddressBookApp.java:

package net.java.apps.addressbook.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.*;
import net.java.ws.addressbook.client.services.AddressBook;
import net.java.ws.addressbook.client.domain.ContactList;
import net.java.ws.addressbook.client.domain.Contact;

import java.util.Iterator;
import java.util.Collection;
import java.util.ArrayList;

/**
 * Basic entry point for the GWT address book application.
 *
 * @author Ryan Heaton
 */
public class AddressBookApp implements EntryPoint {
  public void onModuleLoad() {
    //the panel that will hold the widgets that will look up by name.
    VerticalPanel byNamePanel = new VerticalPanel();

    //the Enunciate-generated client-side GWT address book service
    final AddressBook book = new AddressBook();

    //the grid we will use to display the contacts that are found.
    final Grid contactGrid = new Grid();

    //the oracle that will make a remote call to populate the suggest box.
    SuggestOracle oracle = new SuggestOracle() {
      public void requestSuggestions(final Request request, final Callback callback) {
        String query = request.getQuery();

        //call the method to find contacts by name.
        book.findContactsByName(query, new AddressBook.FindContactsByNameResponseCallback() {
          public void onResponse(ContactList response) {
            Iterator contactIt = response.getContacts().iterator();
            Collection suggestions = new ArrayList();
            while (contactIt.hasNext()) {
              final Contact contact = (Contact) contactIt.next();

              //add the suggestion.
              suggestions.add(new Suggestion() {
                public String getDisplayString() {
                  return contact.getName();
                }

                public String getReplacementString() {
                  return contact.getName();
                }
              });
            }

            contactGrid.clear(); //clear the grid.
            callback.onSuggestionsReady(request, new Response(suggestions));
          }

          public void onError(Throwable throwable) {
            //do nothing if an error occurs while asking for suggestions
          }
        });
      }
    };

    //the suggest box (instantiated with our oracle).
    final SuggestBox suggestBox = new SuggestBox(oracle);

    //The panel that will hold the suggest box and the "find" button.
    HorizontalPanel findForm = new HorizontalPanel();
    findForm.add(suggestBox);

    //the "find" button.
    Button findButton = new Button("find");
    findButton.addClickListener(new ClickListener() {
      public void onClick(Widget widget) {
        //when "find" is clicked, make the query and populate the grid.
        String text = suggestBox.getText();
        book.findContactsByName(text, new AddressBook.FindContactsByNameResponseCallback() {
          public void onResponse(ContactList response) {
            contactGrid.resize(6 * response.getContacts().size(), 2);
            Iterator contactIt = response.getContacts().iterator();
            int i = 0;
            while (contactIt.hasNext()) {
              Contact contact = (Contact) contactIt.next();
              contactGrid.setWidget(++i, 0, new Label("Name:"));
              contactGrid.setWidget(i, 1, new Label(contact.getName()));
              contactGrid.setWidget(++i, 0, new Label("Phone:"));
              contactGrid.setWidget(i, 1, new Label(contact.getPhone()));
              contactGrid.setWidget(++i, 0, new Label("Address:"));
              contactGrid.setWidget(i, 1, new Label(contact.getAddress1()));
              contactGrid.setWidget(++i, 0, new Label("City:"));
              contactGrid.setWidget(i, 1, new Label(contact.getCity()));
              contactGrid.setWidget(++i, 0, new Label("Type:"));
              contactGrid.setWidget(i, 1, new Label(contact.getContactType()));
              contactGrid.setWidget(++i, 0, new HTML("<hr/>"));
              contactGrid.setWidget(i, 1, new HTML("<hr/>"));
            }
          }

          public void onError(Throwable throwable) {
            //if an error while doing a "find," display it in the grid.
            contactGrid.resize(1, 1);
            contactGrid.setWidget(0, 0, new Label("ERROR: " + throwable.getMessage()));
          }
        });
      }
    });

    //add the find button.
    findForm.add(findButton);

    //add the find form to the panel.
    byNamePanel.add(findForm);

    //add the display grid to the panel.
    byNamePanel.add(contactGrid);

    //create the tab panel.
    TabPanel panel = new TabPanel();

    //add the find by name panel to the tab panel.
    panel.add(byNamePanel, "&nbsp;<a href=\"#byname\">by name</a>&nbsp;", true);

    //create some HTML that will embed our flash component.
    HTML flashHTML = new HTML("<object width=\"550\" height=\"400\">\n" +
              "<param name=\"movie\" value=\"bytype.swf\">\n" +
              "<embed src=\"bytype.swf\" width=\"550\" height=\"400\">\n" +
              "</embed>\n" +
              "</object>");

    //add the flash component to the other tab in the tab panel.
    panel.add(flashHTML, "&nbsp;<a href=\"#bytype\">by type</a>&nbsp;", true);

    //add the tab panel to the root HTML.
    RootPanel.get().add(panel);
  }
}

You'll notice that Enunciate has also generated client-side code for accessing our Web service API via GWT. Again, this adds significant benefits in terms of type safety and simplifies the creation of the initial environment.

Step 5: Build, Package, and Run the Application

Once again, we run

mvn jetty:run-war

Assuming everything builds correctly, we can navigate to http://localhost:8080/addressbook/addressbook.html and see our Rich AJAX Application with an embedded Flash component, both of which make dynamic calls to our Web service API.



Web Service Programming for the Masses, Part I: Developing the Web Service API

Posted by stoicflame on January 09, 2008 at 11:33 AM | Permalink | Comments (5)

So you've got a web application that needs to expose a Web service API. Maybe you're going to be writing a Rich Internet Application (RIA) using Flex or some AJAX Framework. Or maybe you'll be developing a desktop application that needs to interface with your online data. Or perhaps you've got a few business partners that want to integrate with your application.

Whatever the reason, you need a solution that not only fits your client-side requirements, but one that is also easy to develop, easy to debug, easy to maintain, and easy to enhance. Don't be too hasty with your choice of a Web service implementation technology. REST is not always the best answer. It might be, but not always. Take some time to research your needs, gather your requirements, and look at other remoting technologies that are available. Then decide what you need.

This is the first part of a tutorial will walk you through developing a Web service API that could meet the requirements of all of the above-mentioned use cases. For the sake of clarity and brevity, we'll keep the operations simple, but by the time we're done, we'll have a fully-functional Web service API that exposes a bouquet of endpoints: SOAP, REST/XML, JSON, GWT-RPC, and even AMF (for all you Flex developers). In Part II of this tutorial, we'll prove out the Web service API by building a rich AJAX application with an embedded Flash component that accesses it.

By the end of this two-part tutorial, we'll have established a good custom-fit Web service development process for maintaining your Web service API in your favorite development environment.

For those of you who would like to see the example code on your local filesystem, you can check it out of SVN at http://svn.codehaus.org/enunciate/trunk/enunciate/src/samples/addressbook/.

Step 1: Create Your Project

We will be exposing a Web service API for an address book application. We will be using Maven 2 and Enunciate 1.6 to build and package our application. (Note that we could also use Ant to build the application, but Maven will get us started more quickly.)

We'll label our application “addressbook” and assign it a group id “net.java.ws.addressbook”. Assuming Maven is already installed, we can use its archetype plugin to bootstrap the project from the command line:

mvn archetype:create -DgroupId=net.java.ws.addressbook -DartifactId=addressbook

This will create a simple project for us with an empty Java source file and an associated test. You can open up the project in your favorite IDE; Maven has plugins available that will create project files for whichever one you like to work with.

Before our project is fully usable, we need to tweak it to declare our dependencies, configure the compile settings, and add any additional build tools. As for our dependencies, we need only one additional dependency on Enunciate's runtime libraries (groupId: org.codehaus.enunciate, artifactId:enunciate-rt). We also need to declare a Maven plugin for Enunciate to build the Web service application (with the “assemble” goal). For deployment, we can use the Maven Jetty plugin to deploy and run the application. Finally, we need to configure Maven's compiler plugin to compile for JDK 1.5 compatability and make sure we're using “war” packaging since we're building a web application. In the end, our project descriptor looks like this:

addressbook/pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>net.java.ws.addressbook</groupId>
  <artifactId>addressbook</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>addressbook</name>
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.enunciate</groupId>
        <artifactId>maven-enunciate-plugin</artifactId>
        <version>1.6</version>
        <executions>
          <execution>
            <goals>
              <goal>assemble</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
  <dependencies>
    <dependency>
      <groupId>org.codehaus.enunciate</groupId>
      <artifactId>enunciate-rt</artifactId>
      <version>1.6</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Step 2: Create Your Domain

The domain of an address book application is very simple. It consists of a basic Contact object. The Contact has fields for an id, name, phone number, two address fields, and city. We'll also add a contact type enumeration object ("friend", "family", or "professional") and create an object representing a contact list for use when we want to retrieve multiple contacts from a Web service request. You'll also note that we've annotated Contact and ContactList with a JAXB annotation, @javax.xml.bind.annotation.XmlRootElement, which will allow us to return these types as XML elements from a REST request (see below).

addressbook/src/main/java/net/java/ws/addressbook/domain/Contact.java:

package net.java.ws.addressbook.domain;

import javax.xml.bind.annotation.XmlRootElement;

/**
 * A contact in the address book.
 *
 * @author Ryan Heaton
 */
@XmlRootElement
public class Contact {

  private int id;
  private String name;
  private String phone;
  private String address1;
  private String address2;
  private String city;
  private ContactType contactType;

  /**
   * The id of the contact.
   *
   * @return The id of the contact.
   */
  public int getId() {
    return id;
  }

  /**
   * The id of the contact.
   *
   * @param id The id of the contact.
   */
  public void setId(int id) {
    this.id = id;
  }

  /**
   * The name of the contact.
   *
   * @return The name of the contact.
   */
  public String getName() {
    return name;
  }

  /**
   * The name of the contact.
   *
   * @param name The name of the contact.
   */
  public void setName(String name) {
    this.name = name;
  }

  /**
   * The phone of the contact.
   *
   * @return The phone of the contact.
   */
  public String getPhone() {
    return phone;
  }

  /**
   * TThe phone of the contact.
   *
   * @param phone The phone of the contact.
   */
  public void setPhone(String phone) {
    this.phone = phone;
  }

  /**
   * The first address field of the contact.
   *
   * @return The first address field of the contact.
   */
  public String getAddress1() {
    return address1;
  }

  /**
   * The first address field of the contact.
   *
   * @param address1 The first address field of the contact.
   */
  public void setAddress1(String address1) {
    this.address1 = address1;
  }

  /**
   * The second address field of the contact.
   *
   * @return The second address field of the contact.
   */
  public String getAddress2() {
    return address2;
  }

  /**
   * The second address field of the contact.
   *
   * @param address2 The second address field of the contact.
   */
  public void setAddress2(String address2) {
    this.address2 = address2;
  }

  /**
   * The city of the contact.
   *
   * @return The city of the contact.
   */
  public String getCity() {
    return city;
  }

  /**
   * The city of the contact.
   *
   * @param city The city of the contact.
   */
  public void setCity(String city) {
    this.city = city;
  }

  /**
   * The contact type.
   *
   * @return The contact type.
   */
  public ContactType getContactType() {
    return contactType;
  }

  /**
   * The contact type.
   *
   * @param contactType The contact type.
   */
  public void setContactType(ContactType contactType) {
    this.contactType = contactType;
  }
}
addressbook/src/main/java/net/java/ws/addressbook/domain/ContactType.java:

package net.java.ws.addressbook.domain;

/**
 * A contact type.
 *
 * @author Ryan Heaton
 */
public enum ContactType {

  friend,

  family,

  professional

}
addressbook/src/main/java/net/java/ws/addressbook/domain/ContactList.java:

package net.java.ws.addressbook.domain;

import javax.xml.bind.annotation.XmlRootElement;
import java.util.Collection;

/**
 * A list of contacts.
 *
 * @author Ryan Heaton
 */
@XmlRootElement
public class ContactList {

  private Collection contacts;

  /**
   * The contact list.
   *
   * @return The contact list.
   */
  public Collection getContacts() {
    return contacts;
  }

  /**
   * The contact list.
   *
   * @param contacts The contact list.
   */
  public void setContacts(Collection contacts) {
    this.contacts = contacts;
  }
}

Step 3: Create Your Services

The next step involves creating the services that will comprise our Web service API. We'll create an AddressBook interface that defines three operations: getContact, findContactsByName, and findContactsByType, each of them throwing an AddressBookException in case something goes wrong.

Furthermore, we have to identify our interface as a Web service. We do this by applying the JAX-WS annotation @javax.jws.WebService to the interface. As it turns out, this service interface also lends itself nicely to a mapping to a REST endpoint. We can decide, for example, to map the getContact method to an HTTP GET that will take the id parameter from the URL in the form of a context parameter. To perform the mapping to a resource-oriented interface, we use Enunciate's proprietary REST annotations (@RESTEndpoint, @Noun, @Verb, @ProperNoun, etc.)

addressbook/src/main/java/net/java/ws/addressbook/services/AddressBook.java:

package net.java.ws.addressbook.services;

import net.java.ws.addressbook.domain.ContactList;
import net.java.ws.addressbook.domain.Contact;
import net.java.ws.addressbook.domain.ContactType;

import javax.jws.WebService;

import org.codehaus.enunciate.rest.annotations.*;

/**
 * An address book that provides access to a set of contacts.
 *
 * @author Ryan Heaton
 */
@WebService
@RESTEndpoint
public interface AddressBook {

  /**
   * Get a contact by id.
   *
   * @param id The id of the contact.
   * @return The contact.
   * @throws AddressBookException If the contact wasn't found.
   */
  @Noun (
    "contact"
  )
  @Verb (
    VerbType.read
  )
  Contact getContact(@ProperNoun Integer id) throws AddressBookException;

  /**
   * Find contacts by name.
   *
   * @param name The name to search for.
   * @return The contacts that were found.
   */
  ContactList findContactsByName(String name) throws AddressBookException;

  /**
   * Find contats by type.
   *
   * @param type The type of contact.
   * @return The contacts.
   */
  ContactList findContactsByType(ContactType type) throws AddressBookException;
}
addressbook/src/main/java/net/java/ws/addressbook/services/AddressBookException.java:

package net.java.ws.addressbook.services;

/**
 * @author Ryan Heaton
 */
public class AddressBookException extends Exception {

  public AddressBookException(String message) {
    super(message);
  }
}

Of course, we have to create an implementation of the AddressBook. This is what will query our database and perform the business logic of our application. For the purposes of this tutorial, we'll just create a simple in-memory store of contacts.

Our implementation must also reference its Web service interface in order to be identified as the implementation for the Web service. We again use the JAX-WS-specified @javax.jws.WebService and the Enunciate-specific @RESTEndpoint annotation to accomplish this.

addressbook/src/main/java/net/java/ws/addressbook/impl/AddressBookImpl.java:

package net.java.ws.addressbook.impl;

import net.java.ws.addressbook.services.AddressBook;
import net.java.ws.addressbook.services.AddressBookException;
import net.java.ws.addressbook.domain.Contact;
import net.java.ws.addressbook.domain.ContactList;
import net.java.ws.addressbook.domain.ContactType;

import java.util.*;

import org.codehaus.enunciate.rest.annotations.RESTEndpoint;

import javax.jws.WebService;

/**
 * @author Ryan Heaton
 */
@WebService (
  endpointInterface = "net.java.ws.addressbook.services.AddressBook"
)
@RESTEndpoint
public class AddressBookImpl implements AddressBook {

  private static final Map STORE = loadContacts();

  public Contact getContact(Integer id) throws AddressBookException {
    Contact contact = STORE.get(id);
    if (contact == null) {
      throw new AddressBookException("contact not found: " + id);
    }
    return contact;
  }

  public ContactList findContactsByName(String name) throws AddressBookException {
    ArrayList contacts = new ArrayList();
    if (name != null) {
      for (Contact contact : STORE.values()) {
        if (contact.getName().toLowerCase().contains(name.toLowerCase())) {
          contacts.add(contact);
        }
      }
    }
    ContactList list = new ContactList();
    list.setContacts(contacts);
    return list;
  }

  public ContactList findContactsByType(ContactType type) throws AddressBookException {
    ArrayList contacts = new ArrayList();
    for (Contact contact : STORE.values()) {
      if (contact.getContactType().equals(type)) {
        contacts.add(contact);
      }
    }
    ContactList list = new ContactList();
    list.setContacts(contacts);
    return list;
  }

  private static Map loadContacts() {
    final int size = 20;
    Random random = new Random();
    String[] firstNames = new String[]{"Sally", "George", "Harold", "Tammy", "Robert", "Daniel", "Jane", "Mike", "David", "John"};
    String[] lastNames = new String[]{"Beach", "Jobs", "Gates", "Bush", "Clinton", "Gore", "Moore", "Jones", "Adams", "Washington", "Smith"};
    String[] addresses = new String[]{"1 First Street", "2 Second Street", "3 Third Street"};
    String[] cities = new String[]{"Long Beach, CA", "New York, NY", "Orlando, FL", "Honolulu, HI", "Oklahoma City, OK"};
    String[] phoneNumbers = new String[]{"111-1111", "222-2222", "333-3333", "444-4444", "555-5555", "666-6666", "777-7777"};

    HashMap contacts = new HashMap();
    for (int i = 0; i < size; i++) {
      Contact contact = new Contact();
      contact.setId(i);
      contact.setName(firstNames[random.nextInt(firstNames.length)] + " " + lastNames[random.nextInt(lastNames.length)]);
      contact.setAddress1(addresses[random.nextInt(addresses.length)]);
      contact.setCity(cities[random.nextInt(cities.length)]);
      contact.setPhone(phoneNumbers[random.nextInt(phoneNumbers.length)]);
      contact.setContactType(ContactType.values()[random.nextInt(ContactType.values().length)]);
      contacts.put(i, contact);
    }

    return Collections.unmodifiableMap(contacts);
  }
}

Step 4: Enable and Configure GWT and AMF (Flex Data Services) Endpoints.

Enunciate will publish your Web service API as GWT-RPC endpoints and AMF (Flex Data Services) endpoints if you enable this option in the Enunciate configuration file. The configuration for the GWT module requires a path to the installed Google Web Toolkit and a name for the client module that will be generated (see the GWT documentation for details). The configuration for the AMF module requires a path to the installed Flex SDK.

addressbook/enunciate.xml:

<enunciate>
  <modules>
    <gwt disabled="false" gwtHome="/path/to/gwt/home" rpcModuleName="net.java.ws.addressbook.AddressBookGWT"/>
    <amf disabled="false" flexHome="/path/to/flex/sdk/home"/>
  </modules>
</enunciate>

Step 5: Build, Package, and Run the Application

Since we've got everything set up and all our endpoints configured, we can now use Maven to invoke Enunciate, build, package, and even deploy our application to Jetty by running the command:

mvn jetty:run-war

Assuming everything builds as expected, Jetty should be running your Web service API. Things to note:

  1. You can see the documentation for your Web service API by browsing to http://localhost:8080/addressbook/index.html. You'll notice that Enunciate generated full documentation for your Web service API scraped from your JavaDocs. You've got your SOAP endpoints and REST endpoints documented along with the documentation for the data types.
  2. Each of the endpoints are up and running and servicing requests. Your browser doesn't know how to format requests for the SOAP endpoints, GWT endpoints, or the AMF endpoints, but you can see that the endpoints are at least responding to invalid requests by browsing to http://localhost:8080/addressbook/soap/AddressBookService, http://localhost:8080/addressbook/gwt/AddressBookService, and http://localhost:8080/addressbook/amf/AddressBookService.
  3. Your browser does, however, know how to format a REST request. You can see the XML for a contact by making a request to http://localhost:8080/addressbook/rest/contact/4, for example. Or, to see the same contact in JSON format, browse to http://localhost:8080/addressbook/json/contact/4.
  4. Enunciate automatically generates client-side code that can be used to access your SOAP endpoints, GWT endpoints, and AMF endpoints (ActionScript classes). By default, the client-side libraries that can access your SOAP endpoints are provided as downloads from your Web service API. To see these downloads, navigate to http://localhost:8080/addressbook/downloads.html. You can also configure Enunciate to make the client-side GWT code and ActionScript classes available for download from the download page. They are not available as a download by default because they are subject to cross-site scripting restrictions and are usually for internal consumption (see Part II).


Enunciate 1.6: AMF Endpoints for Flex Developers

Posted by stoicflame on January 09, 2008 at 11:13 AM | Permalink | Comments (2)

If you haven't checked out the Enunciate project recently, you may want to give it another look to see how it can improve the development, deployment, and consumption of your Web service API.

Enunciate 1.6 was just released with support for mounting your JAX-WS endpoint classes via AMF (thanks to Adobe's release of BlazeDS). This should be really interesting to Flex developers. Not only will you be able to make Web service calls via AMF, but Enunciate will also generate type-safe client-side ActionScript classes that will access those endpoints.

Enunciate will also publish your endpoints via SOAP, REST/XML, JSON, and GWT. You'll also get your WSDLs, schemas, and full user-level documentation of your Web service API.

I'll be shortly posting a two-part tutorial on how to publish this kind of a rich API with your service endpoints and how to write an AJAX app with an embedded Flash component that consumes it.





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds