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

Wednesday, November 27, 2013

Book Review: Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON

I was particularly interested in accepting Packt Publishing's offer to provide a book review of Masoud Kalali's and Bhakti Mehta's Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON because its title mentions three things I have had good experience with (REST, JAX-RS 2.0, and JSON) as well as a concept that I only have basic familiarity with but wanted to learn more about (WebSockets). For this review, I was provided with an electronic copy of this book with about 100 pages of "regular" text and code examples.

Preface

The Preface of Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON is a good place to start if you are thinking of purchasing this book and want to get an overview of what it entails. The Preface breaks down what is in each of the five chapters with three or four sentence descriptions of each chapter.

It is also in the Preface that the reader learns that the book assumes use of Maven (final chapter only) and GlassFish Server Open Source Edition 4 (most chapters) for building and running the examples. The recent news that Oracle will not provide commercial support for GlassFish 4 has certainly added tarnish to the idea of using GlassFish 4, but it is still a valid application server to use for Java EE illustrative purposes as it remains freely available and is going to continue to be the reference implementation of the Java EE 8 specification.

Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON's Preface also states "who this book is for": "This book is ... for application developers who are familiar with Java EE and are keen to understand the new HTML5-related functionality introduced in Java EE 7 to improve productivity. To take full advantage of this book, you need to be familiar with Java EE and have some basic understanding of using GlassFish application server." I agree that readers of this book will be much better off if they have a basic understanding of Java SE and Java EE principles. Examples of this are terms that are assumed to be implicitly understood such as POJO (Plain Old Java Object) and StAX. Perhaps the most important assumed knowledge is awareness of what JSON is and how it differs from and compares to XML.

Chapter 1: Building RESTful Web Services Using JAX-RS

The initial chapter of Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON begins with a brief introduction to the basic characteristics and principles of the REST architectural style before quickly moving onto "the basic concept of building RESTful Web Services using the JAX-RS 2.0 API." The chapter states that the "Java API for Representational State Transfer (JAX-RS) specification defines a set of Java APIs for building web services conforming to the REST style." Note that JAX-RS 2.0 is standardized via JSR 339 and is more commonly as "The Java API for RESTful Web Services.

Chapter 1 provides step-by-step instructions for converting Java POJOs into RESTful resources via application of JAX-RS annotations such as @Path (defining resource), @GET (defining methods), and @Produces (defining MIME type). This section continues these step-by-step instructions by illustrating how to write an Application subclass and define subresources. Along the way, this chapter also demonstrates using curl to communicate with an HTTP-exposed service from the command line.

After covering converting a POJO to a resource and listing additional JAX-RS annotations in a handy table, the first chapter moves onto discussion of the client API for JAX-RS (this standardized client API for JAX-RS is new to 2.0). Entities, which are passed as part of requests and responses, are also covered along with custom entity providers (implementations of MessageBodyReader and MessageBodyWriter). Use of JAXB with JAX-RS is also introduced.

One part of this chapter that I found particularly interesting and useful is the inclusion of a highlighted section on "Tips for debugging errors with MessageBodyReader and MessageBodyWriter." I would like to see more books have sections like this.

Chapter 1's section on "using the Bean Validation API with JAX-RS" introduces the @ValidationOnExecution annotation as part of Java EE's Bean Validation support. The chapter's Java validation coverage also talks about extracting status responses from Response.readEntity(GenericType<T>) to handle errors.

Chapter 2: WebSockets and Server-sent Events

As the chapter's title suggests, Chapter 2 is an introduction to WebSockets and Server-Sent Events (SSEs), with Chapter 3 going into more details on these subjects. This second chapter begins with an overall view of polling between a client and server and then illustrates an example of this using an Ajax (XMLHttpRequest) JavaScript client example.

After outlining the drawbacks of polling mechanisms between clients and servers, the chapter moves onto coverage of long polling. The XMLHttpRequest of Ajax fame is then used again, but this time with significantly more explanation. The chapter includes a discussion on the disadvantages of long polling.

Chapter 2 introduces concepts that attempt to address the drawbacks and limitations of polling. Server-sent Events (SSE) (or EventSource) are described as "an HTML5 browser API that makes event pushing between server and client available to web application developers." The authors add, "The major SSE API that is considered the foundation of SSE in the client side for JavaScript developers is the EventSource interface." Code listings are provided to illustrate SSE implemented via Java servlet on the server side and JavaScript on the client side along with a JSP example that embeds JavaScript.

WebSockets are the theme of the remainder of Chapter 2 and are described as a "component of HTML5" that "adds a brand new method for interaction between clients and servers to address the scalability and flexibility required for modern web-scale applications by introducing a full duplex event-based communication channel between clients and servers." Chapter 2 mentions that modern browsers support WebSockets and discusses operations available on a JavaScript WebSockets object.

The authors mention that Java EE 7 provides "full support for HTML5, SSE and WebSockets" and references JSR 356 ("Java API for WebSocket").

This information-packed second chapter begins to wind down with a table comparing characteristics (browser performance, communication channel, and complexity) of the three covered communication approaches (long polling, Server-Sent Events, and WebSockets). I liked the fact that the chapter then concludes with example use cases/scenarios where each of the three approaches to asynchronous web communication is best suited.

I found it a bit odd that the second chapter has the side note referencing Jersey as the JAX-RS reference implementation rather than the first chapter (which was devoted to JAX-RS) including that side note. A more appropriate side note for this chapter references the article Memory Leak Patterns in JavaScript.

Chapter 3: Understanding WebSockets and Server-sent Events in Detail

Chapter 3 dives more deeply into the concepts introduced in Chapter 2. These more in-depth topics related to WebSockets include encoders and decoders in the Java API for WebSockets (@ServerEndpoint), Java WebSocket Client API (@ClientEndpoint), sending blob/binary data rather than text (JSON/XML), WebSockets security (including example in GlassFish), and three best practices for WebSockets applications. The more in-depth topics on Server-sent Events covered in the third chapter include an example of developing a Server-sent Event client using Jersey API and three "best practices for applications based on Server-sent Events."

The third chapter introduces JSON and JSON-P.

Chapter 4: JSON and Asynchronous Processing

The fourth chapter of Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON covers JSR 353 ("Java API for JSON Processing") and "related APIs." The chapter explains that Java EE 7 adds JSON support that replaces less standard open source Java JSON processing products such as google-gson and Jackson.

Chapter 4 provides a nice overview of the JSON API and includes a table that lists the classes provided along with a description of the classes and how those classes are used. The authors state that "JSONObject is the entry point to the entire JSON API arsenal," but it is the Json class that provides factory methods for creating instances of other classes in the JSON API. The chapter covers generating JSON documents (JsonGeneratorFactory and JsonGenerator), parsing JSON documents (JsonParser), using JSON object model to generate JSON (JsonBuilderFactory and JsonObject), and using JSON object model to parse JSON (JsonReader and JsonObject). A quick paragraph contrasts when to use the streaming approaches versus the object model approaches for generating and parsing JSON; the trade-off is similar to that between StAX and DOM (memory considerations versus ease of use).

The next section of Chapter 4 covers Servlet 3.1 enhancements with specific focus on NIO additions to servlets (ReadListener and WriteListener) and WebSockets support in servlets. Changes to servlets to support this new functionality are covered and include ServletOutputStream, ServletInputStream, @WebServlet asyncSupported attribute, asynchronous request and response processing, and JAX-RS 2.0 filters and interceptors.

