CS 639 - class 18  REST and Java EE and Jersey

 

Note hw3 is due next Tuesday—questions?

JUnit: anyone new to it?  Handout from cs636 on JUnit is available, linked under Resources>JUnit4

Handout on firstRest

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

 

Note the Wikipedia article on REST

Now we’ll look at the big chart from there, actually from Richardson and Ruby, “Restful Web Services”, the first important book with implementations: 

 

 

RESTful Web Service HTTP methods[10]

Resource

GET

PUT

POST

DELETE

Collection URI, such as http://example.com/resources/

List the URIs and perhaps other details of the collection's members.

Replace the entire collection with another collection.

Create a new entry in the collection. The new entry's URL is assigned automatically and is usually returned by the operation.

Delete the entire collection.

Element URI, such as http://example.com/resources/ef7d-xj36p

Retrieve a representation of the addressed member of the collection, expressed in an appropriate Internet media type.

Replace the addressed member of the collection, or if it doesn't exist, create it.

Treat the addressed member as a collection in its own right and create a new entry in it. Added by eoneil: or other action (catch all)

Delete the addressed member of the collection.

 

Chap 4 orderService implements POST for /order, GET, PUT, and DELETE for /order/1234.

Online orderService implements DELETE for /order as well, and also another resource: POST to  /poke

 

 

REST in general, continued.

Last time looked at expected meaning of various actions. One important aspect of these operations is whether or not they can be repeated safely without changing the result—

 

Idempotent or not?  See pg. 38 for definition, also note at top of pg. 72 (or search “idempotent”)

GET retrieve a copy of a resource: YES, and also “safe” / “read-only” (no server-side modifications happen affecting the resource as seen by the client)

POST: create a new resource (plus catch-all): NO

PUT: replace a resource with given new content: YES

DELETE: delete a resource: YES

 

This property allows retry, important for many things including security.

 

Of course there’s also SOAP Web services. Later we should come back and do a comparison. Since they only use one URL for a whole service, and POST, they certainly are not idempotent.

 

Implementing a REST server, and also a client.

 

We’re using Java with Java EE, but C# with .NET is also a viable option. Our book covers both ways.

 

Now look at our examples, firstRest and orderService (for Chap 4). They are both use the Jersey implementation of the Java EE JAX-RS, the Java API for RESTful Web Services.  What does that mean?

The Software Universe for Java and where REST support lives in it.

 

The main Java distributions from Sun: Java EE (EE = enterprise edition) vs. the more basic Java SE (“the JDK”) (SE = standard edition)

Older names still in use: J2EE, JEE, J2SE, JSE.

 

So far everything we’ve used is in Java SE, except the servlet API: so SAX, DOM/XPath, reflection API, are all in the JDK, i.e., the library of Java SE. So is JDBC for database access, which you probably used in your database class.

 

Java EE contains the servlet API, and lots of other things, most famously “EJBs” Enterprise Java Beans, (APIs for them, anyway)  tomcat can’t execute EJBs on its own, but we don’t need to use them. You need a full feature app server like JBoss or Glassfish to run EJBs.

 

Java EE also contains JAX-RS, the Java API for RESTful Web Services, AKA JSR 311 (JSR = Java Spec. Request, so 311 is the spec. number).  As an API, it’s just a spec of what should be supported, with a spec. doc linked to the class web page.  So we need an implementation of this spec to run something.  Actually the spec only specifies the server side, I guess on the excuse that anyone should be able to do HTTP requests to certain URIs from the client. 

 

JAX-RS an API in the generalized sense of covering annotations as well as method calls. Also it’s a “framework” in the sense that it specifies calls to our code at appropriate moments. It’s “just an API” in the sense that it doesn’t have implementation of the specified actions.

 

We’re using the Jersey, the “reference implementation” for this JAX-RS specification. Also tomcat (by Apache) is the reference implementation of Java EE’s servlet container. That means Sun, the Java EE source, has pointed to it as definitive. Jersey is part of project Glassfish, a Sun-sponsored development effort for Sun’s own web app server (Glassfish) and support for it (luckily done in separately usable pieces, so we can use their JAX-RS code with tomcat, our app server).

 

Building programs that use JAX-RS: we need extra jars over the JDK

In servlet1 and pa2, we needed an extra jar file for the servlet API, part of Java EE, so we used tomcat’s lib/servlet-api.jar (see build.xml). And so did eclipse, because of our setup of tomcat within it (see icon for Apache Tomcat Libraries in Java Build Path>Libraries, click it, see servlet-api.jar).

Then tomcat executed our code, with the help of its implementation of the servlet API and related services.

 

Now to use JAX-RS as implemented by Jersey, we need two kinds of jars:

·         JAX-RS (JSR 311) API: jsr311-api.jar   for API, part of Java EE so not in our JDK

·         the Jersey implementation: jersey-bundle-xx.jar  (or several separate jars)  (and it needs asm-xx.jar for byte-code manipulation) xx= version number.

 

The Java EE library is delivered in many jars. Jar files are big, so we don’t want to include ones we don’t need in our projects.

 

Jersey offers the JAX-RS server implementation and also a client library to give a complete REST solution framework.

 

We are also using Eclipse. Eclipse is independent of Sun, which in fact has its own IDE, Netbeans.  But even so, eclipse is a very popular IDE for Java EE development, and has a lot of support for Java EE though its “WTP” Web Tools Project (mentioned in the firstRest tutorial).  We see its Java EE support via the “Java EE Perspective” inside eclipse.  With this perspective selected, we see the Servers View, where we can start and stop tomcat, etc. You can get this Servers view to show in other perspectives too, via Window>Show View .... So more basically, the Java EE support is behind the whole tomcat-handling ability of eclipse.

 

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.)