Showing posts with label REST. Show all posts
Showing posts with label REST. Show all posts

Thursday, March 26, 2009

Using JSON with JAXB in new Jersey 1.0.2

Jersey 1.0.2 was released in early February and I just got around to updating to it. I was quite excited to read about the new support for JAXB marshalling to JSON formats (this was introduced I believe in an earlier release but not well detailed). Jakub Podlesak has some nice articles on this functionality on his blog and would recommend reading them. One thing I found, is initially I attempted to use the MAPPED_JETTISON notation in the JSONConfiguration which worked fine with Java JDK 1.6u10. However when I changed this to Natural notation to support my GXT application expected output, I recieved the following error:

javax.xml.bind.JAXBException: property "retainReferenceToInfo" is not supported 
   at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:63) 
   ...


Upon re-reading the dependency notes of Jersey, I noticed the following note: "This module depends on the JAXB reference implementation version 2.1.10 or greater, and such a version is required when enabling support for the JAXB natural JSON convention..." . The version of JAXB shipped with JDK 1.6 is not at a high enough level to support this functionality. Fortuantely the workaround to get this to work is pretty easy if you following the following steps:
  1. Download the JAXB jar from the Jersey depenendencies: jaxb-api-2.1.jarand save this to a local location.
  2. Copy the jaxb-api-2.1.jar to the jre/lib/endorsed folder of your JRE or JDK installation. You may need administrative privileges on some platforms.
  3. Restart eclipse (or other IDE).
  4. In your project class path, include the other JAXB jar files listed under the "non maven developers require... section of the JAXB portion of the document

Information on the endorsed libraries is available here.

Monday, December 1, 2008

GXT Tree and Loading with REST