The authors introduce asynchronous support with EJB 3.1 and 3.2 and include an interesting observation: "In Java EE 6, the @Asynchronous [annotation] was only available in full profile while in Java EE 7 the annotation is added to the web profile as well."

One of the interesting side notes of this chapter is mention that key JSON API classes can be used with the Automatic Resource Management (try-with-resources) mechanism introduced with Java SE 7.

Chapter 5: RESTful Web Services by Example

While the first four chapters of Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON are packed with new information and details related to writing RESTful web services with Java EE, JAX-RS 2.0, WebSockets, and JSON, the fifth and final chapter provides two examples of applying these concepts and technologies to representative use cases. The samples in this chapter are built with Maven and deployed to GlassFish.

As the authors advertised at the beginning of the chapter, these two samples do illustrate integrated application of topics covered in the prior chapters of the book. The first sample employs Server-sent Events, Asynchronous Servlet, JSON-P API, JAX-RS 2.0, EJB Timers (@Schedules) and the Twitter Search API (including Twitter's OAuth support and Twitter4j).

The second Chapter 5 example demonstrates integration and application of WebSockets, JAX-RS/HTTP "verbs" (GET, DELETE, POST), using JSON-P for writing JSON documents, and leveraging asynchronous benefits. I like the approach this book has taken of using four information-heavy chapters to introduce concepts and then devoting the entire final chapter to examples of how to integrate these concepts into realistic sample applications.

Miscellaneous Observations

The following are some miscellaneous observations I have made regarding Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON.

  • Covers a lot of material in a relatively small number of pages.
  • Has some interesting and useful emphasized side notes.
  • Lacks a lot of color in the graphics and screen snapshots, meaning the printed book probably does not lose much in way of presentation when compared to the electronic versions.
  • There are some type-setting issues, especially in code samples, where some spaces are missing that would normally separate types, variable names, and so forth.
  • Although Maven and GlassFish are used in the book, many of the examples could be tweaked to build with a Ant, Gradle, or other build system and to be deployed to an application server other than GlassFish.
  • Some experience with Java EE, HTML, and REST/HTTP concepts is assumed. More experience in these areas will obviously make the book more approachable, but only basic familiarity is needed to understand the concepts in this book.
Conclusion

This book is what its title and its preface describe: a book that details how to develop RESTful services with JAX-RS 2.0, WebSockets, JSON, and more. As the authors state in the Preface, this best is best suited for developers with at least minimal Java EE knowledge as there is some assumed knowledge in a book that covers this much in a little over 100 pages.

Thursday, October 6, 2011

JavaOne 2011: REST and Hypermedia as the Engine of Application State with Standard Java APIs

For my last technical session of JavaOne 2011, I wanted to attend something "easy" with a short title because of the mental fatigue and crammed brain resulting from four days of blistering rate of technical information coming my way. With that in mind, I chose to walk across the hall from Hilton San Francisco's Golden Gate 6/7/8 to Golden Gate 3/4/5 for the presentation "REST and Hypermedia as the Engine of State with Standard Java APIs" (24609). Although I have worked with REST and JAX-RS/Jersey, the abstract of this presentation caught my eye. In addition, I believe that HATEOAS may be the least understood, most confusing, and most misused term (or unused portion) in all of REST-dom.

The presenters of "REST and HATEOAS with Standard Java APIs" (Mattias Hellborg Arthursson and Kalle Stenflo, both from Jayway) stayed away from using the acronym HATEOAS because no one seems to know how to pronounce. Because I'm typing it rather than saying it, I'm using the abbreviation from here on out. Their agenda showed items to be covered including REST, JAX-RS, RESTful JAX-RS ("true REST with hypermedia"), and "Generic JavaScript Client."

Stenflo briefly discussed the history of REST based on Fielding's famous dissertation. He mentioned that the "architectural style" of REST had been misused and hijacked by several to be a structural style. Stenflo then went on to discuss the basics of REST with definitions of terms like "resource." He also discussed the HTTP "verbs" and stated that "REST is about using the web the way it was intended." Stenflo discussed "self-descriptive messages" that "contain all information needed to complete a task" (this statelessness is what makes horizontal scaling easier).

Stenflo had multiple slides titled "HATEOAS: Hypermedia as the Engine of Application State." This concept allows for "discoverable resource interaction" and "allows resources to evolve over time." Stenflo explained that the consumer is provided with the correct link for a given resource.

Arthursson stated that if you're doing REST with Java, "you should probably be using JAX-RS." Arthursson introduced some of the key annotations that JAX-RS provides such as @Path, @GET, @POST, @PUT, and @DELETE. He feels (as do I) that the JAX-RS is one of the best JSRs in recent years. The Expert Group for JAX-RS 2.0 is about to publish a draft and the new version will focus on improvements requested from the community. These features include a common client API (that would be very nice) and "support for hypermedia" (JAX-RS currently does not support HATEOAS)

Until JAX-RS 2.0 implementations are available (and the spec is not even written yet!), we need a way to handle hypermedia in Java-based REST style applications. Arthursson introduced some approaches from RESTeasy Atom link, Jersey linking, and "purely annotation-based" approaches that are "more readable, "more versatile," and require DTOs to have "knowledge of hypermedia." His understanding is that the JSR Expert Group is leaning toward the annotation-based approach as well.

Arthursson introduced jax-rs-hateoas as an open source JAX-RS 1.x extension. The README for jax-rs-hateoas states:

JAX-RS-HATEOAS is a small extension to JAX-RS to enable hypermedia in JAX-RS 1.x applications.

Adding hypermedia to your existing application is easily done in four steps:
* Extend HateoasApplication (or JerseyHateoasApplication)
* Add a RequestContextFilter in your web.xml (enables the framework to get access to the full request URIs)
* Add @Linkable annotations to all methods that should be linkable.
* Use HateoasResponse instead for building responses with HATEOAS links.
The normal Response methods (ok, created, entity, etc.) will now return a HateoasResponseBuilder.
The HateoasResponseBuilder contains methods to easily add links to your responses:
** link(String linkId, Object... params) - adds a link to the referenced linkId
(as defined by an @Linkable annotation).
** selfLink(String linkId, Object... params) - adds a link to the referenced linkId with a 'self' rel
(rather than the default rel defined in the referenced @Linkable annotation
** ...etc

Arthursson covered these steps in his slides and then moved to an IDE (SpringSource Tool Suite) to show a live example. His examples demonstrated the ability to use @Linkable from jax-rs-hateoas to provide hypermedia in responses (such as HateoasResponse). The jax-rs-hateoas project seems pretty easy to use and it was stated that it's pretty small and is designed to allow developers to customize their hypermedia as necessary.

Following Arthursson's IDE-based demonstration, Stenflo took back over to demonstrate a JavaScript client that is a "single-page JavaScript application." He stated that it's useful for a developer to test out his or her REST API. The demonstrated client works on hypermedia returned to the client.

The speakers referenced several projects that attendees might find useful in Jayway's Github respository. They specifically referenced rest-assured in addition to jax-rs-hateoas and also stated that the demonstrated JavaScript client is available in the demos section (there is also a core download).

A good question was whether XML was supported in jax-rs-hateoas (only JSON was demonstrated). At this point, only JSON (the "cool stuff") is supported, but they acknowledged that XML needs to be supported.

This was an interesting presentation and looks like a promising solution for bridging the gap between today and JAX-RS 2.0 in terms of appropriately using HATEOAS in REST-based applications using JAX-RS. The project is still young, but could end up filling a niche in the Java/REST world. In the end, it's likely that JAX-RS 2.0 will make this obsolete, but that could be years from now.

Wednesday, February 17, 2010

More Groovy-based Simple HTTP Clients

In my previous blog post, I briefly wrote about the use of Groovy, Groovy's GDK String, and the Java-provided URL class to write simple HTTP/REST clients. In this post, I look at how use of the URLConnection returned from a call to URL.openConnection() can be used to gain even more details about the HTTP connection.

The URLConnection class and its child HttpURLConnection class provide many useful methods for accessing details regarding a HTTP connection. These are easily obtained from a call to URL.openConnection(). This is demonstrated in the next Groovy code listing.


#!/usr/bin/env groovy
def NEW_LINE = System.getProperty("line.separator")
if (args.length < 1)
{
println "Enter a URL as an argument."
System.exit(-1)
}
def address = args[0]
def urlInfo = address.toURL()
println "URL: ${address}${NEW_LINE}"
println "Host/Port: ${urlInfo.host}/${urlInfo.port}${NEW_LINE}"
println "Protocol: ${urlInfo.protocol}${NEW_LINE}"

def connection = urlInfo.openConnection()
println "Connection Type: ${connection.class}"
println "Content Type: ${connection.contentType}"
println "Response Code/Message: ${connection.responseCode} / ${connection.responseMessage}"
println "Request Method: ${connection.requestMethod}"
println "Date: ${connection.date}"
println "Last-Modified: ${connection.lastModified}"
println "Content Length: ${connection.contentLength}"


The above code leads to output like that shown in the following screen snapshot where I run the script against three URLs I often hit from my web browser: http://www.javaworld.com/, http://java.net/, and http://www.oracle.com/technology/index.html.



The response code and message are included in the output shown in the above screen snapshot. In all three cases, the response code and message are 200 and OK respectively. The content length of -1 is the value provided by the getContentLength method when (according to the Javadoc) "content length is not known."

In the example above, I used the convenience methods provided for "certain header fields [that] are accessed frequently" (see URLConnection Javadoc description). Because many more header fields are included in responses, it can be useful to find out what these are and see their values. This is demonstrated in the next code sample.


#!/usr/bin/env groovy
def NEW_LINE = System.getProperty("line.separator")
if (args.length < 1)
{
println "Enter a URL as an argument."
System.exit(-1)
}
def address = args[0]
def urlInfo = address.toURL()
println "===================================================================="
println "====== HEADER FIELDS FOR URL ${address}"
println "===================================================================="
def connection = urlInfo.openConnection()
headerFields = connection.getHeaderFields()
headerFields.each {println it}


The simple Groovy script above uses the GDK Map.each method that acts on closures and prints out the field values for each header field. The output from running this script against the same three URLs as before is shown next.



This output shows that there are some differences in the fields returned by each of the three URLs in response to the implicit GET request.

The combination of Groovy, Groovy GDK, URL, and HttpURLConnection make it easy to generate a quick and dirty HTTP client. This can be useful in testing HTTP-exposed services including HTTP-based REST services.

Tuesday, February 16, 2010

Minimalistic HTTP Clients with Groovy

The Rocky Mountain Oracle Users Group (RMOUG) Training Days 2010 main technical sessions are going to be held tomorrow and Thursday of this week and I will be presenting two presentations on Groovy and JAX-RS/REST at this conference. It seems like a good time to use Groovy and JAX-RS/REST in the same blog post.

In my previous blog posts on JAX-RS and REST, I have often used RESTClient as the client for testing my deployed services. RESTClient provides a nice graphical user interface and is easy to use. However, if I only need command line test, Groovy is one easy option.

With Groovy, it is easy to take advantage of Java's URI and URL classes. Groovy makes it even easier to use these than in traditional Java because the Groovy GDK-provided String class provides the very convenient toURI() and toURL methods. This is such an easy process that it's actually easier to demonstrate it than to attempt to describe it. So, here is an example in two lines of Groovy code (really one line if you don't count the shebang line):


#!/usr/bin/env groovy
println "http://localhost:8080/rest/resources/movies/2/2".toURL().text


The URL incorporated in the String in the above code points to a JAX-RS-powered REST-based web service I discussed in a previous blog post. The simple service behind that URL returns a "movie of the day" for the month and date given by the two integers at the end of the URL. When the above Groovy code is executed, the output appears as shown in the next screen snapshot.



That simple Groovy code of one real executable line leveraged the power of the Groovy GDK's String and a standard Java class to easily provide the text provided by the resource at the other end of the URL. It doesn't get any easier than that.

The next example is slightly more interesting, but still only requires a small number of lines of Groovy code:


#!/usr/bin/env groovy
address = "http://localhost:8080/rest/resources/movies/2/2"
println "URL: ${address.toURL().text}"
println ""
println "URI: ${address.toURI().toURL().text}"


The output from the above script is shown next.



Because Java's URL class provides significantly more details than just the text available from accessing that URL, this information is readily accessed in a Groovy script. An example of some of this additional information being retrieved is shown in the next code listing and associated output screen snapshot.


#!/usr/bin/env groovy
def NEW_LINE = System.getProperty("line.separator")
def address = "http://localhost:8080/rest/resources/movies/2/2"
def urlInfo = address.toURL()
println "URL: ${address}${NEW_LINE}"
println "URL Text: ${urlInfo.text}${NEW_LINE}"
println "Host/Port: ${urlInfo.host}/${urlInfo.port}${NEW_LINE}"
println "Protocol: ${urlInfo.protocol}${NEW_LINE}"
println "User Info: ${urlInfo.userInfo}${NEW_LINE}"
println "File: ${urlInfo.file}${NEW_LINE}"




For the final simple example in this post, I make the simple script above a little more dynamic by using the first argument to the script as the URL from which to access details.


#!/usr/bin/env groovy
def NEW_LINE = System.getProperty("line.separator")
if (args.length < 1)
{
println "Enter a URL as an argument."
System.exit(-1)
}
def address = args[0]
def urlInfo = address.toURL()
println "URL: ${address}${NEW_LINE}"
println "URL Text: ${urlInfo.text}${NEW_LINE}"
println "Host/Port: ${urlInfo.host}/${urlInfo.port}${NEW_LINE}"
println "Protocol: ${urlInfo.protocol}${NEW_LINE}"
println "User Info: ${urlInfo.userInfo}${NEW_LINE}"
println "File: ${urlInfo.file}${NEW_LINE}"


The corresponding output is shown in the next screen snapshot.



In this post, I have demonstrated how easy it is to mix the best of Groovy with the best of Java to put together a very simple client for accessing HTTP-exposed services such as one finds in many REST applications. I didn't even get to the URL.openConnection() method here, but this method opens up a great deal more information that might be useful for simple testing tools when using HTTP-exposed services.

Monday, February 15, 2010

JAX-RS and HTTP Responses

HTTP Status Codes are a big part of the HTTP protocol that most of us have seen countless times in our web browsing and development. We are used to seeing 404 (Not Found), 200 (OK), and so forth. Because HTTP is often closely tied with REST style applications, it is not surprising that JAX-RS provides tremendous support for returning appropriate HTTP responses from REST-based web services. In this blog post, I look at the tip of the iceberg in terms of JAX-RS support for HTTP-based service responses.

The code listing that follows would not generally be a useful piece of functionality, but it is used here to demonstrate how easy it is with JAX-RS to generate HTTP responses with specific HTTP status codes.


@Path("/responses")
public class ResponseMaker
{
/**
* Generates an HTTP response based on the String provided as part of this
* resource's URI.
*
* @param responseString Portion of this resource's URI from which the
* particular response will be generated.
* @return Response based on provided URI portion.
*/
@GET
@Path("/{responseString}")
@Consumes("text/plain")
@Produces("text/html")
public Response getResponse(
@PathParam("responseString") final String responseString)
{
final String desiredResponse = responseString != null
? responseString.trim().toUpperCase()
: Status.BAD_REQUEST.name();
Status status = null;
try
{
status = Status.valueOf(desiredResponse);
}
catch (IllegalArgumentException illegalArgEx)
{
status = Status.BAD_REQUEST;
}
return Response.status(status).type(MediaType.TEXT_HTML_TYPE).build();
}
}


The above code uses JAX-RS annotations to indicate which HTTP method corresponds to the Java method in the code listing (@GET indicates HTTP GET method). This simple method accepts a String and, if the String matches one of the Response.Status enum value's string representations, that Status is set. Otherwise, a default of BAD_REQUEST is selected. The Response is built with that status and with a MediaType of TEXT_HTML_TYPE.

I use RESTClient as an easy client tool to demonstrate this simple JAX-RS web service in action. The next two screen snapshots show what RESTClient sees (and what any other client would see) in terms of responses to certain provided URIs (shown at top of GUI).






JAX-RS also makes it easy to turn encountered exceptions into HTTP response codes indicating the error. The next piece of code demonstrates this.


/**
* Return an exception-based HTTP response based on the provided number
* indicating a particular exception to be used. The HTTP method PUT would
* normally likely not be the method used for this type of operation, but
* it makes it easy to differentiate from the other method in this class
* already tied to @GET and also accepting a single String.
*
* There are four cases in which a particular exception is used to build the
* response and that exception and a particular Response.Status are provided,
* telling the JAX-RS provider which HTTP status to tie to that particular
* thrown exception. In the default/general case when one of the first four
* are not used, no specific Response.Status is used, so the general 500
* Internal Server Error will be returned to the client along with the'
* exception's stack trace as the body.
*
* @param exceptionNumberType A number used to determine which type of
* exception is used for the basis of the response.
* @return Response based on the described exception type.
*/
@PUT
@Path("/{exceptionNumberType}")
@Consumes("text/plain")
public Response causeException(
@PathParam("exceptionNumberType") final int exceptionNumberType)
{
Exception exception;
Status status = null;
switch (exceptionNumberType)
{
case 1 : exception = new NullPointerException("1. Null Encountered.");
status = Status.NOT_FOUND;
break;
case 2 : exception = new IllegalArgumentException("2. Bad argument");
status = Status.PRECONDITION_FAILED;
break;
case 3 : exception = new RuntimeException("3. Runtime Exception");
status = Status.BAD_REQUEST;
break;
case 4 : exception = new NumberFormatException("4. Bad Numeric Format");
status = Status.NOT_ACCEPTABLE;
break;
default : exception = new Exception("General Exception");
}
throw status != null
? new WebApplicationException(exception, status)
: new WebApplicationException(exception);
}


I included some verbose comments on this method to describe how it behaves in greater detail. This method primarily demonstrates how the WebApplicationException is useful in turning Java exceptions into HTTP responses. I only use two of this exception's eight constructors in this example, but they demonstrate the difference between providing a particular HTTP Response.Status with the exception (and counting on the JAX-RS provider to place the results of Response.Status.getReasonPhrase() in the response body) or allowing the JAX-RS provider to associated a Response.Status with the exception (500) and showing the entire stack trace of the exception.

This difference is demonstrated in the next three screen snapshots. Two are of exceptions for which a particular (largely nonsensical here and only meant to illustrate how to do this) HTTP status code is associated with the exception and one is for the case where the JAX-RS provider assigns 500 implicitly and puts the exception's stack trace in the response body rather than the Response.Status.getReasonPhrase().







In this post, I have attempted to demonstrate how easy JAX-RS makes it to specify HTTP responses based on regular and exceptional conditions. I have only touched on a small part of the extensive JAX-RS support for building appropriate responses.

Playing with Jersey/JAX-RS Method Designators

There is a part of me that likes to try things just to see what happens. If I read something that says, "Never do this," I have a hard time not doing just that thing if I believe doing so comes at no significant cost. There can be value to this approach when I learn about the results associated with an improper action and can then more readily recognize the underlying cause when I inadvertently cause the same problem myself or see a colleague dealing with the same effects. In this blog post, I look at how JAX-RS 1.1 (specifically the Jersey implementation) handles some of the "don't do that" rules related to resource methods.

One JAX-RS Resource Method Designation Per Java Method

JAX-RS literature warns that only one JAX-RS method designation annotation is allowed per method in a Java class resource. In other words, I should only be able to apply @GET or @POST or @PUT or @DELETE to a method, but never apply more than one of them to the same method. This actually seems sensible, but it prevents some of the "trickery" one commonly saw in servlet applications where the doPost and doGet methods were set up so that one called the other or they both called the same piece of code.

To find out what the negative consequences are of specifying multiple JAX-RS method designators on the same method, I took the method addMovieOfTheDay from my previous blog post (JAX-RS and the REST Uniform Interface) and added the @POST annotation on top of its existing @PUT annotation. The altered code is shown next:

Two JAX-RS Method Designation Annotations for a Single Method

/**
* Add a new entry or update an existing entry representing a movie of the day.
*
* @param month Month for the movie of the day.
* @param date Day of the month for the movie of the day.
* @param title Title of the movie of the day.
* @return HTML excerpt for movie of the day entry status.
*/
@PUT
@POST
@Path("/{month}/{date}/{title}")
@Consumes("text/plain")
@Produces("text/html")
public String addMovieOfTheDay(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date,
@PathParam("title") final String title)
{
final Map<Integer, String> moviesOfTheMonth =
MOVIE_OF_THE_DAY.get(month-1);
if (moviesOfTheMonth != null)
{
moviesOfTheMonth.put(date, title);
}
return generateAddedMovieHtml(title);
}


The above code compiles without incident. This is not surprising because, as the JAX-RS 1.1 specification states (Section 3.3 Resource Models, emphasis added), "A request method designator is a runtime annotation that is annotated with the @HttpMethod annotation." However, when I try to deploy it following the steps outlined in my blog post JAX-RS with Jersey: An Introduction, I see an error in the GlassFish v3 web-based Administrative Console. A screen snapshot of that is shown next.



The output states that an IllegalStateException has been thrown and suggests seeing the logs for additional details. There is a very lengthy stack trace in the logs, but the most significant piece of it is this:


[#|2010-02-14T19:43:38.495-0700|SEVERE|glassfishv3.0|com.sun.jersey.server.impl.application.WebApplicationImpl|_ThreadID=25;_ThreadName=Thread-1;|A (sub-)resource method, public java.lang.String rmoug.td2010.rest.MovieOfTheDay.addMovieOfTheDay(java.lang.Integer,java.lang.Integer,java.lang.String), should have only one HTTP method designator. It currently has the following designators defined: [@javax.ws.rs.PUT(), @javax.ws.rs.POST()]|#]


With the context of the situation in mind, this is pretty straightforward: "only one HTTP method designation" is allowed for a resource method and the method "[addMovieOfTheDay(Integer,String)] currently has the designators ... @PUT ... @POST [defined]." Okay, so that is bad. Note to self: The advice to only use one method designator per class resource method should be heeded.

As I wrote this post, I verified that NetBeans 6.8 continues the tradition of creating new Java servlets with the automatically generated doPost and doGet methods calling the same protected method (processRequest in NetBeans 6.8, though I seem to recall this method had a different name previously, such as doProcess()). I decided the next thing to try was annotating two separate methods with respective annotations, but have them call the same underlying method. My assumption was that this would work fine. The code for this is shown in the next listing.

Two Designators/Two Methods But with Single Common Method

/**
* Add a new entry or update an existing entry representing a movie of the day.
*
* @param month Month for the movie of the day.
* @param date Day of the month for the movie of the day.
* @param title Title of the movie of the day.
* @return HTML excerpt for movie of the day entry status.
*/
@PUT
@Path("/{month}/{date}/{title}")
@Consumes("text/plain")
@Produces("text/html")
public String addMovieOfTheDay(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date,
@PathParam("title") final String title)
{
return addMovie(month, date, title);
}

/**
* Add a new entry or update an existing entry representing a movie of the day.
*
* @param month Month for the movie of the day.
* @param date Day of the month for the movie of the day.
* @param title Title of the movie of the day.
* @return HTML excerpt for movie of the day entry status.
*/
@PUT
@Path("/{month}/{date}/{title}")
@Consumes("text/plain")
@Produces("text/html")
public String addMovieOfTheDayPost(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date,
@PathParam("title") final String title)
{
return addMovie(month, date, title);
}

/**
* "Common" method for adding/updating a movie of the day that is intended
* to support PUT and POST.
*
* @param month Month for the movie of the day.
* @param date Day of the month for the movie of the day.
* @param title Title of the movie of the day.
* @return HTML excerpt for movie of the day entry status.
*/
private String addMovie(final Integer month, final Integer date, final String title)
{
final Map<Integer, String> moviesOfTheMonth =
MOVIE_OF_THE_DAY.get(month-1);
if (moviesOfTheMonth != null)
{
moviesOfTheMonth.put(date, title);
}
return generateAddedMovieHtml(title);
}


This code compiles without incident because I appended "Post" to the method name of the added method. However, I "forgot" to change the annotation to @Post for the new method and this became obvious when I tried to deploy to GlassFish. I saw the same general error as before in the console, but the logs had details on the new problem:


[#|2010-02-14T20:43:06.557-0700|SEVERE|glassfishv3.0|com.sun.jersey.server.impl.application.WebApplicationImpl|_ThreadID=25;_ThreadName=Thread-1;|A resource, class rmoug.td2010.rest.MovieOfTheDay, has ambiguous sub-resource method for HTTP method PUT, URI path template /{month}/{date}/{title}, and output mime-type: text/html. The problematic mime-type sets (as defined by @Produces annotation at Java methods addMovieOfTheDay and addMovieOfTheDayPost) are [text/html] and [text/html]|#]


This SEVERE-level log message is telling us that the two methods annotated with @PUT are ambiguous because they have the same URI path and same MIME type. One of these needs to be different to allow the JAX-RS provider to differentiate them. For our purposes, I simply change the new method addMovieOfTheDayPost to have the @POST annotation rather than the @PUT annotation.

Method Intended for POST Annotated with @POST

/**
* Add a new entry or update an existing entry representing a movie of the day.
*
* @param month Month for the movie of the day.
* @param date Day of the month for the movie of the day.
* @param title Title of the movie of the day.
* @return HTML excerpt for movie of the day entry status.
*/
@POST
@Path("/{month}/{date}/{title}")
@Consumes("text/plain")
@Produces("text/html")
public String addMovieOfTheDayPost(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date,
@PathParam("title") final String title)
{
return addMovie(month, date, title);
}


The code again compiles and even deploys this time. I don't show it here, but using RESTClient to access the resource via PUT and via POST works for both HTTP methods. In short, it does appear that one can delegate functionality from two different Java methods associated with different HTTP methods to the same common method.

The last thing I wanted to try in this area was having one method call the other rather than two methods calling a third, common method. To try this, I changed the method added for POST support to simply call the method for PUT:

POST-Supporting Method Calling PUT-Supporting Method

/**
* Add a new entry or update an existing entry representing a movie of the day.
*
* @param month Month for the movie of the day.
* @param date Day of the month for the movie of the day.
* @param title Title of the movie of the day.
* @return HTML excerpt for movie of the day entry status.
*/
@PUT
@Path("/{month}/{date}/{title}")
@Consumes("text/plain")
@Produces("text/html")
public String addMovieOfTheDay(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date,
@PathParam("title") final String title)
{
final Map<Integer, String> moviesOfTheMonth =
MOVIE_OF_THE_DAY.get(month-1);
if (moviesOfTheMonth != null)
{
moviesOfTheMonth.put(date, title);
}
return generateAddedMovieHtml(title);
}

/**
* Add a new entry or update an existing entry representing a movie of the day.
*
* @param month Month for the movie of the day.
* @param date Day of the month for the movie of the day.
* @param title Title of the movie of the day.
* @return HTML excerpt for movie of the day entry status.
*/
@POST
@Path("/{month}/{date}/{title}")
@Consumes("text/plain")
@Produces("text/html")
public String addMovieOfTheDayPost(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date,
@PathParam("title") final String title)
{
return addMovieOfTheDay(month, date, title);
}


This approach compiled successfully, deployed successfully, and I was able to invoke both the PUT-supporting and POST-supporting Java methods successfully. So, the approach of having one method directly call the other seems to work as well if a developer wants to get around the limitation of not having multiple HTTP method designations on the same Java method.


Only Public Methods Allowed as Resource Methods

Section 3.3.1 ("Visibility") of the JAX-RS 1.1 specification states: "Only public methods may be exposed as resource methods. An implementation SHOULD warn users if a
non-public method carries a method designator or @Path annotation."

Let's see what happens with Jersey in this situation.

To test this one out, I change the just-added method supporting POST to be package scope by removing its public modifier.

Package-level @POST-annotated Method

/**
* Add a new entry or update an existing entry representing a movie of the day
* via HTTP POST.
*
* @param month Month for the movie of the day.
* @param date Day of the month for the movie of the day.
* @param title Title of the movie of the day.
* @return HTML excerpt for movie of the day entry status.
*/
@POST
@Path("/{month}/{date}/{title}")
@Consumes("text/plain")
@Produces("text/html")
/*public*/ String addMovieOfTheDayPost(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date,
@PathParam("title") final String title)
{
return addMovieOfTheDay(month, date, title);
}


In this case, the code compiles and the WAR deploys without apparent incident, but things go less smoothly when a client attempts to use POST to add/change a movie. The package-level method is not available and this is made evident in the RESTClient GUI as shown in the next screen snapshot.



As the above image demonstrates, an HTTP response code of 405 ("Method Not Allowed") is returned when there is no public method associated with the specified HTTP method. As the specification suggests, Jersey does warn of this condition with the following WARNING-level logged statement:


[#|2010-02-14T21:13:31.226-0700|WARNING|glassfishv3.0|com.sun.jersey.server.impl.application.WebApplicationImpl|_ThreadID=28;_ThreadName=Thread-1;|A sub-resource method, java.lang.String rmoug.td2010.rest.MovieOfTheDay.addMovieOfTheDayPost(java.lang.Integer,java.lang.Integer,java.lang.String), MUST be public scoped otherwise the method is ignored|#]



Conclusion

The messages Jersey logs related to problems associated with resource method designations are relatively clear and straightforward. That is good news because it means that when these errors are unintentionally caused, they are more likely to be readily identified and addressed.

Saturday, February 13, 2010

JAX-RS and the REST Uniform Interface

In Roy Fielding's dissertation that started it all, he stated:

"The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components."


The above quotation can be found in Section 5.1.5 ("Uniform Interface") of Chapter 5 ("Representational State Transfer (REST)") of Fielding's now-famous PhD dissertation "Architectural Styles and the Design of Network-based Software Architectures."

The "uniform interface" concept is obviously important to REST and is a driving force behind the design of HTTP methods. JAX-RS provides sophisticated annotations-based support for providing uniform interfaces to clients from regular Java classes. The main intent of this post is to briefly cover how JAX-RS makes it easy to write Java classes that provide this uniform interface.

Nearly all REST-based applications "happen" to be HTTP based as well, but it is often emphasized that HTTP is not absolutely required to implement a REST-based application. Still, HTTP and REST often do go together, so it is not surprising that the JAX-RS-provided annotations @GET, @DELETE, @POST, and @PUT closely mirror in name the respective HTTP methods GET, DELETE, POST, and PUT. Note that JAX-RS also provides @OPTIONS and @HEAD annotations.

In my blog post JAX-RS with Jersey: An Introduction, I demonstrated use of the @GET annotation in conjunction with fellow JAX-RS annotations @Path and @PathParam. Only data retrieval (hence the "GET") was shown in that post. In this post, I expand upon (and change) the MovieOfTheDay class I used in that post to support creation/insertion of new data, updating of existing data, and deletion/removal of data.

As I stated in the previous post, I use an (slightly modified for this post) internal static map to emulate what would normally be a datastore such as a database. This piece of code is shown next.

MovieOfTheDay.java Fragment: Emulated Data Storage Portion

/** Overall simulated database of movies of the day. */
private static final Map<Integer, Map<Integer, String>> MOVIE_OF_THE_DAY;

static
{
MOVIE_OF_THE_DAY = new ConcurrentHashMap<Integer, Map<Integer, String>>();

final Map<Integer, String> janMovies = new ConcurrentHashMap<Integer, String>();
janMovies.put(10, "Trading Places");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.JANUARY), janMovies);

final Map<Integer, String> febMovies = new ConcurrentHashMap<Integer, String>();
febMovies.put(2, "Groundhog Day");
febMovies.put(13, "Casablanca");
febMovies.put(14, "Sleepless in Seattle");
febMovies.put(15, "How to Lose a Guy in 10 Days");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.FEBRUARY), febMovies);

final Map<Integer, String> marMovies = new ConcurrentHashMap<Integer, String>();
marMovies.put(16, "The Fugitive");
marMovies.put(17, "Darby O'Gill and the Little People");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.MARCH), marMovies);

final Map<Integer, String> aprMovies = new ConcurrentHashMap<Integer, String>();
aprMovies.put(25, "Raiders of the Lost Ark");
aprMovies.put(26, "Indiana Jones and the Temple of Doom");
aprMovies.put(27, "Indiana Jones and the Last Crusade");
aprMovies.put(28, "Indiana Jones and the Kingdom of the Crystal Skull");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.APRIL), aprMovies);

