Enterprise Mashup Services

In my previous article, "Enterprise Mashup Services: Real-World SOA or Web 2.0 Novelties?" (JDJ Vol. 11, Issue 12), I discussed how a Java-to-AJAX library such as Direct Web Remoting (DWR) can bridge the gap between mashup services implemented with JavaScript and business services written in Java, allowing developers to blend corporate services with external services such as Google Maps. The problem with this approach is that it relies on AJAX as an integration point, which entails a fragile development platform as well as the need to maintain browser-specific code due to idiosyncrasies in browser support for JavaScript - the primary technology behind AJAX. In addition, JavaScript lacks a standardized approach for componentizing code, making applications written in it difficult to consolidate and reuse. The solution to these shortcomings is to pair AJAX with a component framework. JavaServer Faces (JSF) provides this foundation and eliminates the complexities of JavaScript - besides providing rich integration with the Java EE platform.

A mashup component is a custom JSF component that encapsulates the code that operates on a mashup API. Once created, the mashup component eliminates the need to work with JavaScript. Thus the code is both simplified and easily reused, making the API accessible to both JavaScript experts and less-experienced developers. The intent of this article is to build on the concepts introduced in the previous article and present the tools to create enterprise-ready components that encapsulate mashup services.

Rich Components Versus Mashup Components
On the surface, JSF components that abstract mashup APIs appear no different than those that encapsulate AJAX functionality. In fact, the concepts that define both components are the same; it's the philosophy behind the component definitions that differs. AJAX-enriched components abstract the complexities of JavaScript and provide interactive visual effects. JSF components that encapsulate mashup services are created with the same intent; however, mashup components package both visual effects and interactions with services. So mashup components represent Service Oriented Architectures (SOAs) at a micro level within a larger composite application. The example used in this article is a component that blends Google Maps with the Yahoo! Geocoding API. The crux of this solution is the tying of JavaScript events to event handlers implemented with Java code. This marriage lets mashup services work in conjunction with those implemented on the Java EE platform, which in the example are isolated to services provided by the JSF-managed bean facility.

