| ||||||||||||||||||||||||||||||||||
Resin 3.1 Documentation Examples Changes Quercus Database Amber EJB SOA/ESB IoC JMS Servlet JMX Hessian Security Simple Service Configuring a service with JAXB An ESB client Flickr REST JAX-WS example |
Resin offers simplified access to external REST-based services. This example shows how to access a subset of the flickrTM API. Resin's Service-Oriented Architecture (SOA) allows not only easier deployment of web services via SOAP, REST, and Hessian, but also simplified client access to external services. Traditionally, using REST services as a client required developers to write potentially complex libraries. URL construction and parsing returned XML data can often be clumsy and time-consuming to do manually. Resin's SOA and JAXB simplify these tasks. This tutorial shows an example for a subset of the flickrTM API. Files in this tutorial
Writing a REST ClientMany companies now offer web services externally to be used as components in third-party applications. These services often have REST, SOAP, and/or XML-RPC interfaces. SOAP and XML-RPC are standards and have somewhat well-defined access methods for clients. REST on the other hand is about being able to use the properties of HTTP to design a custom interface. Thus clients are often difficult to write because each service requires specialization. Unlike the case of SOAP where WSDLs allow automated construction of client stubs, REST-based services essentially defy standardization. While Resin's SOA does not offer automated client construction, it does make the task easier. The example in this tutorial shows how to access a subset of the flickrTM image sharing service API. This API is well-designed and consistent which allows very clean client construction. REST Client ArchitectureThe REST client architecture in Resin can be thought of as a stack. At the top of the stack is the client, which calls methods on a proxy. That proxy then constructs the calls to the external REST service. Complex objects that are sent to the service use JAXB to marshal from Java objects to XML. The request is then processed by the service and a response is sent. The response is unmarshalled from XML to Java using JAXB and returned to the client by the proxy. REST BindingsA REST interface can be thought of as a binding between an HTTP request and a method invocation. All the portions of the HTTP request (the path information, the query, headers, and POSTed data) can be used to construct the method call.
In the case of the flickrTM API, the method
binding is straightforward and uses the query data almost exclusively. For
example, there is a method, The data returned by the service uses a simple XML format. In this case, the service might return the following: <?xml version="1.0" encoding="utf-8" ?> <rsp stat="ok"> <user nsid="12345678901@N01"> <username>foo</username> </user> </rsp> The next section shows how to construct a Java client interface to construct these URLs and decode the responses. Annotated Client InterfaceAn annotated Java class or interface is used to access a REST service as a client. In this example, only JAX-WS annotations are used, so those developers already familiar with JAX-WS can leverage their experience to construct certain types of REST interfaces. package example; import javax.jws.*; import example.data.*; public interface FlickrAPI { @WebMethod(operationName="flickr.people.findByEmail") public FlickrResponse findByEmail(@WebParam(name="api_key") String api_key, @WebParam(name="find_email") String find_email); @WebMethod(operationName="flickr.people.findByUsername") public FlickrResponse findByUsername(@WebParam(name="api_key") String api_key, @WebParam(name="username") String username); @WebMethod(operationName="flickr.people.getInfo") public FlickrResponse getInfo(@WebParam(name="api_key") String api_key, @WebParam(name="user_id") String user_id); @WebMethod(operationName="flickr.people.getPublicGroups") public FlickrResponse getPublicGroups(@WebParam(name="api_key") String api_key, @WebParam(name="user_id") String user_id); @WebMethod(operationName="flickr.people.getPublicPhotos") public FlickrResponse getPublicPhotos(@WebParam(name="api_key") String api_key, @WebParam(name="user_id") String user_id, @WebParam(name="extras") String extras, @WebParam(name="per_page") int per_page, @WebParam(name="page") int page); }
In this interface, there are five methods, each annotated with @WebMethod.
The When a client calls one of the methods in the interface above, Resin automatically constructs a URL, makes the request, and decodes the response. Notice that the return value of all the methods above is FlickrResponse. This class is a JAXB-annotated class that wraps all the responses sent from the service. The next section shows how to construct the JAXB-annotated classes for easy access to response data. JAXB-Annotated Response Classes
Even though flickrTM uses a custom XML format
for responses, it is easy to construct simple JAXB-annotated classes for
these responses, even without schema! First notice that the responses all
have a wrapper element, package example.data; import javax.xml.bind.annotation.*; @XmlRootElement(name="rsp") public class FlickrResponse { @XmlAttribute public String stat = "ok"; @XmlAnyElement(lax=true) public FlickrPayload payload; public String toString() { return "FlickrResponse[stat=" + stat + ", payload=" + payload + "]"; } }
Notice the @XmlRootElement annotation on the class. This annotation sets the
the element name to
FlickrPayload is an empty interface, used only for typing in this example.
FlickrUser implements FlickrPayload and is the response payload used by the
package example.data; import javax.xml.bind.annotation.*; @XmlRootElement(name="user") public class FlickrUser implements FlickrPayload { @XmlAttribute public String nsid; @XmlElement public String username; public String toString() { return "FlickrUser[nsid=" + nsid + ", username=" + username + "]"; } }
The package example.data; import java.util.*; import javax.xml.bind.annotation.*; @XmlRootElement(name="photos") public class FlickrPhotos implements FlickrPayload { @XmlAttribute public int page; @XmlAttribute public int pages; @XmlAttribute public int perpage; @XmlAttribute public int total; @XmlElement(name="photo") public List<Photo> photos = new ArrayList<Photo>(); public static class Photo { @XmlAttribute public String id; @XmlAttribute public String owner; @XmlAttribute public String secret; @XmlAttribute public int server; @XmlAttribute public String title; @XmlAttribute public int ispublic; @XmlAttribute public int isfriend; @XmlAttribute public int isfamily; public String toString() { return "Photo[id=" + id + ", " + "owner=" + owner + ", " + "secret=" + secret + ", " + "server=" + server + ", " + "title=" + title + ", " + "ispublic=" + ispublic + ", " + "isfriend =" + isfriend + ", " + "isfamily=" + isfamily + "]"; } } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("FlickrPhotos[page=" + page + ", "); sb.append( "pages=" + pages + ", "); sb.append( "perpage=" + perpage + ", "); sb.append( "total=" + total + ", "); sb.append( "photos=("); for (Photo photo : photos) { sb.append(photo.toString()); sb.append(' '); } sb.append(")]"); return sb.toString(); } } Here there are four attributes of the root element and a list of child elements representing the photos. Each photo has a set of attributes as well. By default, JAXB serializes lists as unadorned sequences of elements. For example, a FlickrPhotos object with two photos might have the following XML: <rsp stat="ok"> <photos total="2" perpage="10" pages="1" page="1"> <photo isfamily="0" isfriend="0" ispublic="1" title="Our wedding" server="2" secret="x123456" owner="12345678901@N01" id="3041"/> <photo isfamily="0" isfriend="1" ispublic="0" title="Best friends" server="1" secret="y123456" owner="12345678901@N01" id="3042"/> </photos> </rsp>
The remaining payload classes, Client configurationA REST client is configured using the <web-service-client> tag, just like with SOAP and Hessian clients. The only difference is that the URL must use the "rest:" prefix. The Resin SOA connects to the service using the given URL and places a proxy instance into JNDI. Any clients may access the service then by doing a JNDI lookup, then calling methods on the returned object. <web-service-client jndi-name="rest/flickr"> <url>rest:${webApp.url}/flickr/rest/</url> <interface>example.FlickrAPI</interface> <jaxb-package>example.data</jaxb-package> </web-service-client>
Notice the <jaxb-package> tag. In order for JAXB to marshal and
unmarshal objects, it must know all the classes it might encounter.
There are two ways to inform JAXB about the classes: either by a list of
package names or an explicit list of classes. In this example, all of the
classes that JAXB will use are in the package
Simply listing a <jaxb-package> may not be appropriate in all cases.
In fact in this example, notice that FlickrPayload is simply an empty
interface and is not JAXB-annotated. Thus JAXB should load only those
classes in the FlickrError FlickrGroups FlickrPerson FlickrPhotos FlickrResponse FlickrUser Testing the interfaceAn example implementation of the service is provided here for testing purposes only. It returns sample data upon receiving REST requests and does not actually store or display images. Because setting up a REST service is covered in another tutorial, only the client side of the REST connection is explained fully here. The demonstration JSP simply looks up the proxy interface in JNDI, then calls the methods with sample queries. The calls and their results are printed. flickr is a trademark of Yahoo! Inc.
|