final Map<Integer, String> mayMovies = new ConcurrentHashMap<Integer, String>();
mayMovies.put(26, "Star Wars: Episode 1 - The Phantom Menace");
mayMovies.put(27, "Star Wars: Episode 2 - Attack of the Clones");
mayMovies.put(28, "Star Wars: Episode 3 - Revenge of the Sith");
mayMovies.put(29, "Star Wars: Episode 4 - A New Hope");
mayMovies.put(30, "Star Wars: Episode 5 - The Empire Strikes Back");
mayMovies.put(31, "Star Wars: Episode 6 - Return of the Jedi");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.MAY), mayMovies);

final Map<Integer, String> junMovies = new ConcurrentHashMap<Integer, String>();
junMovies.put(15, "The Great Outdoors");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.JUNE), junMovies);

final Map<Integer, String> julMovies = new ConcurrentHashMap<Integer, String>();
julMovies.put(4, "Independence Day");
julMovies.put(21, "Men in Black");
julMovies.put(22, "Men in Black 2");
julMovies.put(23, "I, Robot");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.JULY), julMovies);

final Map<Integer, String> augMovies = new ConcurrentHashMap<Integer, String>();
augMovies.put(10, "North by Northwest");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.AUGUST), augMovies);

final Map<Integer, String> sepMovies = new ConcurrentHashMap<Integer, String>();
sepMovies.put(1, "Dead Poets Society");
sepMovies.put(20, "Uncle Buck");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.SEPTEMBER), sepMovies);