Anyone who has used GXT can tell you that it is very powerful, but examples (and the javadoc) are still few and hard to find. The examples that come with the SDK are great (see Ext GWT 1.2 Samples), but if you need to something beyond these you typically need to figure it out on your own. I hope to be able to write a series of blog articles on some of the designs I have figured out for the conversion of my Stamp application from Swing to GXT. (Some of my readers may remember I was experimenting with EXT-JS (more on this in a future blog)).
Getting a tree to work in GXT is not too hard. In fact the demos do a pretty job of illustrating this. However, my tree was a little more complex than the trees shown. First some features of my tree:
  1. Has three separate types of objects (Stamp Collections, Albums and Countries)
  2. Stamp Collections and Albums may contain children (Albums and Countries respectfully)
  3. A Restful WebService is used to return the content of a particular item. (for example to get the albums of a stamp collection with id of "5" the URI might look like http://hostname/web-app/resources/collections/albums/5).

My original approach was that I wanted to make a request on the selected tree node when an expansion occured. However when I used filtering, this caused the invocation of the filter to actually load all the nodes (which was very slow on the first filter). You can turn this off, however that defeated the point of the filter. It also didn't seem like the right solution since at least in the examples the trees were being loaded via an RPC call. So the problem really boiled down to making the tree make an AJAX call to the Restful Web Service upon requesting the expansion of an unloaded node. First I should mention that trees in GXT actually act on BaseModel objects. One point of interest here is that BaseModel object are not TreeModel's (in this way it does work differently than Swing-based trees).
The approach I took was to create a customized DataProxy and pass this proxy to the store on creation. Thus as nodes are loaded/expanded the data proxy will be used to obtain the node's children. My proxy essentially overloaded the load( ) method and for convienence added another method getRequestBuilder( ). I would send requests using the output of the request builder to the Restful Web Service and process the results with a JSONReader and output the load result in the callback's onSuccess( ) method. So lets look at some code:
public class StampTreeDataProxy implements DataProxy<BaseModel,List<BaseModel>> {

   public StampTreeDataProxy( ) {
      super();
   }

   /**  
    * Generate the request builder for the object. If it is the root node (ie. null)
    * then request the stamp collections.  Else, get the appropriate Albums or Countries.
    * For any other type of object, since we do not know how to build a Restful Web
    * URI, simply return a null value.
    * 
    * @param item   The current (parent) item.
    */
   protected RequestBuilder getRequestBuilder( BaseModel item ) {
      RequestBuilder builder = null;
      if( item == null ) {
 builder = HttpUtils.getRestfulRequest(StampModelTypeHelper.getResourceName(StampCollection.class));
      } else if( item instanceof NamedBaseTreeModel ){
         String pathInfo = ((item instanceof Album) ?
                StampModelTypeHelper.getResourceName(Country.class) :    
                StampModelTypeHelper.getResourceName(Album.class)) +"/" + ((NamedBaseTreeModel)item).getId();
         builder = HttpUtils.getRestfulRequest(
                StampModelTypeHelper.getResourceName(item.getClass()), pathInfo, HttpMethod.GET );
      }
      return builder;
   }
  
   public void load(DataReader<BaseModel, List<BaseModel>> reader, 
       final BaseModel parent, final AsyncCallback<List<BaseModel>> callback) {
      
      RequestBuilder builder = getRequestBuilder( parent );
      // If the builder is null, then we do not have a Restful Builder we can handle.
      if( builder == null ) {
         callback.onSuccess(new ArrayList<BaseModel>());
         return;
      }
      builder.setCallback(new RequestCallback() {
      
         public void onError(Request request, Throwable exception) {
            GWT.log("error",exception);
         }

      @SuppressWarnings("unchecked")
      public void onResponseReceived(Request request, Response response) {
         if( response.getStatusCode() == Response.SC_OK ) {
            if (HttpUtils.isJsonMimeType(response.getHeader(HttpUtils.HEADER_CONTENT_TYPE))) {
               JSONValue json = JSONParser.parse(response.getText());
               Class _c = StampCollection.class;
               if( parent instanceof StampCollection || parent instanceof Album) {
                  _c = ( parent instanceof StampCollection ) ? Album.class : Country.class;
               }

               // Modified JsonReader which can read structures of ModelType definitions.
               // For this, I believe a regular JsonReader would work, however
               // it would create instances of BaseModel objects instead of
               // my specific types (which have nice accessor methods).
               StructuredJsonReader<BaseListLoadConfig> reader = new
               StructuredJsonReader<BaseListLoadConfig>(new StampModelTypeHelper(), _c );
               ListLoadResult lr = reader.read(new BaseListLoadConfig(), json.toString());
               callback.onSuccess(lr.getData());
            }
            } else {
               GWT.log("a non-status code of OK" + response.getStatusCode(), null);
            }
         }
      });
      try {
         builder.send();
      } catch (RequestException e) {
         e.printStackTrace();
      }
   }
}

Using this DataProxy, I can create an instance and pass it to the store I am creating for the tree:
public class BrowseStore extends TreeStore<BaseModel> {
  
   public BrowseStore( ) {
      super(new BaseTreeLoader<BaseModel>( new StampTreeDataProxy()));
   }
 
   // Simplified method to create the tree content and load it 
   public void load( ) {
      removeAll();
      getLoader().load();
   }
  
}

In this desgin, when load() is called (either on the loader of the store or the store itself), the DataProxy will be called for the root element. Since I am ignoring the reader (argument to the load( ) method) I create the new StructuredJsonReader. As stated above, the reason for this is three fold:
  1. It handles structured ModelTypes (ie. a ModelType with a field representing another ModelType.
  2. The ability to delegate the creation of the BaseModel to another class (in this case the StampModelTypeHelper which will create the appropriate instance of the modeled object (eg. Country).
  3. Finally uses the class provided to create the propert model type definition.

There is one negative of this solution. Currently it is posting a single request per node in the tree (eg. given an Album get all the countries). I plan on redesigning this to be a little more efficient, however given the mixture of BaseModel types, in order to get an efficient structure downloaded, I may need to forgoe the clean model structure in preference for a more efficient algorithm.

Friday, October 3, 2008

Collections and JAX-RS

As I had previously reported, both RestEasy and Jersey both suffered from the inability to return a collection of JAXB marshalled objects. I was thinking on this a little, and while I could use the JAXBCollection fix in Jersey, it seemed a little bit of a hack until it goes into the 1.0 release. Instead (for now), I have written a few wrapper classes which they themselves are XmlRootElements thus supporting marshalling with JAXB. This actually worked and is a reasonable workaround (since I really only need to return collections for four to five persistent objects. These wrapper objects simply have a collection/list of the persistent objects with the XmlElement set to have the appropriate name of the child elements for processing:

package org.javad.stamp.model.collections; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; import org.javad.stamp.model.Country; @XmlRootElement(name="countryList") public class CountryList { @XmlTransient public List countries = new ArrayList(); public CountryList( ) { } public void setCountries( List c ) { countries = c; } @XmlElement(name="country") public List getCountries() { return countries; } public void addCountry( Country c ) { countries.add(c); } }

Deploying RestEasy in Tomcat 6.0

Since I was successful deploying Jersey to Tomcat 6.0, I decided to check out RestEasy from JBoss. One issue I ran into with Jersey was it's inability to XML serialize a collection of objects. This is bug ID is 18, and it is fixed for the 1.0 release (however a beta of this does not appear to be available yet), but I decided to see if RestEasy (a non-reference implementation) has the same issue.

UPDATE I have discovered from some testing that neither XML nor JSON is currently supported as a return type for a Collection/List of items. An issue has been filed for RestEasy for JSON (RESTEASY-134) with an RC1 delivery, however I did not see one for XML.

Here are the steps I used to get this working:

  1. Download RestEasy from the JBoss website: http://www.jboss.org/resteasy/
  2. Follow the steps 2 to 4 of my previous blog Deploying Jersey in Tomcat 6.0
  3. Download and create a Eclipse library for JavaAssist. Include this in your WEB project (or provide the javaassist.jar for Tomcat)
  4. Create a new RestEasy library in eclipse which contains the content of the rest easy installation lib location. You can probably skip a few of the jars if you do not need all the functionality (such as jyaml.jar and possibly mail.jar)
  5. Modify your web.xml for your project to include the following (this comes right from the sample web.xml in the rest easy install):

    <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>TestWeb</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <context-param> <param-name>resteasy.scan</param-name> <param-value>true</param-value> </context-param> <!-- set this if you map the Resteasy servlet to something other than /* <context-param> <param-name>resteasy.servlet.mapping.prefix</param-name> <param-value>/resteasy</param-value> </context-param> --> <!-- if you are using Spring, Seam or EJB as your component model, remove the ResourceMethodSecurityInterceptor --> <context-param> <param-name>resteasy.resource.method-interceptors</param-name> <param-value> org.jboss.resteasy.core.ResourceMethodSecurityInterceptor </param-value> </context-param> <listener> <listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class> </listener> <servlet> <servlet-name>Resteasy</servlet-name> <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class> </servlet> <servlet-mapping> <servlet-name>Resteasy</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
  6. Start your tomcat in Eclipse and you should be good to go!

Thursday, October 2, 2008

Deploying Jersey in Tomcat 6.0

Jersey is a reference implementation for the JAX-RS (JSR-311) for building RESTful Web services. It is nearing approval with the JSR committees. While there are many wikis and articles in using Jersey with Netbeans (downloading the Netbeans 6.1 EE package includes everything you need for JSR-311), there was very little information on using Tomcat 6.0 with Jersey. After piecing together several blogs, I was able to get a simple resource working in Tomcat using JSR-311.

The following are the steps I used to get this working:

  1. Download and unjar the Jersey distribution (I used 0.8) from https://jersey.dev.java.net/ to a location on your system (lets call it JERSEY_HOME for this article).
  2. Download and install the Java-WS distribution (I used 2.1) from https://jax-ws.dev.java.net/. (lets call it JAXWS_HOME for this article).
  3. Rama Pulavarthi wrote a blog (in fact the key for me) in configuring tomcat to reference the Jersey distribution jars. To summarize, in TOMCAT_HOME/conf/catalina.properties modify the shared.loader property entry to point to your JAXWS_HOME/lib/*.jar. Mine looks like this:

    shared.loader=C:/dev/jaxws-ri/lib/*.jar
  4. Create a new Dynamic Web Project in Eclipse using Tomcat 6.0 as the application server.
  5. Create a new library in which you add the following JARs:
    1. JERSEY_HOME/lib/asm-3.1.jar
    2. JERSEY_HOME/lib/jersey.jar
    3. JERSEY_HOME/lib/jsr311-api.jar
  6. Next modify the web.xml to include the adaptor for jersey. Most of the Blogs refer to a different class than what appears in 0.8 I am not certain which is the right class, only the following works for me (and the documented one is not available throught the downloads!):

    <servlet> <servlet-name>ServletAdaptor</servlet-name> <servlet-class>com.sun.jersey.impl.container.servlet.ServletAdaptor</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ServletAdaptor</servlet-name> <url-pattern>/resources/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>30</session-timeout> </session-config>
  7. If you start Tomcat, you will see the following error:

    com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes.

    This error is due to not providing any root RESTful resources.

  8. Create a new class and include the following:

    import javax.ws.rs.GET; import javax.ws.rs.ProduceMime; import javax.ws.rs.Path; @Path("/test") public class TestResource { @GET @ProduceMime("text/html") public String getMessage( ) { return "hello"; } }
  9. Start and test the application with the following url:

    http://localhost:8080/appName/resources/test

    and you should see a hello in your web-browser.

Making your servlet application URLs more Restful

If you are a developer who likes to work with Java you may have come to know REST and Restful Web Services. The real advantage of REST is its ability to get rid of some of the "webspeak" and make your URLs a little more platform independent. As a client developer it is much nicer to make a request to http://somehost/Application/stamp/5533 to retrieve stamp 5533 than the traditional http://somehost/Application/servlet/StampServlet?id=5533. Theoretically you could rewrite your application services layer without modifying the client. If you are not familiar with REST and Restful Web Services, a good dissertation on this can be found here by Roger L. Costello.

So all of this is nice, but how can we apply this to a servlet-based Web Application? There are projects out there such as the Java Restlet API, and while I think this is good, it does mean essentially having a non-servlet compatible solution (since the Restlet takes the place of a servlet). Instead, you can take advantage of some of the REST themes by using servlet-based Web Applications following these steps:

  • We want to write a Rest-like servlet for accessing Stamps. We have a servlet of the class StampServlet which is mapped in the web.xml of our servlet container as stamp. This would look like the following:
    <servlet> <description>Servlet for processing Stamp Restful requests</description> <servlet-name>stamp</servlet-name> <servlet-class>org.javad.stamp.servlet.StampServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>stamp</servlet-name> <url-pattern>/stamp/*</url-pattern> </servlet-mapping>

    The key here is the /* in the url-pattern of the servlet-mapping. This will mean any URI that is received that starts with "stamp" will send it to the StampServlet, but we can use any of the information on the URI chain to help direct the servlet to process the request in a Restful way. For example, if we are retrieving the details of a stamp our URL would look like:
    http://hostname/StampApp/stamp/563

    Using the GET method, where the ID of the stamp in this case is 563. If this was a modify function, the URL would look similar only a method of PUT would be used.
  • REST uses many of the less-popular methods of HTTP. In particular the PUT and DELETE methods. Fortunately, the HttpServlet class does implement these with the doPut(HttpServletRequest,HttpServletResponse) and doDelete(HttpServletRequest,HttpServletResponse) methods. If you are not comfortable using these methods (for example the HttpServlet Javadoc does mention PUT for use in placing files on the server like FTP), we can make our URLs be a little less Restful, but still Restlike by using POST method and inserting a keyword after the ID of the object in question. I should note, that for some applications like GWT, using protocols other than POST or GET is difficult. In GWT we can attach a header value (X-HTTP-Method-Override) to indicate we'd like to use DELETE, but it is still sent to the POST method receiver initially. In this case, using a POST method URL along with the method override header we can still have a Restful URL that would look like the following:
    <form action="http://hostname/StampApp/stamp/563" method="POST"> </form>

    More practical would be the use in an AJAX application using the XMLHttpRequest in Javascript (since we can override the X-HTTP-Method-Override header:
    var req = new XMLHttpRequest(); req.open( "POST", "http://hostname/StampApp/stamp/563" ); req.setRequestHeader("X-HTTP-Method-Override","DELETE"); req.send();
  • URL Construction is great, but how do I make use of this in my servlet doPOST( )? Since we mapped the servlet with a /*, anything to the right of the stamp in the URI is treated as part of the PathInfo. Therefore within your doPOST() method, you can request the path-info and take the appropriate action. Similar to the following:
    protected void doPost(HttpServletRequest request, HttpServletResponse response) { String pathInfo = request.getPathInfo(); Method method = request.getHeader("X-HTTP-Method-Override"); if( pathInfo.isEmpty() ) { createNewStamp( ); // purely a POST } else if( method != null && pathInfo != null) { long id = Integer.parseInt( pathInfo.split("/")[0]); if ("DELETE".equalsIgnoreCase( method ) ) { doDelete(request,response); // or call deleteStamp(id); } else if ( "PUT".equalsIgnoreCase( method )) { doPut( request, response ); // or call modifyStamp(id); } else { // if there are further elements in the pathInfo call the appropriate code... } } else { log("POST method called for stamp details without Method Override header. Use GET method to retrieve stamp details or specify a Method Override header."); } }

While this is not perfect, it is certainly easier to program a client to a URI like stamp/563 than the traditional way of stamp?id=563&action=DELETE. In the above example, it is likely that the code for servicing an action like Delete is in a specified method, so instead of trying to call the doDelete( ) simply a call to deleteStamp(id) would probably be more appropriate. Using this technique allows you to support both methods of processing your objects in a consistent way, while being flexible in support for the client technology. While it does muck up your doPost() methods a little bit this is a minor tradeoff for more readable URIs.

I should also mention that additional values in the pathInfo are used in Rest to indicate additional actions to take place against that object. For example:

http://hostname/StampApp/stamp/563/catalogues

With a method of GET would refer to a request to retrieve the catalogues for the stamp with the ID 563. In this situation having a controller (such as that provided by the Restful application) would definitely help in forwarding these to the correct service. Depending on the complexity of your application, you may be able to simply do this internally within your servlet such as in the else case mentioned above, however for more than a few actions this can be complex. Especially if catalogues (using the example above) can exist outside of the context of a stamp. This would mean you'd have to provide a servlet or some application that can process catalogues and find a way to tie the stamp servlet with the catalogue servlet. The worst case I can think of in my application would be trying to get all of the stamps that are in a country, filtered by an album in a stamp collection. This might look like:
http://hostname/StampApp/collection/56/album/25/country/76/stamps

As you can see, this is not quite as readable. Since I can also get stamps by album or simply by collection I might be more prone to simple request the stamps for collection 56 and then take on the album/country ids as queryString data:

http://hostname/StampApp/collection/56/stamps?album=25&country=76

Not pure Rest, but Rest-like. If I truly did want to retain the Restful URI, I would likely write a controller which returned stamps, and if the pathInfo of the GET request for the collection servlet contained stamps in it I would directly call it passing the pathInfo and then allow the controller to decide how to filter the URI (in my case I have a StampFilter object which accepts the three objects and calls the appropriate JPA Query based on the filter setup so this would be quite easy for me to do).