CHAPTER 15

image

RESTful Web Services

The SOAP web services stack (SOAP, WSDL, WS-*) described in the previous chapter, delivers interoperability in both message integration and RPC style. Still heavily used in the B2B industry, SOAP web services didn’t have the momentum expected of them on the Internet. With the rise of Web 2.0, new web frameworks have emerged and brought more agile web development and more reactive user interfaces. Mobile devices, with their native and web applications aggregating data, started to be part of our day-to-day lives. HTML 5 and JavaScript revolutionized our surfing experience. With all that, a new kind of web services has gained in popularity: RESTful web services. With that in mind, many key web players such as Amazon, eBay, Google, and Yahoo! have decommissioned their SOAP web services in favor of RESTful resource-oriented services.

Representational State Transfer (REST) is an architectural style based on how the Web works. Applied to services, it tries to put the Web back into web services. To design a RESTful web service, you need to know Hypertext Transfer Protocol (HTTP) and Uniform Resource Identifiers (URIs), and to observe a few design principles. This basically means that each unique URL is a representation of some object. You can interact with that object using an HTTP GET (to get its content), DELETE, POST (to create it), or PUT (to update the content).

RESTful architectures quickly became popular because they rely on a very robust transport protocol: HTTP. RESTful web services reduce the client/server coupling, making it much easier to evolve a REST interface over time without breaking existing clients. Like the protocol they are based on, RESTful web services are stateless and can make use of HTTP cache and proxy servers to help you handle high load and scale much better. Furthermore, they are easy to build as no special toolkit or WSDL-like is required.

The beginning of this chapter will cover a series of concepts to understand what REST is. Then it will put all these concepts together to write RESTful web services and to consume them.

Understanding RESTful Web Services

SOAP web services are meant to be able to use several transport protocols, HTTP being one of them. As a result, they only use a very small subset of its capabilities. On the other hand, RESTful web services are HTTP-centric and make the most of this very rich protocol. In the REST architectural style, every piece of information is a resource, and these resources are addressed using Uniform Resource Identifiers (URIs), typically links on the Web. The resources are acted on by using a set of simple, well-defined operations. The REST client-server architectural style is designed to exchange representations of these resources using a defined interface and protocol. These principles encourage RESTful applications to be simple and lightweight, and to have high performance.

A Web-Browsing Experience

Because REST is derived from the Web, to better understand it, I’ll start with a real-life web-browsing experience. How would you proceed to reach the list of Java technology books at Apress? You point your browser to the Apress web site: http://www.apress.com. Although it is likely that this page will not contain the exact information you’re seeking, you’re expecting it to give you access in one way or another to the Java book list. The home page offers a search engine on all Apress books, but there is also a book directory sorted by technologies. Click the Java node, and the hypermedia magic happens: here is the full list of Apress Java books at http://www.apress.com/java. You get a page showing you 20 books out of 191. If you want to have all of the books displayed on the page in a list format, click on http://www.apress.com/java?limit=all&mode=list.