final Map<Integer, String> octMovies = new ConcurrentHashMap<Integer, String>();
octMovies.put(28, "Psycho");
octMovies.put(29, "Sixth Sense");
octMovies.put(30, "Ghostbusters");
octMovies.put(31, "Young Frankenstein");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.OCTOBER), octMovies);

final Map<Integer, String> novMovies = new ConcurrentHashMap<Integer, String>();
novMovies.put(10, "The Italian Job");
novMovies.put(11, "Ocean's Eleven");
novMovies.put(12, "Ocean's Twelve");
novMovies.put(13, "Ocean's Thirteen");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.NOVEMBER), novMovies);

final Map<Integer, String> decMovies = new ConcurrentHashMap<Integer, String>();
decMovies.put(1, "Holiday Inn");
decMovies.put(24, "It's A Wonderful Life");
decMovies.put(25, "A Christmas Carol");
decMovies.put(26, "A Christmas Story");
decMovies.put(27, "Home Alone");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.DECEMBER), decMovies);
}


The above code snippet sets up an underlying data store with the movies of the day. The MovieOfTheDay class represents a resource and its class declaration is shown in the next code listing. The @Path annotation is significant because it tells the JAX-RS provider that this class represents a resource.

MovieOfTheDay.java Fragment: Class Declaration with @Path

