CS 639 - class 18  REST and Java EE and Jersey

 

Note hw3 is available, due next Tuesday

Note links at end of class web page: REST, Java EE, JAX-RS, Jersey, JAXB

The firstRest Tutorial

The firstRest tutorial assumes you want to use Jersey, tomcat, and eclipse, exactly our setup.  It uses the Jersey client library, which we will do at least part of the time.

 

Note that you don’t need to do the Jersey download yourself, as indicated in the tutorial. All the needed jars are in the firstRest project already.  Remember not to overwrite web.xml when you set up your eclipse Dynamic Web Project.

 

Just unzip firstRest.zip, use “ant build”, “ant deploy” as in pa2.  This version uses env variables TOMCAT_URL and CATALINA_HOME just like pa2, so everything should work immediately.

 

firstRest has a web.xml with

<url-pattern>/rest/*</url-pattern>

 

and is deployed as the firstRest webapp, so its base URL is

<TOMCAT_URL>/firstRest/rest/

 

Hello.java is the server-side source. Let’s pare it down to the XML  hello”:

      import ...

 

   @Path("/hello")

   public class Hello {

      // This method is called if XML is request

      @GET

      @Produces(MediaType.TEXT_XML)

      public String sayXMLHello() {

            return "<?xml version=\"1.0\"?>" + "<hello> Hello Jersey" + "</hello>";

      }

   }

 

This is all that is needed to support a GET to URI baseURL+/hello = <TOMCAT_URL>/firstRest/rest/hello

 

Since we have returned a String from the method, that specifies the XML to return.  Of course we don’t want to compose XML this way any more.

 

Note the Media type (older name: MIME type) “text/xml”, used in HTTP’s Content-Type and Accept headers, i.e., it’s a type in HTTP’s typing system. This results in a Content-Type header for the resulting reply:

 

HTTP/1.1 200 OK

Server: Apache-Coyote/1.1

Content-Type: text/xml    

Date: Tue, 05 Apr 2011 20:13:29 GMT

Connection: close

 

<?xml version="1.0"?><hello> Hello Jersey</hello>

 

It’s easy to get this with wget: use  --save-headers).  See build.xml for firstRest, one target.

 

For more info on media types, see http://en.wikipedia.org/wiki/Internet_media_type

To start with, we are interested in text/xml, text/plain, text/html, application/xml, and application/json. text is for human-readable info, and application is for app-readable, so xml qualifies either way.

 

We see that REST is using HTTP’s typing system (media types) for message content.

 

Once deployed, we can exercise this with wget, our helpful browser-like tool.  But with it, we have more control over headers than we do with a browser:

      <target name="restGetXML">

            <echo message="running wget to get XML" />

            <exec executable="wget">

                  <arg value="--header=Accept: text/xml" />

                  <arg value="${BASE_URL}hello" />

                  <arg value="-Ohello-response.xml" />

            </exec>

      </target>

Here BASE_URL is <TOMCAT_URL>/firstRest/rest/

 

So the client is saying it is happy to accept XML, and this matches with the server’s capability to send XML. 

 

So we see that these two headers are crucial, especially when the server can supply multiple Mime types, as this one can:

 

            // This method is called if HTML is request

            @GET

            @Produces(MediaType.TEXT_HTML)

            public String sayHtmlHello() {

                  return "<html> " + "<title>" + "Hello Jersey" + "</title>"

                              + "<body><h1>" + "Hello Jersey" + "</body></h1>" + "</html> ";

            }

 

 

I’ve added a POST to firstRest: Recall that POST is a catch-all verb, so we can do weird things with it.  Here the server takes in XML, puts it under a new root <echo> element, and returns that.

 

            // this echoes received XML elements inside an echo element

            @POST

            @Consumes(MediaType.TEXT_XML)

            @Produces(MediaType.TEXT_XML)

            public String echoXMLHello(String message) {

                  int endDeclaration = message.indexOf("?>");          

                  return message.substring(0,endDeclaration+2) +

                        "<echo>" + message.substring(endDeclaration + 2) + "</echo>";

            }

 

      <target name="restEcho">

                  <echo message="running wget to post order to /order" />

                  <exec executable="wget">

                        <arg value="--post-file=order.xml"/>

                        <arg value="--header=Content-Type: text/xml" />

                        <arg value="${BASE_URL}hello" />

                        <arg value="-Oecho-response.xml"/>

                  </exec>

      </target>

 

 

This is a POST that doesn’t create an element, so it returns HTTP 200, OK, not HTTP 201, Created, and doesn’t include the Location header.

 

The Java client for firstRest

 

We could modify URLGrabber to be a REST client—it’s that easy.  But Jersey has set up helper classes to make it even easier.

 

How does this work?

 

If you look at the web.xml, you’ll see the URL mapping of /rest/* that matches anything starting with “/rest/” after the context name “firstRest”, so for example the URL

<TOMCAT_URL>/firstRest/rest/hello

Will be processed the servlet.

 

Also note that the servlet invoked is from the jersey support.  So the jersey servlet accepts the call to doGet, say, and then dispatches it into the code we write, at the method marked up with the right path and @GET.

 

But how does it find the .class files that have annotations?  By looking at the init parameter set up in web.xml;

    <init-param>

      <param-name>com.sun.jersey.config.property.packages</param-name>

      <param-value>cs639.first</param-value>

    </init-param>

 

This specifies the package, and thus the directory under classes, that has the @GET, etc. annotations.  The servlet can access this init param via the ServletContext object.

 

It can also access the Accept: header via the request object.  So it has all it needs to locate the right method to call.

 

(The servlet also looks at the Java type of the arguments, so we have a choice of how to accept or produce content for the method.  So far we’re just using the basic String support, but in orderService we use other objects.)