Shale Remoting
The Shale Framework, which can be found on the Apache Web site (http://shale.apache.org/), provides a rich Web development framework that extends JSF. Instead of going into the numerous features that Shale provides, I'll focus on a single aspect, Shale Remoting, which maps a server-side resource such as a static JavaScript file or a method associated with a managed bean to a URL. For example, Shale maps the URL faces/remote/hellobean/welcomeUser to helloBean.welcomeUser(), which calls the welcomeUser method associated with the helloBean managed bean. The URL faces/static/com/thepeninsulasedge/scripts/maps.js identifies a script file located in a Java archive (JAR) under the package structure /com/thepeninsulasedge/scripts/.

In short, Shale Remoting provides a simple mechanism to implement AJAX functionality in custom JSF components that would otherwise require the implementation of a custom PhaseListener or ViewHandler to handle XMLHttpRequests and serve static resources. In this article, Shale is used to map JavaScript event listeners to methods defined in managed beans. More specifically, it's used to tie Glisteners - JavaScript functions that respond to events triggered by Google Map's GMap2 object - to methods implemented in Java. Note that DWR could be used to accomplish this task; however, Shale provides tighter integration with the JSF managed bean facility as well as the ability to serve resource files from archives.

Using Shale Remoting
In the example that follows we construct a simple managed bean (HelloBean) that defines a method (welcomeUser) that's invoked by a JavaScript function in Figure 1.

The function is called when a user enters his or her name into a text field, and a response for each event is displayed in a div below the text field. Listing 1 shows the complete JSP page. The HelloBean managed bean defines three methods: welcomeUser, getParam, and writeResponse.

The latter two methods are simply utility functions. The getParam method extracts a value from the request string via the RequestParameterMap for a given parameter. The writeResponse method writes a response to the FacesContext, which is rendered to the client and processed by an XMLHttpRequest handler (more on this later).

The welcomeUser method is invoked with the URL faces/dynamic/hellobean/welcomeUser. This method extracts the value associated with the username parameter from the request, manipulates the extracted value, and then writes a response to the client using the writeResponse method. A parameter can be passed to the welcomeUser method by using the URL faces/dynamic/hellobean/welcomeUser?username=Ric, which calls the method and provides a name/value pair as a parameter. Accessing the URL via a browser provides a simple way to test this, and should display a response similar to that shown in Figure 2.

Once mapped to a URL, the welcomeUser method is easily tied to an event listener that uses the JavaScript XMLHttpRequest object to post to the URL mapped to the method innovation. The code to perform this operation is in Listing 2 and is contained in a file, scripts.js. (Listings 2-11 can be downloaded from the online version of this article at http://java.sys-con.com).

For simplicity's sake, the Prototype framework (http://prototype.conio.net/) is used to manipulate XMLHttpRequest objects - the AJAX.Request function handles each request. The function requires a URL, which is used to call the welcomeUser method defined in the HelloBean managed bean. The AJAX.Request function also accepts a JavaScript object as an argument, which defines the request method (for example, POST or GET) as well as the parameters to append to the request string. The final argument passed to the AJAX.Request function identifies a handler used to process the response initiated asynchronously by the request. In the example, the response is handled by the displayResponse function, which writes the text of the response to a div identified by the ID response. The $(...) notation is shorthand for the document.getElementById() JavaScript function and is a feature of the Prototype framework.

The welcomeUser function is fired on the onkeyup event of the text field. With each keystroke the user enters in the text field, the welcomeUser function is called, which invokes the method defined in the managed bean. In essence, the managed bean contains the logic to respond to each JavaScript onkeyup event.

JavaServer Faces Components Now that we have identified a mechanism to link Java code to JavaScript, let's quickly review the key facets of a JSF component before putting the pieces together to build our first mashup component. There are essentially three elements to a JSF component: behavior, presentation, and tag definition. (Figure 3 shows the class definitions for each element of the component used in this article.)


Behavior
Component classes characterize behavior and extend the javax.faces.component.UIComponentBase class or one of its subclasses (Listing 2). Each component class is also defined in the faces-config.xml file shown as follows:

<component>
<component-type>
com.thepeninsulasedge.components.MapPanel
</component-type>
<component-class>
com.thepeninsulasedge.components.UIMap
</component-class>
</component>

Presentation
A component's presentation is delegated to a separate class that extends the javax.faces.render.Renderer class (Listing 3). A renderer produces a graphical representation that need not be implemented with HTML. The presentation could be represented by XUL, ASK, Telnet, or any number of protocols. For our purposes the renderer is used to generate HTML and JavaScript. The JavaScript produced by the renderer is used to post to URLs defined by the Shale Framework as well as consume the Google Maps API. Note that this compromises the clean separation between presentation and behavior implemented by the JSF component architecture because behavior is now also defined in the renderer. Unfortunately this is a necessary evil when mixing JSF with AJAX. Each renderer class must also be defined in the faces-config.xml file shown as follows:

<render-kit>
   <renderer>
     <component-family>
com.thepeninsulasedge.components.MapPanel
     </component-family>
     <renderer-type>com.thepeninsulasedge.components.Map</renderer-type>
     <renderer-class>
com.thepeninsulasedge.components.MapRenderer
     </renderer-class>
   </renderer>
</render-kit>

Tag Definition
The JSP tag representing the component is defined by a subclass of the javax.faces.webapp.UIComponentTag class (Listing 4) and a tag library descriptor (TLD) - an XML file that provides metadata for the tag (Listing 5). The TLD should be registered in the web.xml file shown as follows:

    <jsp-config>
      <taglib>
        <taglib-uri>http://thepeninsulasedge.com/jsf</taglib-uri>
        <taglib-location>/WEB-INF/pc.tld</taglib-location>
      </taglib>
    </jsp-config>

Developing Mashup Components with JSF and Shale
As previously mentioned, the intent of this article is to build a JSF component that encapsulates a mashup service and provides the ability to link JavaScript events that represent interactions with the encapsulated service to methods associated with a managed bean. Now that you have a basic understanding of the core technologies involved, let's take a look at an example of a mashup component.

<tpe:map id="gmap"
    initLat="#{mapbean.initLat}"
    initLng="#{mapbean.initLng}"
    zoomLevel="#{mapbean.initZoom}"
    inlineStyle="width:500px;height:500px;"
       key="#{mapbean.key}"
       model="#{mapbean}"/>

The tpe:map component represents a wrapper for the Google Maps API, and it generates the HTML and JavaScript shown in Listing 6. The rendered component (shown in Figure 4) provides a generic interface to the API and can easily be changed to represent another mapping API such as Yahoo! Maps. To do so, simply replace the renderer class (Listing 3) provided in the example with one that generates the HTML and JavaScript required to display a map from Yahoo! Maps. User interactions with the Google Maps API, such as zooming, panning, and clicking, are trapped by JavaScript events, which in turn request a URL that is mapped to a method associated with a managed bean.

The map component has two required attributes: key and model. The key attribute is the account identifier for the Google Maps service. You can request a key at www.google.com/apis/maps/signup.html. The model attribute requires a subclass of the com.thepeninsulasedge.components.model.MapModel abstract object (Listing 7). A concrete implementation of this object represents the model for the map component. The object contains geospatial points in the form of com.thepeninsulasedge.components.model.GeoPoint objects (Listing 8) that represent visual markers on the map. The object also contains methods or event handlers to respond to events fired by the map.


The MapModel and MapBean
The MapModel object (Listing 7) defines two abstract methods: onSelect and onMoveEnd. The onSelect method is called when a user selects a marker on the map, and the onMoveEnd method is invoked after a user completes a zoom or pan operation.

abstract public String onSelect(GeoPoint pt);

abstract public String onMoveEnd(String lat, String lng, String zoom);

Both methods represent steps in a simple template or behavior pattern in which behavior is implemented by a subclass of the MapModel object (Figure 5).

The onSelect and onMoveEnd methods are called by the selectPoint and moveEnd template methods, respectively. Both methods extract values for parameters from a request string, pass those values onto the methods that inject behavior (for example, onMove or onSelect) into the encapsulated method, and return a response using the writeResponse method.

The moveEnd and selectPoint methods are invoked by the faces/dynamic/mapbean/moveEnd and faces/dynamic/mapbean/selectPoint URLs, respectively. The addSelectPointListener and addMoveEndListener JavaScript functions defined in the mapScript.js file (Listing 9) associate a GEvent listener with the current GMap object. When called, a listener posts a request to the URL that invokes either the moveEnd or selectPoint method. In this example, Gevent listeners are generated on marker selections or pan and zoom operations. For more information on Gevent listeners or other facets of the Google Maps API, refer to the Google Maps API documentation (www.google.com/apis/maps).

The concrete implementation of the MapModel object used in this example is com.thepeninsulasedge.view.managed.MapBean (Listing 10). The class defines an onSelect method that extracts a message or string contained in a GeoPoint object, which is associated with a marker on the map. The extracted message is then written as a response to the initial XMLHttpRequest object using the writeResponse method, and displayed in an info window on the map (Figure 6).

The onMoveEnd method updates a display that shows the coordinates of the map's center and the current level of magnification (Figure 7).

Besides providing methods to respond to JavaScript events, the MapBean also defines three action methods - addPointToMap, removeSelectedPoint, and clearMap - that are executed by command components. These methods demonstrate how the tpe:map component can be integrated with existing JSF components. For example, the addPointToMap method adds a new point to the map by manipulating the collection of GeoPoint objects contained in the MapBean. The method creates a new GeoPoint instance from an address and adds the point to the MapBean's current list of points. The method is executed by h:commandButton and the address is provided by an h:inputText field (See Listing 11).

GeoPoints
Each GeoPoint object in the MapBean is associated with a marker on a map and located by latitude and longitude. (To better understand the relationship between GeoPoint objects and the MapBean, see the diagram in Figure 8.)

Thus, when a user clicks a marker, the corresponding GeoPoint object is located in the MapBean by finding a GeoPoint object with a matching set of coordinates. The following Predicate (see the Apache Commons Collection at http://jakarta.apache.org/commons/collections/) is used to perform the evaluation between latitude and longitude and a corresponding GeoPoint object. The Predicate performs this evaluation while iterating through a collection of GeoPoint objects.

   public static Predicate findPredicate(final String lat, final String lng) {
     return new Predicate() {
       public boolean evaluate(Object obj) {
     if (!(obj instanceof GeoPoint))
       return false;
     GeoPoint pt = (GeoPoint)obj;
     if (pt.getLat().equals(lat) && pt.getLng().equals(lng)) {
       return true;
     } else {
       return false;
     }
    }
     };
   }

The GeoPointUtil class also provides a convenient way to create new instances of GeoPoint objects using the Yahoo! Geocoding API. The technique simply parses coordinates from an XML result generated by a request and uses the coordinates to create a new instance. The result is parsed with the Apache Commons Digester (http://jakarta.apache.org/commons/digester/).

The MapRenderer
The HTML and JavaScript in Listing 6 is the code used to create a new instance of the GMap2 object - the JavaScript object that represents a map in the Google Maps API. The code also ties the JavaScript used to consume the Google Maps API to the Java code in the MapBean. The com.thepeninsulasedge.components.MapRenderer class (Listing 3) is responsible for producing the markup shown in Listing 6. To understand which method in the MapRenderer class produced a specific snippet of JavaScript or HTML, look at the comments generated by the MapRenderer class in Listing 6. Note that only the load function used to instantiate the GMap2 object and a set of variables are dynamically generated in the MapRenderer class. This is done to ensure that each tpe:map component has a unique load function and set variables, allowing multiple tpe:map components to be used in a single page. Uniqueness is guaranteed by appending the component's client identifier to the name associated with the dynamically generated variable or function. According to JSR 127, each server-side component in JSF is guaranteed a unique client identifier.

The MapRenderer class relies on functions defined in the mapScripts.js file (Listing 9). These functions limit the need to hardcode JavaScript into the class and define essential functions such as addSelectPointListener and addMoveEndEvent. For the JavaScript generated by the MapRenenderer class to use functions defined in mapScripts.js, the file must first be imported. This is done with Shale Remoting. The linkJavascript method in org.apache.shale.remoting.XhtmlHelper is used for the import statement for the mapScripts.js file:

<script type="text/javascript"
src="/GoogleMapsAndDWR-JSFView-context-root/faces/static/com/thepeninsulasedge/
components/scripts/mapScripts.js">
</script>

The script import provides a reference to the mapScript.js file relative to the Web applications class path. Thus, the file specified is located under the package structure com/thepeninsulasedge/components/scripts/. A similar import is generated for the prototype.js file.

Conclusion
Consuming mashup services in the enterprise is a reality, and encapsulating mashup APIs with custom JSF components provides an elegant solution for packaging and reusing these services. The example in this article consolidated the complex markup and script shown in Listing 6 into a simple and concise component that amounted to a single XML tag. Imagine that - the power of Google Maps packaged in a simple component. Moreover, the componentization of the Google Maps API, as well as the linkage between JavaScript events and the JSF managed bean facility, makes it relatively easy to tie Google Maps to J2EE services such as EJB 3.0. The result of this blending of external mashups, Shale, and JSF is an enterprise-ready mashup service in very accessible package. To learn more about the technologies referenced in this article, please refer to the references provided.

References

© 2008 SYS-CON Media