/**
* Simple class that provides a movie for the provided month and day of that
* month.
*/
@Path("/movies")
public class MovieOfTheDay
{
// . . .
}


By specifying "/movies" as an argument to the @Path annotation, I am instructing the JAX-RS provider that this class represents a resource accessible in part by the URI portion "/movies".

With the @Path annotation provided on the class level, methods can be defined for the JAX-RS provider to tie to the uniform interface. The next code snippet demonstrates this for GET. There are actually two methods annotated with the @GET annotation. The first method, getMovieUsage(), simply returns a String specifying how the other GET method can be used. The other GET method is called getMovieOfTheDay(Integer,Integer) and it provides the movie of the day for the day provided as the two integer parameters (month and date in that month).

MovieOfTheDay.java Fragment: Two GET Methods

/**
* This method provides a "usage" string related to retrieving movie of the
* day information.
*
* @return String describing how to access movie of the day information.
*/
@GET
@Path("/")
@Produces("text/plain")
public String getMovieUsage()
{
return
"To see the movie of the day, provide URL with month and day: "
+ "\thttp://localhost:8080/rest/resources/movies/<<month>>/<<day>>";
}

/**
* Obtain the movie of the day as indicated by the provided month and date.
*
* @param month Month for which movie of the day is desired.
* @param date Date for which movie of the day is desired.
* @return Title of the movie of the day for provided month and date.
*/
@GET
@Path("/{month}/{date}")
@Consumes("text/plain")
@Produces("text/html")
public String getMovieOfTheDay(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date)
{
final Map<Integer, String> moviesOfTheMonth =
MOVIE_OF_THE_DAY.get(month-1);
final String movieOfTheDay = moviesOfTheMonth != null
? moviesOfTheMonth.get(date)
: "Fletch Lives!";
return movieOfTheDay != null
? generateSelectedMovieHtml(movieOfTheDay, month, date)
: generateSelectedMovieHtml("Fletch", month, date);
}