Say you save the link in your favorite bookmark manager and, as you go through the book list, The Definitive Guide to Grails 2 by Graeme Rocher and Jeff Brown captures your attention. The hyperlink on the book title takes you to the book page (http://www.apress.com/9781430243779) where you can read the abstract, author biography, and so on, and you notice one of the books listed in the Related Titles section might be as valuable for your current project. You would like to compare Graeme Rocher’s book with Practical JRuby on Rails Web 2.0 Projects: Bringing Ruby on Rails to Java by Ola Bini (http://www.apress.com/9781590598818). Apress book pages give you access to a more concrete representation of its books in the form of online previews: open a preview, go through the table of contents, and make your choice.

Here is what we do on a day-to-day basis with our browsers. REST applies the same principles to your services where books, search results, table of contents, or book’s covers can be defined as resources.

Resources and URIs

Resources are given a central role in RESTful architectures. In the Web-Browsing Experience above you’ve noticed that a resource is anything the client might want to reference or interact with, any piece of information that might be worthwhile referencing in a hyperlink (a book, a search result, a table of contents . . .). A resource can be stored in a database, a file . . . anywhere it can be addressed. Avoid as much as possible exposing abstract concepts as resources; instead opt for simple objects. Some resources used in the CD-BookStore application could be:

  • A list of Java books
  • The book The Definitive Guide to Grails 2
  • Ola Bini’s résumé

Resources on the Web are identified by a URI (Uniform Resource Identifier). A URI is a unique identifier for a resource, made of a name and a structured address indicating where to find this resource. Various types of URIs exist: WWW addresses, Universal Document Identifiers, Universal Resource Identifiers, and finally the combination of Uniform Resource Locators (URLs) and Uniform Resource Names (URNs). Examples of resources and URIs are listed in Table 15-1.

Table 15-1. Examples of Resources and URIs

Resource URI
The catalog of Apress books http://www.apress.com/book/catalog
The cover of the Java EE 6 book http://www.apress.com/book/catalog/beginning-javaee6.jpg
Information about jobs at Apress http://www.apress.com/info/jobs
The weather in Paris for 2013 http://www.weather.com/weather/2013?location=Paris,France
Interesting photos on Flickr for January 1, 2013 http://www.flickr.com/explore/interesting/2013/01/01
Interesting photos on Flickr for the last 24 hours http://www.flickr.com/explore/interesting/24hours
The list of adventure movies http://www.movies.com/categories/adventure

URIs should be as descriptive as possible and should target a unique resource. Note that different URIs identifying different resources may lead to the same data. Actually, at some point in time, the list of the interesting photos uploaded to Flickr on 01/01/2013 was the same as the list of the uploaded photos in the last 24 hours, but the information conveyed by the two corresponding URIs is not the same. The standard format of a URI is as follow:

http://host:port/path?queryString#fragment

HTTP is the protocol, host is a DNS name or IP address and the port is optional. The path is a set of text segment delimited by the “/” character. Following this there is an optional query string (list of parameters represented as a name/value pair, each pair delimited with the “&” character. The last part delimited by “#” is the fragment that is used to point to a certain place in the document. The following URI points to the weather in Lisbon (Portugal) on the morning of the 1st January 2013:

http://www.weather.com:8080/weather/2013/01/01?location=Lisbon,Portugal&time=morning

Representations

So far I’ve explained what a resource is and where you can find it. But what is the representation of the resource the URI is pointing to? Are there several representations for a single resource? Actually you might want to get the representation of a resource as text, JSON, XML, PDF document, JPG image, or another data format. When the client deals with a resource, it is always through its representation; the resource itself remains on the server. Representation is any useful information about the state of a resource. For example, the list of Java books mentioned previously has at least two representations:

How do you choose between the different representations of a given resource? Two solutions are possible. The service could expose one URI per representation as shown above. However, the two URIs are really different and do not seem directly related. Following is a neater set of URIs:

The first URI is the default representation of the resource, and additional representations append their format extension to it: /csv (for text/csv), /xml, /pdf, and so on.

Another solution is to expose one single URI for all representations (e.g., http://www.apress.com/java) and to rely on the mechanism called content negotiation, which I’ll discuss in more detail a little later in this chapter. For instance, a URI could have a human-readable and a machine-processable representation.

Addressability

An important tenet to follow when designing RESTful web services is addressability. Your web service should make your application as addressable as possible, which means that every valuable piece of information in your application should be a resource and have a URI, making that resource easily accessible. The URI is the only piece of data you need to publish to make the resource accessible, so your business partner won’t have guesswork to do in order to reach the resource.

For example, you’re dealing with a bug in your application, and your investigations lead you to line 42 of the class CreditCardValidator.java as the place where the bug occurs. Because you’re not responsible for this domain of the application, you want to file an issue so a qualified person will take care of it. How would you point her to the incriminating line? You could say, “Go to line 42 of the class CreditCardValidator,” or, if your source code is addressable through a repository browser, you could save the URI of the line itself in the bug report. This raises the issue of defining the granularity of your RESTful resources in your application: it could be at the line, method, or class level, and so forth.

Unique URIs make your resources linkable, and, because they are exposed through a uniform interface, everyone knows exactly how to interact with them, allowing people to use your application in ways you would have never imagined.

Connectedness

In graph theory, a graph is called connected if every pair of distinct vertices in the graph can be connected through some path. It is said to be strongly connected if it contains a direct path from u to v and a direct path from v to u for every pair of vertices u,v. REST advocates that resources should be as connected as possible.

Once again, this daunting statement resulted from an examination of the success of the Web. Web pages embed links to navigate through pages in a logical and smooth manner and, as such, the Web is very well connected. If there is a strong relationship between two resources, they should be connected. REST states that web services should also take advantage of hypermedia to inform the client of what is available and where to go. It promotes the discoverability of services. From a single URI, a user agent accessing a well-connected web service could discover all available actions, resources, their various representations, and so forth.

For instance, when a user agent (e.g., a Web browser) looks up the representation of a CD (see Listing 15-1), this representation could have not only the names of the artist, but also a link, or a URI, to the biography. It’s up to the user agent to actually retrieve it or not. The representation could also link to other representations of the resource or available actions.

Listing 15-1.  A CD Representation Connected to Other Services

<cd>
  <title>Ella and Louis</title>
  <year ref=" http://music.com/year/1956 ">1956</year >
  <artist ref=" http://music.com/artists/123">Ella Fitzgerald</artist>
  <artist ref=" http://music.com/artists/456 ">Louis Armstrong</artist>
  <link rel="self" type="text/json" href=" http://music.com/album/789 "/>
  <link rel="self" type="text/xml" href=" http://music.com/album/789 "/>
  <link rel=" http://music.com/album/comments " type="text/xml"
       href=" http://music.com/album/789/comments "/>
</cd>

Another crucial aspect of the hypermedia principle is the state of the application, which must be led by the hypermedia. In short, the fact that the web service provides a set of links enables the client to move the application from one state to the next by simply following a link.

In the preceding XML snippet, the client could change the state of the application by commenting on the album. The list of comments on the album is a resource addressable with the URI http://music.com/album/789/comments. Because this web service uses a uniform interface, once the client knows the URI format, the available content types and the data format, it will know exactly how to interact with it: a GET will retrieve the list of existing comments, a PUT will update the comment, and so on. From this single initial request, the client can take many actions: the hypermedia drives the state of the application.

Uniform Interface

One of the main constraints that make an architecture RESTful is the use of a uniform interface to manage your resources. Pick whatever interface suits you, but use it in the same way across the board, from resource to resource, from service to service. Never stray from it or alter the original meaning. By using a uniform interface, “the overall system architecture is simplified and the visibility of interactions is improved” (Roy Thomas Fielding, Architectural Styles and the Design of Network-based Software Architectures). Your services become part of a community of services using the exact same semantic.

The de facto web protocol is HTTP, which is a document-based standardized request/response protocol between a client and a server. HTTP is the uniform interface of RESTful web services. Web services built on SOAP, WSDL, and other WS-* standards also use HTTP as the transport layer, but they leverage only a very few of its capabilities (as SOAP web services can also use other transport protocols such as JMS). You have to discover the semantic of the service by analyzing the WSDL and then invoke the right methods. RESTful web services have a uniform interface (HTTP methods and URIs), so, once you know where the resource is (URI), you can invoke the HTTP method (GET, POST, etc.).

In addition to familiarity, a uniform interface promotes interoperability between applications; HTTP is widely supported, and the number of HTTP client libraries guarantees that you won’t have to deal with communication issues.

Statelessness

The last feature of REST is statelessness, which means that every HTTP request happens in complete isolation, as the server should never keep track of requests that were executed before. For the sake of clarity, resource state and application state are usually distinguished. The resource state must live on the server and is shared by everybody, while the application state must remain on the client and is its sole property. Going back to the example in Listing 15-1, the application state is that the client has fetched a representation of the album Ella and Louis, but the server should not hold onto this information. The resource state is the album information itself; the server should obviously maintain this information. The client may change the resource state. If the shopping cart is a resource with restricted access to just one client, the application needs to keep track of the shopping cart ID in the client session.

Statelessness comes with many advantages such as better scalability: no session information to handle, no need to route subsequent requests to the same server (load-balancing), failure handling (e.g., service interruptions), and so on. If you need to keep state, the client has to do extra work to store it.

HTTP

HTTP, a protocol for distributed, collaborative, hypermedia information systems, led to the establishment of the World Wide Web together with URIs, HTML, and the first browsers. Coordinated by the World Wide Web Consortium (W3C) and the Internet Engineering Task Force (IETF), HTTP is the result of several Requests For Comment (RFC), notably RFC 216, which defines HTTP 1.1.

Request and Response

HTTP is based on requests and responses exchanged between a client and a server. A client sends a request to a server and expects an answer (see Figure 15-1). The messages exchanged are made of an envelope and a body, also called a payload or entity.

9781430246268_Fig15-01.jpg

Figure 15-1. HTTP request and response

When you navigate on the APress web site you only see the web pages, not the technical details of the HTTP request and response. To have an idea of what’s happening behind the scenes, you can use another tool such as cURL. For instance, here is a request sent to the server when you go to http://www.apress.com/java?limit=all&mode=list:

$ curl -v –X GEThttp://www.apress.com/java?limit=all&mode=list
> GET /java?limit=all&mode=list HTTP/1.1
> User-Agent: curl/7.23.1 (x86_64-apple-darwin11.2.0) libcurl/7.23.1 zlib/1.2.5
> Host: www.apress.com
> Accept: */*

This request has several pieces of information sent from the client to the server:

  • The HTTP method, here GET
  • The path, here /java?limit=all&mode=list
  • Several other request headers (User-Agent)

Notice that there is no body as part of the request. Actually, a GET never has a body. To this request, the server will send the following response:

< HTTP/1.1 200 OK
< Date : Sat, 17 Nov 2012 17:42:15 GMT
< Server: Apache/2.2.3 (Red Hat)
< X-Powered-By: PHP/5.2.17
< Vary: Accept-Encoding,User-Agent
< Transfer-Encoding: chunked
< Content-Type : text/html; charset=UTF-8
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
  <title>
  ...

This response is made up of the following:

  • A response code: In this case, the response code is 200-OK.
  • Several response headers: In the preceding code, the response headers are Date, Server, Content-Type. Here the content type is text/html, but it could be any other media such as XML (application/xml) or images (image/jpeg).
  • Entity body, or representation: The content of the returned web page is the entity body in this example (here I just showed a fragment of an HTML page).

image Note  cURL (http://curl.haxx.se/) is a command-line tool for transferring files with URL syntax via protocols such as HTTP, FTP, SFTP, SCP, and many more. You can send HTTP commands, change HTTP headers, and so on. It is a good tool for simulating a user’s actions at a web browser.

Headers

HTTP header fields are components of the message header of requests and responses. Header fields are colon-separated name-value pairs in clear-text string format, terminated by a carriage return and line feed character sequence. A core set of fields is standardized by the Internet Engineering Task Force (IETF) and must be implemented by all HTTP-compliant protocol implementations. But additional field names may be defined by each application if needed. Table 15-2 lists a subset of common defined header values that you can find either in the request or response.

Table 15-2. Subset of Common Defined Header Values

Header Name Description
Accept Content-Types that are acceptable (e.g., text/plain)
Accept-Charset Character sets that are acceptable (e.g., utf-8)
Accept-Encoding Acceptable encodings (e.g., gzip, deflate)
Accept-Language Acceptable languages for response (en-US)
Cookie An HTTP cookie previously sent by the server
Content-Length The length of the request body in bytes
Content-Type The MIME type of the body of the request (e.g., text/xml)
Date The date and time that the message was sent
ETag An identifier for a specific version of a resource (e.g., 8af7ad3082f20958)
If-Match Only performs the action if the client supplied entity matches the same entity on the server
If-Modified-Since Allows a 304-Not Modified to be returned if content is unchanged since a date
User-Agent The user agent string of the user agent (e.g., Mozilla/5.0)

HTTP Methods

The Web consists of well-identified resources linked together and accessed through simple HTTP requests. The main types of requests standardized in HTTP are GET, POST, PUT, DELETE. These are also called verbs, commands, or methods. HTTP defines four other methods that are less frequently used: HEAD, TRACE, OPTIONS, CONNECT.

GET

GET is a simple read that requests a representation of a resource. GET should be implemented in a safe way, meaning it shouldn’t change the state of the resource. In addition, GET must be idempotent, which means it must leave the resource in the same state when called once, twice, or more. Safety and idempotence bring greater stability. When a client does not get a response (e.g., due to a network failure), it might renew its requests, and those new requests will expect the same answer it should have received originally, without corrupting the resource state on the server.

POST

Given a representation (text, XML, etc.), calling a POST method creates a new resource identified by the requested URI. Examples of POST could be appending a message to a log file, a comment to a blog, a book to a booklist, and so on. Consequently, POST is not safe (the resource state is updated) nor idempotent (sending the request twice will result in two new subordinates). If a resource has been created on the origin server, the response should be status 201-Created. Most modern browsers generate only GET and POST requests.

PUT

A PUT request is intended to update the state of the resource stored under a given URI. If the request URI refers to a nonexistent resource, the resource will be created under this same URI. Examples of PUT could be updating the price of a book or the address of a customer. PUT is not safe (because the state of the resource gets updated), but it is idempotent: you can send the same PUT request many times, and the final resource state will always be the same.

DELETE

A DELETE request deletes the resource. The response to a DELETE may be a status message as part of the body or no status at all. DELETE is also idempotent but not safe.

Others

As mentioned previously, other HTTP methods exist, even if they are less used:

  • HEAD is identical to GET except that the server doesn’t return a message body in the response. HEAD might be useful for checking the validity of a link or the size of an entity without transferring it.
  • When a server receives a TRACE request from the client, it echoes back the received request. This can be useful to see what intermediate servers, proxies or firewalls are adding or changing in the request.
  • OPTIONS represents a request for information about the communication options available on the request/response chain identified by the URI. This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating resource retrieval.
  • CONNECT is used in conjunction with a proxy that can dynamically switch to being a tunnel (a technique by which the HTTP protocol acts as a wrapper for various network protocols).

Content Negotiation

Content negotiation, described in section 12 of the HTTP standard, is defined as “the process of selecting the best representation for a given response when there are multiple representations available.” Clients’ needs, desires, and capabilities vary; the best representation for a mobile-device user in Japan might not be the best for a feed-reader application in the United States.

Content negotiation is based on, but not limited to, the HTTP request headers Accept, Accept-Charset, Accept-Encoding, Accept-Language, and User-Agent. For example, to get the CSV representation of Apress Java books, the client application (the user agent) will request http://www.apress.com/java with a header Accept set to text/csv. You could also imagine that, based on the Accept-Language header, the server selects the proper CSV document to match the corresponding language (Japanese or English).

Content Types

HTTP uses Internet media types (originally called MIME types) in the Content-Type and Accept header fields in order to provide open and extensible data typing and type negotiation. Internet media types are divided into five discrete, top-level categories: text, image, audio, video, and application. These types are further divided into several subtypes (text/plain, text/xml, text/xhtml, etc.). Some of the most common public content types are as follows:

  • text/plain: This is the default content type, as it’s used for simple text messages.
  • text/html: Very commonly used in our browsers, this content-type informs the user-agent that the content is an HTML page.
  • image/gif, image/jpeg, image/png: This image top-level media type requires a display device (such as a graphical display, a graphics printer, etc.) to view the information.
  • text/xml, application/xml: Format used for XML exchanges.
  • application/json: JavaScript Object Notation (JSON) is a lightweight data-interchange text format independent of the programming language (see Chapter 12).

Status Codes

Each time a response is received, an HTTP code is associated with it. The specification defines around 60 status codes. The Status-Code element is a three-digit integer that describes the context of a response and is part of the response envelope. The first digit specifies one of five classes of response:

  • 1xx: Informational. The request was received, and the process is continuing.
  • 2xx: Success. The action was successfully received, understood, and accepted.
  • 3xx: Redirection. Further action must be taken in order to complete the request.
  • 4xx: Client Error. The request contains bad syntax or cannot be fulfilled.
  • 5xx: Server Error. The server failed to fulfill an apparently valid request.

Table 15-3 lists some status codes you might have already come across.

Table 15-3. Subset of HTTP Status Code

Status Code Description
100-Continue The server has received the request headers and the client should proceed to send the request body
101-Switching Protocols The requester has asked the server to switch protocols and the server is acknowledging that it will do so
200-OK The request has succeeded. The entity body, if any, contains the representation of the resource
201-Created The request has been fulfilled and resulted in a new resource being created
204-No Content The server successfully processed the request, but is not returning any content
206-Partial Content The server is delivering only part of the resource due to a range header sent by the client
301-Moved Permanently The requested resource has been assigned a new, permanent URI and any future reference to this resource should use one of the returned URIs
304-Not Modified Indicates the resource has not been modified since last requested
307-Temporary Redirect The request should be repeated with another URI; however, future requests should still use the original URI
308-Permanent Redirect The request, and all future requests should be repeated using another URI
400-Bad Request The request cannot be fulfilled due to bad syntax
401-Unauthorized Similar to 403 but when authentication is required and has failed
403-Forbidden The request was valid, but the server is refusing to respond to it
404-Not Found The server has not found anything matching the request URI
405-Method Not Allowed A request was made using a request method not supported by the resource
406-Not Acceptable The requested resource is only capable of generating content not acceptable according to the Accept headers sent in the request
500-Internal Server Error The server encountered an unexpected condition that prevented it from fulfilling the request
501-Not Implemented The server either does not recognize the request method, or it lacks the ability to fulfill the request
503-Service Unavailable The server is currently unavailable (because it is overloaded or down for maintenance); generally, this is a temporary state
505-HTTP Version Not Supported The server does not support the HTTP protocol version used in the request

Caching and Conditional Requests

In most distributed systems, caching is crucial. Caching aims at improving performance by avoiding unnecessary requests or by reducing the amount of data in responses. HTTP provides mechanisms to allow caching and make sure cached data is correct. But, if the client decides not to use any caching mechanism, it will always need to request data even if it hasn’t been modified since the last request.

When a response to a GET is sent, it could include a Last-Modified header indicating the time that the resource was last modified. The next time the user agent requests this resource, it can pass this date in the If-Modified-Since header. The web server (or a proxy) will compare this date with the latest modification date. If the date sent by the user agent is equal or newer, a 304-Not Modified status code with no response body is returned. Otherwise, the requested operation is performed or forwarded.

But dates can be difficult to manipulate and imply that all the interacting agents are, and stay, synchronized. The ETag response header solves this issue. The easiest way to think of an ETag is as an MD5 or SHA1 hash of all the bytes in a representation; if just one byte in the representation changes, the ETag will change.

Figure 15-2 gives an example of how to use ETags. To get a book resource, you use the GET action and give it the URI of the resource (GET /book/12345). The server will return a response with the XML representation of the book, a 200 OK status code, and a generated ETag. The second time you ask for the same resource, if you pass the ETag as a value in an If-None-Match header, the server will not send the representation of the resource assuming the resource has not actually changed since the earlier request. This will instead return a 304-Not Modified status code informing the client that the resource hasn’t changed since last access.

9781430246268_Fig15-02.jpg

Figure 15-2. Using caching and the 304 Not Modified status code

Requests using the HTTP headers If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match, and If-Range are said to be conditional. Conditional requests can save bandwidth and CPU (on both the server and client side) by avoiding unnecessary round-trips or data transmissions. The If-* headers are most often used for GET and PUT requests.

From the Web to Web Services

We all use the Web and know how it works, so why should web services behave differently? After all, services also often exchange uniquely identified resources, linked with others like hyperlinks. Web architecture has proven its scalability for years; why reinvent the wheel? To create, update, and delete a book resource, why not use the common HTTP verbs? For example:

By using HTTP verbs, we have access to all possible Create, Read, Update, Delete (CRUD) actions on a resource.

WADL

While SOAP-based services rely on WSDL to describe the format of possible requests for a given web service, Web Application Description Language (WADL) is used to expose the possible interactions with a given RESTful web service. It eases client development, which can load and interact directly with the resources. WADL was submitted to the W3C but the consortium has no current plans to standardize it because it is not widely supported. Listing 15-2 shows you what it can look like.

Listing 15-2.  WADL Defining Several Operations That Can Be Invoked on a Resource

<application xmlns=" http://wadl.dev.java.net/2009/02 ">
  <doc xmlns:jersey=" http://jersey.java.net/ " jersey:generatedBy="Jersey: 2.0"/>
 
  <resources base=" http://www.apress.com/ ">
 
      <resource path=" {id} ">
        <param name="id" style="template" type="xs:long"/>
        <method name=" GET ">
          <response>
            <representation element="book" mediaType="application/xml"/>
            <representation element="book" mediaType="application/json"/>
          </response>
        </method>
        <method name=" DELETE "/>
      </resource>
 
      <resource path=" book ">
        <method name=" GET ">
          <response>
            <representation element="book" mediaType=" application/xml "/>
            <representation element="book" mediaType=" application/json "/>
          </response>
        </method>
      </resource>
 
  </resources>
</application>

Listing 15-2 describes a resource root (http://www.apress.com/) to where you can pass an id ({id}) to GET or DELETE a book. Another resource allows you to GET all the books from APress in JSON or XML.

RESTful Web Services Specifications Overview

Contrary to SOAP and the WS-* stack, which rely on W3C standards, REST has no standard and is just a style of architecture with design principles. REST applications rely heavily on many other standards:

  • HTTP
  • URI, URL
  • XML, JSON, HTML, GIF, JPEG, and so forth (resource representations)

The Java side has been specified through JAX-RS (Java API for RESTful Web Services), but REST is like a design pattern: a reusable solution to a common problem that can be implemented by several languages.

A Brief History of REST

The term REST was first introduced by Roy Thomas Fielding in Chapter 5 of his PhD thesis, Architectural Styles and the Design of Network-based Software Architectures (University of California, Irvine, 2000, http://www.ics.uci.edu/∼fielding/pubs/dissertation/top.htm). The dissertation is a retrospective explanation of the architecture chosen to develop the Web. In his thesis, Fielding takes a look at the parts of the World Wide Web that work very well and extracts design principles that could make any other distributed hypermedia system—whether related to the Web or not—as efficient.

The original motivation for developing REST was to create an architectural model of the Web. Roy T. Fielding is also one of the authors of the HTTP specification, so it’s not surprising that HTTP fits quite well the architectural design he described in his dissertation.

Java API for RESTful Web Services

To write RESTful web services in Java, you would only need a client and a server that support HTTP. Any browser and an HTTP servlet container will do the job at the cost of some XML configuration and glue-code pain to parse HTTP requests and responses. Once completed, this technical code would be barely readable and maintainable. This is where JAX-RS comes to the rescue. As you’ll see, with just a few annotations you get the full power of invoking HTTP resources and parsing them.

The first version of the JAX-RS specification (JSR 311), finalized in October 2008, defined a set of APIs that promoted the REST architecture style. But it only covered the server-side aspect of REST. With Java EE 7, JAX-RS has been updated to a 2.0 version and now defines a client API among other novelties.

What’s New in JAX-RS 2.0?

JAX-RS 2.0 (JSR 339) is a major release focusing on integration with Java EE 7 and its new features. The major new features of JAX-RS 2.0 are as follows:

  • A client API was missing from JAX-RS 1.x so each implementation defined its own proprietary API. JAX-RS 2.0 fills this gap with a fluent, low-level, request building API.
  • Like SOAP handlers or Managed Bean interceptors, JAX-RS 2.0 now has filters and interceptors so you can intercept request and response and do some processing.
  • With the new asynchronous processing you can now implement long-polling interfaces or server-side push.
  • Integration with Bean Validation has been achieved so you can constrain your RESTful web services.

Table 15-4 lists the main packages defined in JAX-RS.

Table 15-4. Main JAX-RS Packages

Package Description
javax.ws.rs High-level interfaces and annotations used to create RESTful web service
javax.ws.rs.client Classes and interfaces of the new JAX-RS client API
javax.ws.rs.container Container-specific JAX-RS API
javax.ws.rs.core Low-level interfaces and annotations used to create RESTful web resources
javax.ws.rs.ext APIs that provide extensions to the types supported by the JAX-RS API

Reference Implementation

Jersey is the reference implementation of JAX-RS. It is an open source project under dual CDDL and GPL licenses. Jersey also provides a specific API so that you can extend Jersey itself.

Other implementations of JAX-RS are also available such as CXF (Apache), RESTEasy (JBoss), and Restlet (a senior project that existed even before JAX-RS was finalized).

Writing RESTful Web Services

Some of the low-level concepts (such as the HTTP protocol) might have you wondering how the code would look when developing a RESTful web service. The good news is that you don’t have to write plumbing code to digest HTTP requests, nor create HTTP responses by hand. JAX-RS is a very elegant API allowing you to describe a RESTful web service with only a few annotations. RESTful web services are POJOs that have at least one method annotated with @javax.ws.rs.Path. Listing 15-3 shows a typical resource.

Listing 15-3.  A Simple Book RESTful Web Service

@Path ("/book")
public class BookRestService {
 
    @GET
    @Produces ("text/plain")
    public String getBookTitle() {
        return "H2G2";
    }
}

The BookRestService is a Java class annotated with @Path, indicating that the resource will be hosted at the URI path /book. The getBookTitle() method is marked to process HTTP GET requests (using @GET annotation) and produces text (the content is identified by the MIME Media text/plain; I could have also used the constant MediaType.TEXT_PLAIN). To access this resource, you need an HTTP client such as a browser to point to the URL http://www.myserver.com/book.

JAX-RS is HTTP-centric by nature and has a set of clearly defined classes and annotations to deal with HTTP and URIs. A resource can have several representations, so the API provides support for a variety of content types and uses JAXB to marshall and unmarshall XML representations from/into objects. JAX-RS is also independent of the container, so resources can be deployed in GlassFish, of course, but also in a variety of servlet containers.

Anatomy of a RESTful Web Service

From Listing 15-3, you can see that the REST service doesn’t implement any interface nor extend any class; the only mandatory annotation to turn a POJO into a REST service is @Path. JAX-RS relies on configuration by exception, so it has a set of annotations to configure the default behavior. Following are the requirements to write a REST service:

  • The class must be annotated with @javax.ws.rs.Path (in JAX-RS 2.0 there is no XML equivalent to meta-data as there is no deployment descriptor).
  • The class must be defined as public, and it must not be final or abstract.
  • Root resource classes (classes with a @Path annotation) must have a default public constructor. Non-root resource classes do not require such a constructor.
  • The class must not define the finalize() method.
  • To add EJB capabilities to a REST service, the class has to be annotated with @javax.ejb.Stateless or @javax.ejb.Singleton (see Chapter 7).
  • A service must be a stateless object and should not save client-specific state across method calls.

CRUD Operations on a RESTful Web Service

Listing 15-3 shows how to write a very simple REST service that returns a String. But most of the time you need to access a database, retrieve or store data in a transactional manner. For this you can have a REST service and add stateless session beans functionalities by adding the @Stateless annotation. This will allow transactional access to a persistent layer (JPA entities), as shown in Listing 15-4.

Listing 15-4.  A Book RESTful Web Service Creating, Deleting, and Retrieving Books from the Database

@Path("book")
@Stateless
public class BookRestService {
 
  @Context
  private UriInfo uriInfo;
  @PersistenceContext(unitName = "chapter15PU")
  private EntityManager em ;
 
  @GET
  @Produces(MediaType.APPLICATION_XML)
  public Books getBooks() {
    TypedQuery<Book> query = em.createNamedQuery (Book.FIND_ALL, Book.class);
    Books books = new Books(query.getResultList());
    return books;
  }
 
  @POST
  @Consumes(MediaType.APPLICATION_XML)
  public Response createBook(Book book) {
    em.persist (book);
    URI bookUri = uriInfo.getAbsolutePathBuilder().path(book.getId().toString()).build();
    return Response.created(bookUri).build();
  }
 
  @DELETE
  @Path("{id}")
  public Response deleteBook(@PathParam("id") Long bookId) {
    em.remove (em.find(Book.class, bookId));
    return Response.noContent().build();
  }
}

The code in Listing 15-4 represents a REST service that can consume and produce an XML representation of a book. The getBooks() method retrieves the list of books from the database and returns an XML representation (using content negotiation) of this list, accessible through a GET method. The createBook() method takes an XML representation of a book and persists it to the database. This method is invoked with an HTTP POST and returns a Response with the URI (bookUri) of the new book as well as the created status. The deleteBook method takes a book id as a parameter and deletes it from the database.

The code in Listing 15-4 follows a very simple JAX-RS model and uses a set of powerful annotations. Let’s now take a deeper look at all the concepts shown in the code.

URI Definition and Binding URIs

The @Path annotation represents a relative URI that can annotate a class or a method. When used on classes, it is referred to as the root resource, providing the root of the resource tree and giving access to subresources. Listing 15-5 shows a REST service that can be access at http://www.myserver.com/items. All the methods of this service will have /items as root.

Listing 15-5.  Root Path to an Item Resource

@Path ("/items")
public class ItemRestService {
 
  @GET
  public Items getItems() {
    // ...
  }
}

You can then add subpaths to your methods, which can be useful to group together common functionalities for several resources as shown in Listing 15-6 (you may ignore for the moment the @GET, @POST, and @DELETE annotations in the listing, as they will be described later in the “HTTP Method Matching” section).

Listing 15-6.  Several Subpaths in the ItemRestService

@Path(" /items ")
public class ItemRestService {
 
  @GET
  public Items getItems() {
    // URI : /items
  }
 
  @GET
  @Path(" /cds ")
  public CDs getCDs() {
    // URI : /items/cds
  }
 
  @GET
  @Path(" /books ")
  public Books getBooks() {
    // URI : /items/books
  }
 
  @POST
  @Path(" /book ")
  public Response createBook(Book book) {
    // URI : /items/book
  }
}

Listing 15-6 represents a RESTful web service that will give you methods to get all the items (CDs and books) from the CD-BookStore Application. When requesting the root resource /items, the only method without sub @Path will be selected (getItems()). Then, when @Path exists on both the class and method, the relative path to the method is a concatenation of both. For example, to get all the CDs, the path will be /items/cds. When requesting /items/books, the getBooks() method will be invoked. To create a new book you need to point at /items/book.

If @Path("/items") only existed on the class, and not on any methods, the path to access each method would be the same. The only way to differentiate them would be the HTTP verb (GET, PUT, etc.) and the content negotiation (text, XML, etc.), as you’ll later see.

Extracting Parameters

Having nice URIs by concatenating paths to access your resource is very important in REST. But paths and subpaths are not enough: you also need to pass parameters to your RESTful web services, extract and process them at runtime. Listing 15-4 showed how to get a parameter out of the path with @javax.ws.rs.PathParam. JAX-RS provides a rich set of annotations to extract the different parameters that a request could send (@PathParam, @QueryParam, @MatrixParam, @CookieParam, @HeaderParam, and @FormParam).

Listing 15-7 shows how the @PathParam annotation is used to extract the value of a URI template parameter. A parameter has a name and is represented by a variable between curly braces or by a variable that follows a regular expression. The searchCustomers method takes any String parameter while getCustomerByLogin only allows lowercase/uppercase alphabetical letters ([a-zA-Z]*) and getCustomerById only digits (\d+).

Listing 15-7.  Extracting Path Parameters and Regular Expressions

@Path("/customer")
@Produces(MediaType.APPLICATION_JSON)
public class CustomerRestService {
 
  @Path("search/ {text} ")
  public Customers searchCustomers( @PathParam("text") String textToSearch) {
    // URI : /customer/search/ smith
  }
 
  @GET
  @Path(" {login: [a-zA-Z]*} ")
  public Customer getCustomerByLogin( @PathParam("login") String login) {
    // URI : /customer/ foobarsmith
  }
 
  @GET
  @Path(" {customerId : \d+} ")
  public Customer getCustomerById( @PathParam("customerId") Long id) {
    // URI : /customer/ 12345
  }
}

The @QueryParam annotation extracts the value of a URI query parameter. Query parameters are key/value pairs separated by an & symbol such as http://www.myserver.com/customer?zip=75012&city=Paris. The @MatrixParam annotation acts like @QueryParam, except it extracts the value of a URI matrix parameter (; is used as a delimiter instead of ?). Listing 15-8 shows how to extract both query and matrix parameters from URIs.

Listing 15-8.  Extracting Query and Matrix Parameters

@Path("/customer")
@Produces(MediaType.APPLICATION_JSON)
public class CustomerRestService {
 
  @GET
  public Customers getCustomersByZipCode( @QueryParam("zip") Long zip,
                                              @QueryParam("city") String city) {
    // URI : /customer ?zip=75012&city=Paris
  }
 
  @GET
  @Path("search")
  public Customers getCustomersByName( @MatrixParam("firstname") String firstname,
                                           @MatrixParam("surname") String surname) {
    // URI : /customer/search ;firstname=Antonio;surname=Goncalves
  }
}

Two other annotations are related to the innards of HTTP, things you don’t see directly in URIs: cookies and HTTP headers @CookieParam extracts the value of a cookie, while @HeaderParam extracts the value of a header field. Listing 15-9 extracts the session ID from the cookie and the User Agent from the HTTP header.

Listing 15-9.  Extracting Values From the Cookie and HTTP Header

@Path("/customer")
@Produces(MediaType.TEXT_PLAIN)
public class CustomerRestService {
 
  @GET
  public String extractSessionID( @CookieParam("sessionID") String sessionID) {
    // ...
  }
 
  @GET
  public String extractUserAgent( @HeaderParam("User-Agent") String userAgent) {
    // ...
  }
}

The @FormParam annotation specifies that the value of a parameter is to be extracted from a form in a request entity body. @FormParam is not required to be supported on fields or properties.

With all these annotations, you can add a @DefaultValue annotation to define the default value for a parameter you’re expecting. The default value is used if the corresponding parameter is not present in the request. Listing 15-10 sets default values to query and matrix parameters. For example, in the method getCustomersByAge, if the query parameter age is not in the request, the default value is set to 50.

Listing 15-10.  Defining Default Values

@Path("/customer")
public class CustomerRestService {
 
  @GET
  public Customers getCustomersByAge( @DefaultValue("50") @QueryParam("age") int age) {
    // ...
  }
 
  @GET
  public Customers getCustomersByCity( @DefaultValue("Paris") @MatrixParam("city")
                                                                    String city) {
    // ...
  }
}

Consuming and Producing Content Types

With REST, the same resource can have several representations; a book can be represented as a web page, a PDF, or an image showing the book cover. JAX-RS specifies a number of Java types that can represent a resource such as String, InputStream and JAXB beans. The @javax.ws.rs.Consumes and @javax.ws.rs.Produces annotations may be applied to a resource where several representations are possible. It defines the media types of the representation exchanged between the client and the server. JAX-RS has a javax.ws.rs.core.MediaType class that acts like an abstraction for a MIME type. It has several methods and defines the constants listed in Table 15-5.

Table 15-5. MIME Types Defined in MediaType

Constant name MIME type
APPLICATION_ATOM_XML “application/atom+xml”
APPLICATION_FORM_URLENCODED “application/x-www-form-urlencoded”
APPLICATION_JSON “application/json”
APPLICATION_OCTET_STREAM “application/octet-stream”
APPLICATION_SVG_XML “application/svg+xml”
APPLICATION_XHTML_XML “application/xhtml+xml”
APPLICATION_XML “application/xml”
MULTIPART_FORM_DATA “multipart/form-data”
TEXT_HTML “text/html”
TEXT_PLAIN “text/plain”
TEXT_XML “text/xml”
WILDCARD “*/*”

Using the @Consumes and @Produces annotations on a method overrides any annotations on the resource class for a method argument or return type. In the absence of either of these annotations, support for any media type (*/*) is assumed. By default, CustomerRestService produces plain text representations that are overridden in some methods (see Listing 15-11). Note that the getAsJsonAndXML produces an array of representations (XML or JSON).

Listing 15-11.  A Customer Resource with Several Representations

@Path("/customer")
@Produces(MediaType.TEXT_PLAIN)
public class CustomerRestService {
 
  @GET
  public Response getAsPlainText() {
    // ...
  }
 
  @GET
  @Produces(MediaType.TEXT_HTML)
  public Response getAsHtml() {
    // ...
  }
 
  @GET
  @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
  public Response getAsJsonAndXML() {
    // ...
  }
 
  @PUT
  @Consumes(MediaType.TEXT_PLAIN)
  public void putName(String customer) {
    // ...
  }
}

If a RESTful web service is capable of producing more than one media type, the targeted method will correspond to the most acceptable media type, as declared by the client in the Accept header of the HTTP request. For example, if the Accept header is:

Accept: text/plain

and the URI is /customer, the getAsPlainText() method will be invoked. But the client could have used the following HTTP header:

Accept: text/plain; q=0.8, text/html

This header declares that the client can accept media types of text/plain and text/html but prefers the latter using the quality factor (or preference weight) of 0.8 (“I prefer text/html, but send me text/plain if it is the best available after an 80% markdown in quality”). By including such header and pointing at the /customer URI, the getAsHtml() method will be invoked.

Returned Types

So far you’ve seen mostly how to invoke a method (using parameters, media type, HTTP methods . . .) without caring about the returned type. What can a RESTful web service return? Like any Java class, a method can return any standard Java type, a JAXB bean or any other object as long as it has a textual representation that can be transported over HTTP. In this case, the runtime determines the MIME type of the object being returned and invokes the appropriate Entity Provider (see later) to get its representation. The runtime also determines the appropriate HTTP return code to send to the consumer (204-No Content if the resource method's return type is void or null; 200-OK if the returned value is not null). But sometimes you want finer control of what you are returning: the response body (a.k.a. an entity) of course, but also the response code and/or response headers or cookies. That’s when you return a Reponse object. It is a good practice to return a javax.ws.rs.core.Response with an entity since it would guarantee a return content type. Listing 15-12 shows you different return types.

Listing 15-12.  A Customer Service Returning Data Types, a JAXB Bean, and a Response

@Path("/customer")
public class CustomerRestService {
 
  @GET
  public String getAsPlainText() {
    return new Customer("John", "Smith", "[email protected]", "1234565").toString();
  }
 
  @GET
  @Path("maxbonus")
  public Long getMaximumBonusAllowed() {
    return 1234L;
  }
 
  @GET
  @Produces(MediaType.APPLICATION_XML)
  public Customer getAsXML() {
    return new Customer("John", "Smith", "[email protected]", "1234565");
  }
 
  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public Response getAsJson() {
    return Response.ok(new Customer("John", "Smith", "[email protected]", "1234565"),→
                                                        MediaType.APPLICATION_JSON).build();
  }
}

The getAsPlainText method returns a String representation of a customer and the getMaximumBonusAllowed returns a numerical constant. The defaults will apply so the return HTTP status on both methods will be 200-OK (if no exception occurs). The getAsXML returns a Customer JAXB POJO meaning that the runtime will marshall the object into an XML representation.

The getAsJson method doesn’t return an entity but instead a javax.ws.rs.core.Response object. A Response wraps the entity that is returned to the consumer and it’s instantiated using the ResponseBuilder class as a factory. In this example, we still want to return a JAXB object (the Customer) with a 200-OK status code (the ok() method), but we also want to specify the MIME type to be JSON. Calling the ResponseBuilder.build() method creates the final Response instance.

It is recommended to return a custom Response for all requests rather than the entity itself (you can then set a specific status code if needed). Table 15-6 shows a subset of the Response API.

Table 15-6. The Response API

Method Description
accepted() Creates a new ResponseBuilder with an accepted status
created() Creates a new ResponseBuilder for a created resource (with its URI)
noContent() Creates a new ResponseBuilder for an empty response
notModified() Creates a new ResponseBuilder with a not-modified status
ok() Creates a new ResponseBuilder with an ok status
serverError() Creates a new ResponseBuilder with an server error status
status() Creates a new ResponseBuilder with the supplied status
temporaryRedirect() Creates a new ResponseBuilder for a temporary redirection
getCookies() Gets the cookies from the response message
getHeaders() Gets the headers from the response message
getLinks() Get the links attached to the message as header
getStatus() Get the status code associated with the response
readEntity() Read the message entity as an instance of specified Java type using a MessageBodyReader that supports mapping the message onto the requested type

The Response and ResponseBuilder follow the fluent API design pattern. Meaning you can easily write a response by concatenating methods. This also makes the code more readable. Here are some examples of what you can write with this API:

Response. ok ().build();
Response.ok(). cookie (new NewCookie("SessionID", "5G79GDIFY09")).build();
Response.ok("Plain Text"). expires (new Date()).build();
Response.ok(new Customer ("John", "Smith"), MediaType.APPLICATION_JSON). build ();
Response. noContent ().build();
Response. accepted (new Customer("John", "Smith", "[email protected]", "1234565")).build();
Response.notModified(). header ("User-Agent", "Mozilla").build();

HTTP Method Matching

You’ve seen how the HTTP protocol works with its requests, responses, and action methods (GET, POST, PUT, etc.). JAX-RS defines these common HTTP methods using annotations: @GET, @POST, @PUT, @DELETE, @HEAD, and @OPTIONS. Only public methods may be exposed as resource methods. Listing 15-13 shows a customer RESTful web service exposing CRUD methods: @GET methods to retrieve resources, @POST methods to create a new resource, @PUT methods to update an existing resource, and @DELETE methods to delete a resource.

Listing 15-13.  A Customer Resource Exposing CRUD Operations and Retuning Responses

@Path("/customer")
@Produces(MediaType.APPLICATION_XML)
@Consumes(MediaType.APPLICATION_XML)
public class CustomerRestService {
 
  @GET
  public Response getCustomers() {
    // ..
    return Response. ok (customers).build();
  }
 
  @GET
  @Path("{customerId}")
  public Response getCustomer(@PathParam("customerId") String customerId) {
    // ..
    return Response. ok (customer).build();
  }
 
  @POST
  public Response createCustomer(Customer customer) {
    // ..
    return Response. created (createdCustomerURI).build();
  }
 
  @PUT
  public Response updateCustomer(Customer customer) {
    // ..
    return Response. ok (customer).build();
  }
 
  @DELETE
  @Path("{customerId}")
  public Response deleteCustomer(@PathParam("customerId") String customerId) {
    // ..
    return Response. noContent ().build();
  }
}

The HTTP specification defines what HTTP response codes should be on a successful request. You can expect JAX-RS to return the same default response codes:

  • GET methods retrieve whatever information (in the form of an entity) is identified by the requested URI. GET should return 200-OK.
  • The PUT method refers to an already existing resource that needs to be updated. If an existing resource is modified, either the 200-OK or 204-No Content response should be sent to indicate successful completion of the request.
  • The POST method is used to create a new resource identified by the request URI. The response should return 201-CREATED with the URI of this new resource or 204-No Content if it does not result in a resource that can be identified by a URI.
  • The DELETE method requests that the server deletes the resource identified by the requested URI. A successful response should be 200-OK if the response includes an entity, 202-Accepted if the action has not yet been enacted, or 204-No Content if the action has been enacted but the response does not include an entity.

Building URIs

Hyperlinks are a central aspect of REST applications. In order to evolve through the application states, RESTful web services need to be agile at managing transition and building URIs. JAX-RS provides a javax.ws.rs.core.UriBuilder that aims at replacing java.net.URI for making it easier to build URIs in a safe manner. UriBuilder has a set of methods that can be used to build new URIs or build from existing URIs. Listing 15-14 gives you some examples of how you can use the UriBuilder to create any kind of URI with path, query, or matrix parameters.

Listing 15-14.  Using UriBuilder

public class URIBuilderTest {
 
  @Test
  public void shouldBuildURIs() {
    URI uri =
        UriBuilder.fromUri (" http://www.myserver.com").path("book").path("1234").build();
    assertEquals(" http://www.myserver.com/book/1234 ", uri.toString());
 
    uri = UriBuilder.fromUri(" http://www.myserver.com").path(SPI_AMPquot;book ")
                                  . queryParam ("author", "Goncalves").build();
    assertEquals(" http://www.myserver.com/book?author=Goncalves ", uri.toString());
 
    uri = UriBuilder.fromUri(" http://www.myserver.com").path(SPI_AMPquot;book ")
                                 . matrixParam ("author", "Goncalves").build();
    assertEquals(" http://www.myserver.com/book;author=Goncalves ", uri.toString());
 
    uri = UriBuilder.fromUri(" http://www.myserver.com").path(SPI_AMPquot; {path} ")
           .queryParam("author", " {value} ").build(" book ", " Goncalves ");
    assertEquals(" http://www.myserver.com/book?author=Goncalves ", uri.toString());
 
 
    uri = UriBuilder. fromResource (BookRestService.class).path("1234").build();
    assertEquals(" /book/1234 ", uri.toString());
 
    uri = UriBuilder.fromUri(" http://www.myserver.com").fragment("book").build() ;
    assertEquals(" http://www.myserver.com/#book", uri.toString());
 
  }
}

Contextual Information

When a request is being processed, the resource provider needs contextual information to perform the request properly. The @javax.ws.rs.core.Context annotation is intended to inject into an attribute or a method parameter the following classes: HttpHeaders, UriInfo, Request, SecurityContext, and Providers. For example, Listing 15-15 shows the code that injects UriInfo so it can build URIs and HttpHeaders to return some headers information.

Listing 15-15.  A Customer Resource Getting HttpHeaders and UriInfo

@Path("/customer")
public class CustomerRestService {
 
  @Context
  UriInfo uriInfo ;
 
  @Inject
  private CustomerEJB customerEJB;
 
  @GET
  @Path("media")
  public String getDefaultMediaType( @Context HttpHeaders headers) {
    List<MediaType> mediaTypes = headers.getAcceptableMediaTypes();
    return mediaTypes.get(0).toString();
  }
 
  @GET
  @Path("language")
  public String getDefaultLanguage( @Context HttpHeaders headers) {
    List<String> mediaTypes = headers.getRequestHeader(HttpHeaders.ACCEPT_LANGUAGE);
    return mediaTypes.get(0);
  }
 
  @POST
  @Consumes(MediaType.APPLICATION_XML)
  public Response createCustomer(Customer cust) {
    Customer customer = customerEJB.persist(cust);
    URI bookUri = uriInfo .getAbsolutePathBuilder().path(customer.getId()).build();
    return Response.created(bookUri).build();
  }
}

As you saw earlier with HTTP, information is transported between the client and the server not only in the entity body, but also in the headers (Date, Server, Content-Type, etc.). HTTP headers take part in the uniform interface, and RESTful web services use them in their original meanings. As a resource developer, you may need to access HTTP headers; that’s what the javax.ws.rs.core.HttpHeaders interface serves. An instance of HttpHeaders can be injected into an attribute or a method parameter using the @Context annotation, as the HttpHeaders class is a map with helper methods to access the header values in a case-insensitive manner. In Listing 15-15 the service returns the default Accept-Language and MediaType.

image Note  JAX-RS 2.0 uses @Context to inject contextual information. Unfortunately @Inject doesn’t work because the CDI alignment couldn’t be completely archived in this release. Hopefully in the future releases of JAX-RS we will be able to use CDI extensively and just focus on a single annotation: @Inject.

Entity Provider

When entities are received in requests or sent in responses, the JAX-RS implementation needs a way to convert the representations to a Java type and vice versa. This is the role of entity providers that supply mapping services between representations and their associated java types. An example is JAXB, which maps an object to an XML representation and vice versa. If the default XML and JSON providers are not sufficient, then you can develop your own custom entity providers. You can even define your own format. What you need then is to give the JAX-RS runtime a way to read/write your custom format into/from an entity by implementing your own entity provider. Entity providers come in two flavors: MessageBodyReader and MessageBodyWriter.

Let’s say you want to exchange your Customer bean into a custom/format that would look like this: 1234/John/Smith. As you can see, the delimiter is the ‘/’ symbol, the first token is the customer id, the second the first name and the last one the surname. You first need a class (a writer) that takes a Customer bean and maps it to a response body. Listing 15-16 shows the CustomCustomerWriter class that must implement the javax.ws.rs.ext.MessageBodyWriter interface and be annotated with @Provider. The annotation @Produces specifies our custom media type ("custom/format"). As you can see, the writeTo method converts a Customer bean into a stream following the custom format.

Listing 15-16.  A Provider Producing a Custom Representation of a Customer

@Provider
@Produces("custom/format")
public class CustomCustomerWriter implements MessageBodyWriter <Customer> {
 
  @Override
  public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations,
                                                              MediaType mediaType) {
    return Customer.class.isAssignableFrom(type);
  }
 
  @Override
  public void writeTo(Customer customer, Class<?> type, Type gType, Annotation[]
             annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
                   OutputStream outputStream) throws IOException, WebApplicationException {
 
    outputStream.write(customer. getId ().getBytes());
    outputStream.write(' / '),
    outputStream.write(customer. getFirstName ().getBytes());
    outputStream.write(' / '),
    outputStream.write(customer. getLastName ().getBytes());
  }
 
  @Override
  public long getSize(Customer customer, Class<?> type, Type genericType, Annotation[]
                                                        annotations, MediaType mediaType) {
    return customer.getId().length() + 1 + customer.getFirstName().length() + 1 +  →
                                           customer.getLastName().length();
  }
}

On the other hand, to map a request body to a Java type, a class must implement the javax.ws.rs.ext.MessageBodyReader interface and be annotated with @Provider. By default, the implementation is assumed to consume all media types (*/*). The annotation @Consumes in Listing 15-17 is used to specify our custom media type. The method readFrom takes the input stream, tokenizes it using the ‘/’ delimiter and converts it into a Customer object. MessageBodyReader and MessageBodyWriter implementations may throw a WebApplicationException if they can’t produce any representation.

Listing 15-17.  A Provider Consuming a Custom Stream and Creating a Customer

@Provider
@Consumes("custom/format")
public class CustomCustomerReader implements MessageBodyReader <Customer> {
 
  @Override
  public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations,
                                                             MediaType mediaType) {
    return Customer.class.isAssignableFrom(type);
  }
 
  @Override
  public Customer readFrom(Class<Customer> type, Type gType, Annotation[] annotations,
                      MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
                      InputStream inputStream) throws IOException, WebApplicationException {
 
    String str = convertStreamToString(inputStream);
    StringTokenizer s = new StringTokenizer(str, " / ");
 
    Customer customer = new Customer();
    customer.setId(s.nextToken());
    customer.setFirstName(s.nextToken());
    customer.setLastName(s.nextToken());
 
    return customer;
  }
}

Thanks to our CustomCustomerWriter and CustomCustomerReader we can now exchange data represented by our custom format, back and forth from the service to the consumer. The RESTful web service just has to declare the correct media type and the JAX-RS runtime will do the rest:

@GET
@Produces("custom/format")
public Customer getCustomCustomer () {
  return new Customer("1234", "John", "Smith");
}

Our custom format is a specific case, but for common media types, the JAX-RS implementation comes with a set of default entity providers (see Table 15-7). So for most common cases you don’t have to worry about implementing your own reader and writer.

Table 15-7. Default Providers of the JAX-RS Implementation

Type Description
byte[] All media types (*/*)
java.lang.String All media types (*/*)
java.io.InputStream All media types (*/*)
java.io.Reader All media types (*/*)
java.io.File All media types (*/*)
javax.activation.DataSource All media types (*/*)
javax.xml.transform.Source XML types (text/xml, application/xml)
javax.xml.bind.JAXBElement JAXB class XML media types (text/xml, application/xml)
MultivaluedMap<String,String> Form content (application/x-www-form-urlencoded)
javax.ws.rs.core StreamingOutput All media types (*/*), MessageBodyWriter only

Handling Exceptions

The code so far was executed in a happy world where everything works and no exception handling has been necessary. Unfortunately, life is not that perfect, and sooner or later you’ll have to face a resource being blown up either because the data you received is not valid or because pieces of the network are not reliable.

A resource method may throw any checked or unchecked exception. Unchecked exceptions can be rethrown and allowed to propagate to the underlying container. Conversely, checked exceptions cannot be thrown directly and must be wrapped in a container-specific exception (ServletException, WebServiceException or WebApplicationException). But you can also throw a javax.ws.rs.WebApplicationException or subclasses of it (BadRequestException, ForbiddenException, NotAcceptableException, NotAllowedException, NotAuthorizedException, NotFoundException, NotSupportedException). The exception will be caught by the JAX-RS implementation and converted into an HTTP response. The default error is a 500 with a blank message, but the class javax.ws.rs.WebApplicationException offers various constructors so you can pick a specific status code (defined in the enumeration javax.ws.rs.core.Response.Status) or an entity. In Listing 15-18 the method getCustomer throws an unchecked exception (IllegalArgumentException) if the customer id is lower than 1000, and a 404-Not Found if the customer is not found in the database (instead it could have thrown a NotFoundException).

Listing 15-18.  A Method Throwing Exceptions

@Path("/customer")
public class CustomerRestService {
 
  @Inject
  private CustomerEJB customerEJB;
 
  @Path("{customerId}")
  public Customer getCustomer(@PathParam("customerId") Long customerId) {
    if (customerId < 1000)
      throw new IllegalArgumentException("Id must be greater than 1000!");
 
    Customer customer = customerEJB.find(customerId);
    if (customer == null)
      throw new WebApplicationException(Response.Status.NOT_FOUND);
    return customer;
  }
}

To keep your code DRY (which stands for Don’t Repeat Yourself), you can supply exception mapping providers. An exception mapping provider maps a general exception to a Response. If this exception is thrown, the JAX-RS implementation will catch it and invoke the corresponding exception mapping provider. An exception mapping provider is an implementation of ExceptionMapper<E extends java.lang.Throwable>, annotated with @Provider. Listing 15-19 maps a javax.persistence.EntityNotFoundException to a 404-Not Found status code.

Listing 15-19.  An JPA Exception Mapped to a 404-Not Found Status Code

@Provider
public class EntityNotFoundMapper implements ExceptionMapper <EntityNotFoundException> {
 
  public Response toResponse(javax.persistence.EntityNotFoundException ex) {
    return Response.status(404).entity(ex.getMessage()).type(MediaType.TEXT_PLAIN).build();
  }
}

Life Cycle and Callback

When a request comes in, the targeted RESTful web service is resolved, and a new instance of the matching class is created. Thus, the life cycle of a RESTful web service is per request so the service does not have to worry about concurrency and can use instance variables safely.

If deployed in a Java EE container (servlet or EJB), JAX-RS resource classes and providers may also make use of the JSR 250 life-cycle management and security annotations: @PostConstruct, @PreDestroy, @RunAs, @RolesAllowed, @PermitAll, @DenyAll, and @DeclareRoles. The life cycle of a resource can use @PostConstruct and @PreDestroy to add business logic after the resource is created or before it is destroyed. Figure 15-3 shows the life cycle that is equivalent to most components in Java EE.

9781430246268_Fig15-03.jpg

Figure 15-3. Resource life cycle

Packaging

RESTful web services may be packaged in a war or an EJB jar file depending if you are targeting the Web Profile or Full Java EE 7 services. Remember that a RESTful web service can also be annotated with @Stateless or @Singleton to benefit from the services of session beans. SOAP web services had several generated artifacts to package but that’s not the case for REST. The developer is responsible for just packaging the classes annotated with @Path (and other helper classes). Note that there is no deployment descriptor in JAX-RS 2.0.

Invoking RESTful Web Services

So now you know how to develop RESTful web services that can process data, make CRUD operations on a database, bring information from the server and so on. How do you access them? Well, if you look back at Figure 15-1 it is just a matter of being able to make an HTTP request on a URI. Take any tool that is able to do so and you can invoke all the RESTful web services seen so far.

The first tool that comes to our mind is the Web browser. Open your browser, point it to a URI and you will get the visual representation. Unfortunately Web browsers only know how to do GET and POST HTTP methods. If you want more from your Web browser, you can always add a few plugins to allow more flexibility and use PUT and DELETE, too (e.g., Postman on Chrome, but there are more). Another very rich tool you can use is cUrl. With this command-line tool you can have access to all the details of the HTTP protocol and so invoke your RESTful web services.

Before JAX-RS 2.0 there was no standard Java way to easily invoke your RESTful web services. You would either rely on the low-level java.net.HttpURLConnection API or use a proprietary framework API (such as Jersey, Resteasy or Restlet). JAX-RS 2.0 fills this gap with a fluent, easy to use, request building API.

The Client API

JAX-RS 2.0 introduces a new client API so that you can make HTTP requests to your remote RESTful web services easily (despite all the low-level details of the HTTP protocol). It is a fluent request building API (i.e., using the Builder design pattern) that uses a small number of classes and interfaces (see Table 15-8 to have an overview of the javax.ws.rs.client package as well as Table 15-6 for the Response API). Very often, you will come across three main classes: Client, WebTarget, and Response. The Client interface (obtained with the ClientBuilder) is a builder of WebTarget instances. A WebTarget represents a distinct URI from which you can invoke requests on to obtain a Response. From this Response you can check HTTP status, length or cookies but more importantly you can get its content (a.k.a. entity, message body or payload) through the Entity class.

Table 15-8. Main Classes and Interfaces of the javax.ws.rs.client Package

Class/Interface Description
Client Is the main entry point to the fluent API used to build and execute client requests in order to consume responses returned
ClientBuilder Entry point to the client API used to bootstrap Client instances
Configurable Client-side configuration form Client, WebTarget, and Invocation
Entity Encapsulates message entity including the associated variant information
Invocation Is a request that has been prepared and is ready for execution
Invocation.Builder A client request invocation builder
WebTarget A resource target identified by the resource URI

Let’s take it step by step to discover how to use this new API and then put it all together to test a RESTful web service.

Bootstrapping the Client

The main entry point for the API is the Client interface. The Client interface manages and configures HTTP connections. It is also a factory for WebTargets and has a set of methods for creating resource links and invocations. The Client instances are created using one of the static methods of the ClientBuilder class:

Client client = ClientBuilder.newClient();

This example demonstrates the way to create a Client instance using the default implementation. It is also possible to request a particular custom Client implementation using a specific configuration. For example, this code registers the CustomCustomerReader provider (see Listing 15-17) and sets some properties:

Client client = ClientBuilder.newClient();
client.configuration().register(CustomCustomerReader.class).setProperty("MyProperty", 1234);

Targets and Invocations

Once you have a Client you can now target a RESTful web service URI and invoke some HTTP methods on it. That’s what the WebTarget and Invocation interfaces allow you to do. The Client.target() methods are factories for web targets that represent a specific URI. You build and execute requests from a WebTarget instance. You can create a WebTarget with the String representation of a URI:

WebTarget target = client.target(" http://www.myserver.com/book ");

You can also obtain a WebTarget from a java.net.URI, javax.ws.rs.core.UriBuilder or javax.ws.rs.core.Link:

URI uri = new URI(" http://www.myserver.com/book ");
WebTarget target = client.target(uri);

Now that you have a URI to target, you need to build your HTTP resquest. The WebTarget allows you to do that by using the Invocation.Builder. To build a simple HTTP GET on a URI just write:

Invocation invocation  = target.request().buildGet()

Invocation.Builder allows you to build a GET method as well as POST, PUT and DELETE methods. You can also build a request for different MIME types and even add path, query and matrix parameters. For PUT and POST methods you need to pass an Entity, which represents the payload to send to your RESTful web service:

target.request(). buildDelete ();
target. queryParam ("author", "Eloise").request().buildGet();
target. path (bookId).request().buildGet();
target.request(MediaType.APPLICATION_XML).buildGet();
target.request(MediaType.APPLICATION_XML). acceptLanguage ("pt").buildGet();
target.request().buildPost( Entity.entity(new Book() ));

This code just builds an Invocation. You then need to call the invoke()method to actually invoke your remote RESTful web service and get a Response object back. The Response is what defines the contract with the returned instance and is what you will consume:

Response response = invocation.invoke();

So if you put everything together, these are the lines of code to invoke a GET method on a remote RESTful web service located at http://www.myserver.com/book and return a text/plain value:

Client client = ClientBuilder.newClient();
WebTarget target = client. target (" http://www.myserver.com/book ");
Invocation invocation  = target. request (MediaType.TEXT_PLAIN). buildGet ();
Response response = invocation.invoke();

Thanks to the builder API and some shortcuts, you can write the same behavior in a single line of code:

Response response = ClientBuilder.newClient(). target (" http://www.myserver.com/book ")
                                             . request (MediaType.TEXT_PLAIN). get ();

Now let’s see how to manipulate the Response and how to consume entities.

Consuming Responses

The Response class allows the consumer to have some control over the HTTP response returned from the RESTful web service. With its API you can check HTTP status, headers, cookies and, of course, the message body (a.k.a. entity). The code below uses the built-in methods to get access to some low-level HTTP information such as the status code, body length, message date or any HTTP header:

assertTrue(response. getStatusInfo () == Response.Status.OK );
assertTrue(response. getLength () == 4);
assertTrue(response. getDate () != null);
assertTrue(response. getHeaderString ("Content-type").equals("text/plain"));

But most of the time what we really want from a Response is the entity sent from the RESTful web service. The readEntity method reads the message input stream, as an instance of specified Java, using a MessageBodyReader that supports the mapping of the message entity stream to the requested type. This means that if you specify that you want a String, the JAX-RS runtime will use the default String reader:

String body = response. readEntity (String.class);

When the readEntity() method is invoked with POJO, the JAX-RS runtime needs a MessageBodyReader that matches the response's content-type. So, for example, if Book is a JAXB bean and if your content type is XML, then JAX-RS will delegate the JAXB runtime to unmarshall the XML stream into your Book POJO:

Book book = response.readEntity( Book.class );

Putting everything together, the code below invokes a remote RESTful web service and gets the returned String entity:

Response response = ClientBuilder.newClient().target(" http://www.myserver.com/book ")
                                             .request().get();
String body = response. readEntity(String.class) ;

But there is actually a shortcut on the Get method that allows you to specify the desired type and get an entity in a single line of code (without using an intermediate Response object):

String body = ClientBuilder.newClient().target(" http://www.myserver.com/book ")
                                       .request(). get(String.class) ;

Anatomy of a REST Consumer

Contrary to JAX-WS, which is embedded in Java SE and allows you to invoke SOAP web services out of the box with a JDK, with JAX-RS you need the client API in your classpath. But that’s the only constraint. Unlike SOAP you don’t have to generate any artifacts. So RESTful web service consumers can be from any Java class running on the JVM (main class, integration test, batch processing), to any Java EE component running in a container (Servlet, EJB, Managed Bean).

Of course, one of the strength of RESTful web services is interoperability. So if you expose a set of resources on the Internet you will be able to have mobile devices (smartphones, tablets) and other web technologies (e.g., JavaScript) accessing them.

Putting It All Together

Let’s put all these concepts together and write a book RESTful web service, package and deploy it into GlassFish, and test it with cURL and with an integration test using the new client API. The idea is to have a Books JAXB bean that has a list of Book JPA entities that are mapped to a database. The BookRestService gives CRUD operations on the book. Despite being a RESTful web service it is also a stateless session bean allowing transactional demarcation (using an EntityManager). Once deployed, you will be able to create, retrieve, or delete books using HTTP methods with cURL and the JAX-RS client API. Thanks to JAXB and an extension of Jersey, you will be able to have both XML and JSON representations of these books.

The example follows the Maven directory structure and packages all the classes into a war file (chapter15-service-1.0.war). The classes described in Figure 15-4 have to be placed in the following directories:

  • src/main/java: The directory for the Books, Book entity and BookRestService as well as a technical classes used to configure the runtime (more on ApplicationConfig later)
  • src/main/resources: The persistence.xml file used by the resource that maps the Book entity to the Derby database
  • src/test/java: The directory for the integration test BookRestServiceIT
  • pom.xml: The Maven Project Object Model (POM) describing the project and its dependencies

9781430246268_Fig15-04.jpg

Figure 15-4. Putting It All Together

Writing the Book Entity

By now you understand the code of the Book entity, but there is one very important element to notice: this entity is also annotated with the JAXB @XmlRootElement annotation (see Listing 15-20), which allows it to have an XML representation of a book (and JSON with a Jersey extension that you will see later).

Listing 15-20.  A Book Entity with a JAXB Annotation

@Entity
@XmlRootElement
@NamedQuery(name = Book.FIND_ALL, query = "SELECT b FROM Book b")
public class Book {
 
  public static final String FIND_ALL = "Book.findAll";
 
  @Id
  @GeneratedValue
  private String id;
  @Column(nullable = false)
  private String title;
  private Float price;
  @Column(length = 2000)
  private String description;
  private String isbn;
  private Integer nbOfPage;
  private Boolean illustrations;
 
  // Constructors, getters, setters
}

This entity also has to be packaged with a persistence.xml file (not shown here for simplicity).

Writing the Books JAXB Bean

One of the methods of the RESTful web service retrieves all the books from the database. This method could have returned a List<Book> but that would not allow JAXB marshalling. To have an XML representation of a list of books we need to have a JAXB annotated POJO. As you can see in Listing 15-21, the Books class inherits from ArrayList<Book> and has an @XmlRootElement annotation.

Listing 15-21.  A Books JAXB Bean Containing a List of Book

@XmlRootElement
@XmlSeeAlso(Book.class)
public class Books extends ArrayList<Book> {
 
  public Books() {
    super();
  }
 
  public Books(Collection<? extends Book> c) {
    super(c);
  }
 
  @XmlElement(name = "book")
  public List<Book> getBooks() {
    return this;
  }
 
  public void setBooks(List<Book> books) {
    this.addAll(books);
  }
}

Writing the BookRestService

The BookRestService is a RESTful web service, implemented as a stateless session bean, using an entity manager to create, delete, and retrieve books. Let’s split the class in several parts and explain them all.

Header Class

The header of the BookRestService (see Listing 15-22) is important as it uses several metadata annotations. In JAX-RS, users access services by invoking a URI. The @Path("/book") annotation indicates the root path of the resource (the URL used to access it)—in this case, it’s something like http://localhost:8080/book.

Listing 15-22.  Header of the BookRestService Class

@Path("/book")
@Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Consumes ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Stateless
public class BookRestService {
 
  @PersistenceContext(unitName = "chapter15PU")
  private EntityManager em;
  @Context
  private UriInfo uriInfo;
  // ...

The @Produces and @Consumes annotations define the default content type that this resource produces or consumes: XML and JSON. Finally, we find the @Stateless annotation that you’ve seen in Chapter 7, which informs the container that this RESTful web service should also be treated as an EJB and allow transaction demarcation when accessing the database. This service has a reference to an entity manager and a UriInfo injected into it.

Creating a New Book

Following the REST semantic, we use an HTTP POST method to create a new resource in XML or JSON (as specified in the header with the @Consumes annotation). By default, every method consumes XML or JSON, and this is true for the createBook() method. As seen in Listing 15-23 this method takes a Book as a parameter; remember that the Book entity is also a JAXB object and, once the XML has been unmarshalled to a Book object, the entity manager can persist it. If the book parameter is null, a BadRequestException is thrown (400-Bad Request).

Listing 15-23.  The createBook Method of the BookRestService

  // ...
  @POST
  public Response createBook(Book book) {
    if (book == null)
      throw new BadRequestException ();
 
    em.persist(book);
    URI bookUri = uriInfo.getAbsolutePathBuilder().path(book.getId()).build();
    return Response.created(bookUri) .build();
  }
  // ...

This method returns a Response, which is the URI of the newly created book. We could have returned a status code 200-OK (Response.ok()), indicating that the creation of the book was successful, but, following the REST principles, the method should return a 201 (or 204), specifying the request has been fulfilled and resulting in the creation (Response.created()) of a new resource. The newly created resource can be referenced by the URI returned in the response (bookUri).

To create a resource with the code in Listing 15-23, we have the choice of sending either XML or JSON. JSON is less verbose. The cURL command line uses a POST method and passes data in a JSON format that must follow the JSON/XML mapping used in Jersey (remember that the XML in turn is mapped from the Book object using JAXB rules):

$ curl -X POST --data-binary "{"description":"Science fiction comedy book",
 "illustrations":false,"isbn":"1-84023-742-2","nbOfPage":354,"price":12.5,
 "title":"The Hitchhiker's Guide to the Galaxy"}"  →
 -H "Content-Type: application/json " http://localhost:8080/chapter15-service-1.0/rs/book -v

The verbose mode of cURL (the -v argument) displays the HTTP request and response (as shown in the following output). You can see in the response the URI of the created book resource with an ID set to 601:

> POST /chapter15-service-1.0/rs/book HTTP/1.1
> User-Agent: curl/7.23.1 (x86_64-apple-darwin11.2.0) libcurl/7.23.1
> Host: localhost:8080
> Accept: */*
> Content-Type: application/json
> Content-Length: 165
>
< HTTP/1.1 201 Created
< Server: GlassFish Server Open Source Edition  4.0
< Location: http://localhost:8080/chapter15-service-1.0/rs/book/601
< Date: Thu, 29 Nov 2012 21:49:44 GMT
< Content-Length: 0

Getting a Book by ID

To retrieve a book by its identifier, the request must have a URL of the form /book/{id of the book}. The id is used as a parameter to find the book in the database. In Listing 15-24 if the book is not found a NotFoundException (404) is thrown. Depending on the MIME type, the getBook() method will return an XML or a JSON representation of the book.

Listing 15-24.  The getBook Method of the BookRestService

// ...
@GET
@Path("{id}")
public Response getBook( @PathParam("id") String id) {
  Book book = em.find(Book.class, id);
 
  if (book == null)
    throw new NotFoundException ();
 
  return Response.ok(book) .build();
}
// ...

The @Path annotation indicates the subpath within the already specified path at the class level. The use of the {id} syntax binds the URL element to the method parameter. Let’s use cURL to access the book with id 601 and get a JSON representation:

$ curl -X GET -H "Accept: application/json "
                  http://localhost:8080/chapter15-service-1.0/rs/book/601
{"description":"Science fiction comedy book","id":"1","illustrations":false,
                      "isbn":"1-84023-742-2","nbOfPage":354,"price":12.5,"title":"H2G2"}

By just changing the Accept header property, the same code will return the XML representation of book 601:

$ curl -X GET -H "Accept: application/xml "
                  http://localhost:8080/chapter15-service-1.0/rs/book/601
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><book><description>Science fiction
 comedy book</description><id>601</id><illustrations>false</illustrations>978-1-4302-4188-1
1-84023-742-2</isbn><nbOfPage>354</nbOfPage><price>12.5</price><title>H2G2</title></book>

Getting all the Books

To get all the books from the database the code in Listing 15-25 uses a TypedQuery the result of which is set into the Books class. Remember that this class extends ArrayList<Book> and gives an XML representation of the list thanks to its JAXB annotations. The response returns a 200-OK status with the books representation in the message body.

Listing 15-25.  The getBooks Method of the BookRestService

// ...
@GET
public Response getBooks() {
  TypedQuery<Book> query = em.createNamedQuery(Book.FIND_ALL, Book.class);
  Books books = new Books(query.getResultList()) ;
  return Response.ok( books ).build();
}
// ...

Again, if you want to quickly test this code with a cUrl, fire a command line and type the following commands to get either an XML or JSON representation of the list of books:

$ curl -X GET -H "Accept: application/ json "
                          http://localhost:8080/chapter15-service-1.0/rs/book
$ curl -X GET -H "Accept: application/ xml "
                          http://localhost:8080/chapter15-service-1.0/rs/book

Deleting a Book

The deleteBook()method in Listing 15-26 follows the format of the getBook() method because it uses a subpath and an ID as a parameter, with the only difference being the HTTP request used (DELETE instead of GET). If the book is not found in the database a NotFoundException is thrown, otherwise the book is deleted and a 204-No Content is sent back.

Listing 15-26.  The deleteBook Method of the BookRestService

// ...
@DELETE
@Path("{id}")
public Response deleteBook( @PathParam("id") String id ) {
  Book book = em.find(Book.class, id );
 
  if (book == null)
    throw new NotFoundException();
 
  em.remove(book);
 
  return Response.noContent().build();
}
// ...

If we use the verbose mode of cURL (-v argument), we’ll see the DELETE request is sent, and the 204-No Content status code appears in the response to indicate that the resource doesn’t exist anymore.

$ curl -X DELETE http://localhost:8080/chapter15-service-1.0/rs/book/601 -v
> DELETE /chapter15-service-1.0/rs/book/601 HTTP/1.1
> User-Agent: curl/7.23.1 (x86_64-apple-darwin11.2.0) libcurl/7.23.1
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 204 No Content
< Server: GlassFish Server Open Source Edition  4.0

Configuring JAX-RS

Before deploying the BookRestService and Book entity, we need to register a url pattern in Jersey to intercept HTTP calls to the services. This way the requests sent to the /rs path will get intercepted by Jersey. You can either set this url pattern by configuring the Jersey servlet in the web.xml file, or use the @ApplicationPath annotation (see Listing 15-27). The ApplicationConfig class needs to extend javax.ws.rs.core.Application and define all the RESTful web services of the application (here c.add(BookRestService.class)) as well as any other needed extension (c.add(MOXyJsonProvider.class)).

Listing 15-27.  The ApplicationConfig Class Declaring the /rs URL Pattern

@ApplicationPath("rs")
public class ApplicationConfig extends Application {
 
  private final Set<Class<?>> classes;
 
  public ApplicationConfig() {
    HashSet<Class<?>> c = new HashSet<>();
    c.add( BookRestService.class );
 
    c.add( MOXyJsonProvider.class );
 
    classes = Collections.unmodifiableSet(c);
  }
 
  @Override
  public Set<Class<?>> getClasses() {
    return classes;
  }
}

This class has to be somewhere in your project (no particular package) and, thanks to the @ApplicationPath annotation, it will map the requests to the /rs/* URL pattern. This means, each time a URL starts with /rs/, it will be handled by Jersey. And, indeed, in the examples that I have used with cURL, all the resource URLs start with /rs:

$ curl -X GET -H "Accept: application/json"
                          http://localhost:8080/chapter15-service-1.0/rs/book

image Note  In Chapter 12 you’ve seen several specifications around XML and JSON. Two groups of specifications exist: processing (JAXP and JSON-P) and binding (JAXB). Thanks to binding, JAX-RS will convert the list of beans into XML using the built-in MessageBodyReader and MessageBodyWriter which are based on JAXB.  Unfortunately the binding technology for JSON does not exist yet (it is supposed to arrive in the coming years and will probably be called JSON-B). So, for now, marshalling a bean to JSON automatically is impossible. That’s why in the ApplicationConfig class we need to add a Jersey specific provider (MOXyJsonProvider) to have a JSON MessageBodyReader and MessageBodyWriter. But that’s not portable across JAX-RS implementations.

Compiling and Packaging with Maven

All the classes need now to be compiled and packaged in a war file (<packaging>war</packaging>). The pom.xml in Listing 15-28 declares all necessary dependencies to compile the code (which are all in glassfish-embedded-all). Remember that JAXB is part of Java SE, so we don’t need to add this dependency. Notice that the pom.xml declares the Failsafe plugin that is designed to run integration tests (used later to run the BookRestServiceIT integration test class).

Listing 15-28.  The pom.xml File to Compile, Test, and Package the RESTful Web Service

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns=" http://maven.apache.org/POM/4.0.0 "
         xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
         xsi:schemaLocation=" http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd ">
  <modelVersion>4.0.0</modelVersion>
 
  <parent>
    <artifactId>chapter15</artifactId>
    <groupId>org.agoncal.book.javaee7</groupId>
    <version>1.0</version>
  </parent>
 
  <groupId>org.agoncal.book.javaee7.chapter15</groupId>
  <artifactId>chapter15-service</artifactId>
  <version>1.0</version>
  <packaging>war</packaging>
 
  <dependencies>
    <dependency>
      <groupId>org.glassfish.main.extras</groupId>
      <artifactId> glassfish-embedded-all </artifactId>
      <version>4.0</version>
      <scope>provided</scope>
    </dependency>
 
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
 
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
          <source> 1.7 </source>
          <target>1.7</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId> maven-failsafe-plugin </artifactId>
        <version>2.12.4</version>
        <executions>
          <execution>
            <id>integration-test</id>
            <goals>
              <goal> integration-test </goal>
              <goal>verify</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

To compile and package the classes, open a command line in the root directory containing the pom.xml file and enter the following Maven command:

$ mvn package

Go to the target directory, look for a file named chapter15-service-1.0.war, and open it. Notice that Book.class, Books.class, ApplicationConfig.class, and BookRestService.class are all under the WEB-INFclasses directory. The persistence.xml file is also packaged in the war file.

Deploying on GlassFish

Once the code is packaged, make sure GlassFish and Derby are up and running and deploy the war file by issuing the asadmin command-line interface. Open a console, go to the target directory where the chapter15-service-1.0.war file is located, and enter:

$ asadmin deploy chapter15-service-1.0.war

If the deployment is successful, the following command should return the name of the deployed component and its type:

$ asadmin list-components
chapter15-service-1.0 <ejb, web>

Now that the application is deployed, you can use cURL in a command line to create book resources by sending POST requests and get all or a specific resource with GET requests. DELETE will remove book resources.

WADL

I introduced the WADL (Web Application Description Language) at the beginning of the chapter saying that it wasn’t standardized and not very much used in RESTful architecture. But if you want to have a look at what it could look like for our BookRestService RESTful web service, check the following URL: http://localhost/8080/chapter15-service-1.0/rs/application.wadl. Listing 15-29 shows an abstract of this WADL.

Listing 15-29.  Extract of the WADL Generated by GlassFish for the BookRestService RESTful web service

<application xmlns=" http://wadl.dev.java.net/2009/02 ">
  <resources base=" http://localhost:8080/chapter15-service-1.0/rs/ ">
    <resource path=" /book ">
      <method id=" POST " name="POST">
        <request>
          <representation element="book" mediaType="application/xml"/>
          <representation element="book" mediaType="application/json"/>
        </request>
      </method>
      <resource path=" {id} ">
        <param name="id" style="template" type="xs:string"/>
        <method id=" GET{id} " name="GET">
          <response>
            <representation mediaType="application/xml"/>
            <representation mediaType="application/json"/>
          </response>
        </method>
        <method id=" DELETE{id} " name="DELETE"/>
      </resource>
    </resource>
    ...
  </resources>
</application>

The WADL in Listing 15-29 describes the root path (http://localhost:8080/chapter15-service-1.0/rs/) and all the subpaths available in the RESTful service (/book and {id}). It also gives you the HTTP method you can invoke (POST, GET, DELETE . . .).

Writing the BookRestServiceIT Integration Test

Now that the code is packaged and deployed into GlassFish we can write an integration test that will make HTTP requests to the RESTful web service using the new JAX-RS 2.0 Client API. Integration tests are different from unit tests because they don’t test your code in isolation, they need all the containers services. Typically, if GlassFish and Derby are not up and running, the code in Listing 15-30 will not work.

The two first methods test failures. shouldNotCreateANullBook makes sure you cannot create a book with a null Book object. So it posts a null entity and expects a 400-Bad Request. The shouldNotFindTheBookID test case passes an unknown book id and expects a 404-Not Found.

The shouldCreateAndDeleteABook test case is a bit more complex as it invokes several operations. First, it posts an XML representation of a Book object and makes sure the status code is 201-Created. The bookURI variable is the URI of the newly created book. The test case uses this URI to request the new book, then reads the message body, casts it to a Book class (book = response.readEntity(Book.class)) and asserts that the values are correct. Then a DELETE invocation deletes the book from the database and checks the response status code is 204-No Content. A last GET on the resource makes sure it has been deleted by checking the 404-Not Found status code.

Listing 15-30.  The ApplicationConfig Class Declaring the /rs URL Pattern

public class BookRestServiceIT {
 
  private static URI uri = UriBuilder.
               fromUri("http://localhost/chapter15-service-1.0/rs/book").port(8080).build ();
  private static Client client = ClientBuilder.newClient();
  
  @Test
  public void shouldNotCreateANullBook() throws JAXBException {
 
    // POSTs a Null Book
    Response response = client.target(uri).request(). post (Entity.entity( null ,
                                                          MediaType.APPLICATION_XML));
    assertEquals(Response.Status. BAD_REQUEST , response.getStatusInfo());
  }
 
  @Test
  public void shouldNotFindTheBookID() throws JAXBException {
 
    // GETs a Book with an unknown ID
    Response response = client.target(uri).path(" unknownID ").request(). get ();
    assertEquals(Response.Status. NOT_FOUND , response.getStatusInfo());
  }
 
  @Test
  public void shouldCreateAndDeleteABook() throws JAXBException {
 
    Book book = new Book("H2G2", 12.5F, "Science book", "1-84023-742-2", 354, false);
 
    // POSTs a Book
    Response response = client.target(uri).request(). post (Entity.entity( book ,
                                                          MediaType.APPLICATION_XML));
    assertEquals( Response.Status.CREATED , response.getStatusInfo());
    URI bookURI = response.getLocation() ;
 
    // With the location, GETs the Book
    response = client.target( bookURI ).request(). get ();
    book = response. readEntity (Book.class);
    assertEquals(Response.Status.OK, response.getStatusInfo());
    assertEquals("H2G2", book.getTitle());
 
    // Gets the book id and DELETEs it
    String bookId = bookURI.toString().split("/")[6];
    response = client.target(uri).path(bookId).request(). delete ();
    assertEquals(Response.Status. NO_CONTENT , response.getStatusInfo());
 
    // GETs the Book and checks it has been deleted
    response = client.target(bookURI).request(). get ();
    assertEquals(Response.Status. NOT_FOUND , response.getStatusInfo());
 
  }
}

Make sure GlassFish and Derby are up and running, that the application is deployed, and execute this integration test with the Maven Failsafe plugin by entering the following Maven command:

$ mvn failsafe:integration-test

Summary

The previous chapter explained SOAP web services and by now you should know the difference between JAX-WS and JAX-RS web services. REST embraces HTTP so this chapter commenced with a general introduction to resources, representation, addressability, connectedness, and uniform interfaces. With simple verbs (GET, POST, PUT, etc.), you learned how you can access any resource deployed on the Web.

The chapter then zoomed into HTTP, a protocol based on a request/response mechanism, exchanging messages made of headers, cookies, and a body. Using HTTP headers and content negotiation, RESTful web services can choose an appropriate content type from the same URI. Caching can be employed to optimize network traffic with conditional requests using dates and ETags. This optimization can also be used with REST as it is based on HTTP. Thanks to the new JAX-RS 2.0 client API, we’ve managed to programmatically invoke a few RESTful web services.

JAX-RS is a Java API, shipped with Java EE 7 that simplifies RESTful web service development. With a set of annotations, you can define the path and subpaths of your resource, extract different kinds of parameters, or map to HTTP methods (@GET, @POST, etc.). When developing RESTful web services, you must think about resources, how they are linked together, and how to manage their state using HTTP. Now you are ready to expose a few services on the Web and see how many different devices will be able to consume them.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.41.205