The @Path annotation was used on this entire class and then was again used in the previous code sample to annotate the two GET methods to add more to the overall path. In the first case, the usage method's path is defined simply as "/", meaning that it remains the same path as to the overall class. The second GET method, however, has an @Path annotation with "/month/date" specified as an argument. This means that the JAX-RS provider should match this method with a URI containing the class-level @Path's URI portion concatenated with this method's @Path's URI portion: movies/{month}/{date}. The curly braces indicate that the value is a placeholder. In this case, the {month} and {date} are placeholders for Integers (the types of the two parameters to the method that are annotated with @PathParam annotations that tie them directly to the placeholder names).

The code for the other methods on this HTTP-based uniform interface is similar @DELETE annotation makes it obvious which method is associated with deletion of a resource. In my case, I'm using the method associated with the @PUT annotation for both insert/create (new) behavior and for update/modify (change to existing) behavior. There has been controversy in the REST community about how closely (if at all) HTTP methods in HTTP-based REST applications should apply to the CRUD concept (Create/Read/Update/Delete) with the particularly contentious point being the relationship of HTTP methods PUT/POST to the CRUD operations of UPDATE/CREATE.

My current favorite treatment of the POST/PUT issue is John Calcote's blog post PUT or POST: The REST of the Story. For me, whether to use POST or PUT does come down to asking myself the question, "Is the modeled behavior idempotent?" If I answer "yes" (repeated calls always end up with same value/state), then I use PUT. If the modeled behavior is not idempotent (the same call leads to different results), then I use POST. As a side note, it is interesting that "Idempotence" was Eric Lippert's first covered word in his series Five-Dollar Words for Programmers.

Returning to the subject of JAX-RS and the uniform interface, here is a code snippet with the appropriate JAX-RS annotations for deleting and inserting/updating movies of the day.

MovieOfTheDay.java Fragment: DELETE and PUT Methods

/**
* Remove the movie of the day for the day whose month and date are provided
* as parameters.
*
* @param month Month of date for which movie is to be removed.
* @param date Date for which movie is to be removed.
* @return String representing movie of the day information was deleted.
*/
@DELETE
@Path("/{month}/{date}")
@Consumes("text/plain")
@Produces("text/html")
public String deleteMovieOfTheDay(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date)
{
final Map<Integer, String> moviesOfTheMonth =
MOVIE_OF_THE_DAY.get(month-1);
String movieRemoved = "";
if (moviesOfTheMonth != null)
{
movieRemoved = moviesOfTheMonth.remove(date);
}
return generateDeletedMovieHtml(movieRemoved);
}

/**
* Add a new entry or update an existing entry representing a movie of the day.
*
* @param month Month for the movie of the day.
* @param date Day of the month for the movie of the day.
* @param title Title of the movie of the day.
* @return HTML excerpt for movie of the day entry status.
*/
@PUT
@Path("/{month}/{date}/{title}")
@Consumes("text/plain")
@Produces("text/html")
public String addMovieOfTheDay(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date,
@PathParam("title") final String title)
{
final Map<Integer, String> moviesOfTheMonth =
MOVIE_OF_THE_DAY.get(month-1);
if (moviesOfTheMonth != null)
{
moviesOfTheMonth.put(date, title);
}
return generateAddedMovieHtml(title);
}


The code shown to this point in this post illustrates use of JAX-RS annotations @GET, @DELETE, and @PUT to expose methods in the Java class as methods roughly equivalent respectively to reading/retrieving, deleting, and adding/updating. Before moving onto screen snapshots showing these simple examples in action, I provide the entire class (including the example snippets above and the helper utility methods called from the examples above) here in one listing for convenience.

MovieOfTheDay.java: The Complete Class

package rmoug.td2010.rest;

import java.util.Calendar;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.PUT;
import javax.ws.rs.Produces;

/**
* Simple class that provides a movie for the provided month and day of that
* month.
*/
@Path("/movies")
public class MovieOfTheDay
{
/** Handle to logger. */
private static final Logger LOGGER = Logger.getLogger("rmoug.td2010.rest.MovieOfTheDay");

/** Overall simulated database of movies of the day. */
private static final Map<Integer, Map<Integer, String>> MOVIE_OF_THE_DAY;

static
{
MOVIE_OF_THE_DAY = new ConcurrentHashMap<Integer, Map<Integer, String>>();

final Map<Integer, String> janMovies = new ConcurrentHashMap<Integer, String>();
janMovies.put(10, "Trading Places");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.JANUARY), janMovies);

final Map<Integer, String> febMovies = new ConcurrentHashMap<Integer, String>();
febMovies.put(2, "Groundhog Day");
febMovies.put(13, "Casablanca");
febMovies.put(14, "Sleepless in Seattle");
febMovies.put(15, "How to Lose a Guy in 10 Days");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.FEBRUARY), febMovies);

final Map<Integer, String> marMovies = new ConcurrentHashMap<Integer, String>();
marMovies.put(16, "The Fugitive");
marMovies.put(17, "Darby O'Gill and the Little People");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.MARCH), marMovies);

final Map<Integer, String> aprMovies = new ConcurrentHashMap<Integer, String>();
aprMovies.put(25, "Raiders of the Lost Ark");
aprMovies.put(26, "Indiana Jones and the Temple of Doom");
aprMovies.put(27, "Indiana Jones and the Last Crusade");
aprMovies.put(28, "Indiana Jones and the Kingdom of the Crystal Skull");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.APRIL), aprMovies);

final Map<Integer, String> mayMovies = new ConcurrentHashMap<Integer, String>();
mayMovies.put(26, "Star Wars: Episode 1 - The Phantom Menace");
mayMovies.put(27, "Star Wars: Episode 2 - Attack of the Clones");
mayMovies.put(28, "Star Wars: Episode 3 - Revenge of the Sith");
mayMovies.put(29, "Star Wars: Episode 4 - A New Hope");
mayMovies.put(30, "Star Wars: Episode 5 - The Empire Strikes Back");
mayMovies.put(31, "Star Wars: Episode 6 - Return of the Jedi");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.MAY), mayMovies);

final Map<Integer, String> junMovies = new ConcurrentHashMap<Integer, String>();
junMovies.put(15, "The Great Outdoors");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.JUNE), junMovies);

final Map<Integer, String> julMovies = new ConcurrentHashMap<Integer, String>();
julMovies.put(4, "Independence Day");
julMovies.put(21, "Men in Black");
julMovies.put(22, "Men in Black 2");
julMovies.put(23, "I, Robot");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.JULY), julMovies);

final Map<Integer, String> augMovies = new ConcurrentHashMap<Integer, String>();
augMovies.put(10, "North by Northwest");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.AUGUST), augMovies);

final Map<Integer, String> sepMovies = new ConcurrentHashMap<Integer, String>();
sepMovies.put(1, "Dead Poets Society");
sepMovies.put(20, "Uncle Buck");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.SEPTEMBER), sepMovies);

final Map<Integer, String> octMovies = new ConcurrentHashMap<Integer, String>();
octMovies.put(28, "Psycho");
octMovies.put(29, "Sixth Sense");
octMovies.put(30, "Ghostbusters");
octMovies.put(31, "Young Frankenstein");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.OCTOBER), octMovies);

final Map<Integer, String> novMovies = new ConcurrentHashMap<Integer, String>();
novMovies.put(10, "The Italian Job");
novMovies.put(11, "Ocean's Eleven");
novMovies.put(12, "Ocean's Twelve");
novMovies.put(13, "Ocean's Thirteen");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.NOVEMBER), novMovies);

final Map<Integer, String> decMovies = new ConcurrentHashMap<Integer, String>();
decMovies.put(1, "Holiday Inn");
decMovies.put(24, "It's A Wonderful Life");
decMovies.put(25, "A Christmas Carol");
decMovies.put(26, "A Christmas Story");
decMovies.put(27, "Home Alone");
MOVIE_OF_THE_DAY.put(Integer.valueOf(Calendar.DECEMBER), decMovies);
}

/**
* This method provides a "usage" string related to retrieving movie of the
* day information.
*
* @return String describing how to access movie of the day information.
*/
@GET
@Path("/")
@Produces("text/plain")
public String getMovieUsage()
{
return
"To see the movie of the day, provide URL with month and day: "
+ "\thttp://localhost:8080/rest/resources/movies/<<month>>/<<day>>";
}

/**
* Obtain the movie of the day as indicated by the provided month and date.
*
* @param month Month for which movie of the day is desired.
* @param date Date for which movie of the day is desired.
* @return Title of the movie of the day for provided month and date.
*/
@GET
@Path("/{month}/{date}")
@Consumes("text/plain")
@Produces("text/html")
public String getMovieOfTheDay(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date)
{
final Map<Integer, String> moviesOfTheMonth =
MOVIE_OF_THE_DAY.get(month-1);
final String movieOfTheDay = moviesOfTheMonth != null
? moviesOfTheMonth.get(date)
: "Fletch Lives!";
return movieOfTheDay != null
? generateSelectedMovieHtml(movieOfTheDay, month, date)
: generateSelectedMovieHtml("Fletch", month, date);
}

/**
* Remove the movie of the day for the day whose month and date are provided
* as parameters.
*
* @param month Month of date for which movie is to be removed.
* @param date Date for which movie is to be removed.
* @return String representing movie of the day information was deleted.
*/
@DELETE
@Path("/{month}/{date}")
@Consumes("text/plain")
@Produces("text/html")
public String deleteMovieOfTheDay(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date)
{
final Map<Integer, String> moviesOfTheMonth =
MOVIE_OF_THE_DAY.get(month-1);
String movieRemoved = "";
if (moviesOfTheMonth != null)
{
movieRemoved = moviesOfTheMonth.remove(date);
}
return generateDeletedMovieHtml(movieRemoved);
}

/**
* Add a new entry or update an existing entry representing a movie of the day.
*
* @param month Month for the movie of the day.
* @param date Day of the month for the movie of the day.
* @param title Title of the movie of the day.
* @return HTML excerpt for movie of the day entry status.
*/
@PUT
@Path("/{month}/{date}/{title}")
@Consumes("text/plain")
@Produces("text/html")
public String addMovieOfTheDay(
@PathParam("month") final Integer month,
@PathParam("date") final Integer date,
@PathParam("title") final String title)
{
final Map<Integer, String> moviesOfTheMonth =
MOVIE_OF_THE_DAY.get(month-1);
if (moviesOfTheMonth != null)
{
moviesOfTheMonth.put(date, title);
}
return generateAddedMovieHtml(title);
}

/**
* Generate snippet of HTML related to selection of a movie.
*
* @param movieTitle Title of movie selected.
* @param movieMonth Month in which this was movie of the day.
* @param movieDay Day on which this was movie of the day.
* @return HTML describing selected movie of the day.
*/
private String generateSelectedMovieHtml(
final String movieTitle, final int movieMonth, final int movieDay)
{
final String movieOfTheDayBody =
"<p>The movie of the day for " + movieMonth + "/" + movieDay
+ " is '" + movieTitle + "'.</p>";
return generateHtmlTemplate("Movie of the Day", movieOfTheDayBody);
}

/**
* Generate snippet of HTML related to deletion of a movie.
*
* @param deletedMovieTitle Title of movie that was deleted.
* @return HTML describing movie that was deleted.
*/
private String generateDeletedMovieHtml(
final String deletedMovieTitle)
{
final String deletedMovieBody =
deletedMovieTitle != null
? "<p>The movie '" + deletedMovieTitle + "' was removed.</p>"
: "<p>No movie was deleted because no matching movie found.</p>";
return generateHtmlTemplate("Deleted Movie", deletedMovieBody);
}

/**
* Generate snippet of HTML related to added/updated movie information.
*
* @param addedMovieTitle Title of movie being added/updated.
* @return HTML describing added/updated movie.
*/
private String generateAddedMovieHtml(final String addedMovieTitle)
{
final String addedMovieBody =
addedMovieTitle != null
? "<p>The movie '" + addedMovieTitle + "' was added as the Movie of the Day."
: "<p>No movie added for movie of the day.</p>";
return generateHtmlTemplate("Added Movie", addedMovieBody);
}

/**
* Generic HTML template generated from provided HTML title and body.
*
* @param htmlTitle Title portion of HTML document to be generated.
* @param htmlBody Body portion of HTML document to be generated.
* @return HTML template populated with provided head and body.
*/
private String generateHtmlTemplate(
final String htmlTitle, final String htmlBody)
{
final StringBuilder builder = new StringBuilder();
builder.append("<html>")
.append("<head><title>")
.append(htmlTitle)
.append("</title></head>")
.append("<body><span style='font-weight: bold; font-size: 125%;")
.append("text-align: center;'>")
.append(htmlTitle)
.append("</span>");
builder.append(htmlBody);
builder.append("</body></html>");
return builder.toString();
}
}


In my introductory post on JAX-RS with GlassFish, I showed the web.xml file I used to deploy the JAX-RS-annotated class properly on GlassFish. That file is shown here again for convenience.

web.xml

?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>ServletAdaptor</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</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>

</web-app>


The previously referenced blog post provided relatively deep coverage of deploying the JAX-RS application to GlassFish v3, so I won't go into that again here. In that same post, I used a web browser as my simple REST client because I only used methods accessed via HTTP GET. In this post, however, I am also demonstrating DELETE and PUT, so I will use RESTClient. I have blogged previously on using RESTClient (in Easy Java-Based REST Testing Tools and in Using RESTClient with Java's HTTPServlet).

The first screen snapshot demonstrates running RESTClient with the method exposed via HTTP GET that provides usage information.



The next screen snapshot demonstrates the other GET-exposed method. This method provides an actual movie of the day.



In the previous two screen snapshots, the GET HTTP method was used as shown by the checked GET bullet. The next screen snapshot shows a movie of the day being added. In this case, the PUT radio bullet is checked on REST Client. The URL changes here to include a movie title.



I needed to specify the space in the movie title ("Mission Impossible") with %20 in this case. In a realistic, programmatic REST client, I'd make sure either the client framework or my own code ensured that spaces were translated for users rather than relying forcing the client to handle these %20 representations.

To call the deletion method, I simply need to change the HTTP method selected in the RESTClient GUI to "DELETE" and return to the URI with month and date integers. This is shown in the next screen snapshot.



If I run RESTClient GET with the same URI at this point, I'll get a message back saying that the movie of the day is Fletch because that is the default I use for any dates on which no movie of the day is assigned (and I had just deleted the movie assigned to that day).

JAX-RS supports REST in many more ways besides its making it easy to tie custom methods to specific HTTP methods, but I wanted to focus on how JAX-RS enables the uniform interface concept via the standard HTTP methods in this post. In future posts, I plan to consider how JAX-RS further helps with an HTTP-based REST application by handling other items such as content types and responses.