Chapter 9. RESTful web services

This chapter covers

  • The REST architectural style
  • Implementing REST in Java using JAX-RS
  • Using a Groovy client to access RESTful services
  • Hypermedia

RESTful web services dominate API design these days, because they provide a convenient mechanism for connecting client and server applications in a highly decoupled manner. Mobile applications especially use RESTful services, but a good RESTful design mimics the characteristics that made the web so successful in the first place.

After discussing REST in general, I’ll talk about the server side, then about the client side, and finally the issue of hypermedia. Figures 9.1, 9.2, and 9.3 show the different technologies in this chapter.

Figure 9.1. Server-side JAX-RS technologies in this chapter. JAX-RS 2.0 is annotation-based but includes builders for the responses. URIs are mapped to methods in resources, which are assigned using annotations. Resources are returned as representations using content negotiation from client headers.

Figure 9.2. Client-side REST technologies in this chapter. Unlike JAX-RS 1.x, version 2.0 includes client classes. Apache also has a common client, which is wrapped by Groovy in the HttpBuilder project. Finally, you can use standard Groovy classes to parse requests and build responses manually.

Figure 9.3. Hypermedia approaches in this chapter. Hypermedia in JAX-RS is done through transitional links in the HTTP headers, structural links in the message body, or customized responses using builders and slurpers.

9.1. The REST architecture

The term Representational State Transfer (REST) comes from the 2000 PhD thesis[1] by Roy Fielding, a person with one of the all-time great resumes.[2]

1 “Architectural Styles and the Design of Network-based Software Architectures,” available online at www.ics.uci.edu/~fielding/pubs/dissertation/top.htm.

2 Fielding is a cofounder of the Apache Software Foundation; was on the IETF working groups for the URI, HTTP, and HTML specifications; and helped set up some of the original web servers. I place him easily in the Top Ten of CS resumes, along with people like James Duncan Davidson (creator of the first versions of both Tomcat and Ant; he basically owned the 90s), Sir Timothy Berners-Lee (create the web → knighthood FTW), and Haskell Curry (whose first name is the definitive functional programming language, and whose last name is a fundamental coding technique; if your name is your resume, you win).

In his thesis, Fielding defines the REST architecture in terms of addressable resources and their interactions. When restricted to HTTP requests made over the web (not a requirement of the architecture, but its most common use today), RESTful web services are based on the following principles:

  • Addressable resource— Items are accessible to clients through URIs.
  • Uniform interface— Resources are accessed and modified using the standard HTTP verbs GET, POST, PUT, and DELETE.[3]

    3 Some services support HEAD requests as GET requests that return headers with empty responses and OPTIONS requests as an alternate way to specify what types of requests are valid at a particular address. PATCH requests are proposed as a way to do a partial update.

  • Content negotiation— The client can request different representations of resources, usually by specifying the desired MIME type in the Accept header of a request.
  • Stateless services— Interactions with resources are done through self-contained requests.

Web services based on these ideas are intended to be highly scalable and extensible, because they follow the mechanisms that make the web itself highly scalable and extensible.

Part of the scalability of a RESTful web service comes from the terms safe and idempotent:

  • Safe— Does not modify the state of the server
  • Idempotent— Can be repeated without causing any additional effects

GET requests are both safe and idempotent. PUT and DELETE requests are idempotent but not safe. They can be repeated (for example, if there’s a network error) without making any additional changes.[4] POST requests are neither safe nor idempotent.

4 Sometimes it’s hard to picture DELETE requests as idempotent, but if you delete the same row multiple times, it’s still gone.

Another key concept is Hypermedia as the Engine of Application State, which has the truly unfortunate, unpronounceable acronym HATEOAS. Most REST advocates[5] I know simply say “hypermedia” instead.

5 Often known, believe it or not, as RESTafarians.

The principles defined in this section are architectural and are thus independent of implementation language. In the next section I’ll address[6] the Java-specific specification intended to implement RESTful services, JAX-RS.

6 Sorry.

9.2. The Java approach: JAX-RS

The Java EE specification includes the Java API for RESTful Services. Version 1.18 is from JSR 311. The new version, 2.0, is an implementation of JSR 339 and was released in May of 2013.

In this section I’ll implement a set of CRUD methods on a simple POJO.[7] The JAX-RS part doesn’t depend on this, so I’ll discuss that separately. I’ll start with the basic infrastructure and then move to REST.

7 Yes, that’s a URL-driven database, and yes, that violates hypermedia principles. I promise to get to that later.

What do Java developers actually use for REST?

In this book I normally start with what Java developers use for a particular problem, then show how Groovy can help the Java implementations, and finally discuss what Groovy offers as an alternative. When I describe what Java developers typically use, I default to what the Java SE or EE specification provides.

That’s not the case with REST. In addition to the spec, JAX-RS, Java developers use several third-party alternatives. Among the most popular are Restlet (http://restlet.org/), RestEasy (www.jboss.org/resteasy), and Restfulie (http://restfulie.caelum.com.br/), and there are other alternatives as well.[8] It’s hard at this point to know which, if any, is going to be the REST framework of choice for Java developers in a few years.[9]

8 Spring REST doesn’t follow the JAX-RS specification. Apache CXF was designed for JAX-WS, but the latest version has JAX-RS support. Apache Wink is another JAX-RS 1.x implementation.

9 If I had to bet, I’d go with Restlet. Most of the good REST people I know really like it.

Therefore, I’m basing this chapter on the JAX-RS specification, even though it’s not necessarily the most popular alternative. When the alternative is not blindingly obvious, the spec usually wins.[10]

10 Except when it doesn’t. See, for example, JDO, which is still part of Java EE.

The application in this section exposes a POGO called Person as a JAX-RS 2.0 resource. The application supports GET, POST, PUT, and DELETE operations and (eventually) supports hypermedia links.

The infrastructure for the project includes the POJO, Spock tests, and a DAO implementation based on an H2 database. While the implementations are interesting, they are ancillary to the real goal of discussing RESTful services and how Groovy can simplify their development. Therefore they will not be presented in detail in this chapter. As usual, the complete classes, including Gradle build files and tests, can be found in the book source code repository.

As a brief summary, the Person class is shown in the next listing.

Listing 9.1. A Person POGO, used in the RESTful web service
    class Person {
        Long id
        String first
        String last

        String toString() {
            "$first $last"
    }
}

The DAO interface for the Person objects includes finder methods, as well as methods to create, update, and delete a Person. It’s shown in the following listing.

Listing 9.2. The DAO interface with the CRUD methods for Person
import java.util.List;

public interface PersonDAO {
    List<Person> findAll();
    Person findById(long id);
    List<Person> findByLastName(String name);
    Person create(Person p);
    Person update(Person p);
    boolean delete(long id);
}

The implementation of the DAO is done in Groovy using the groovy.sql.Sql class, just as in chapter 8 on databases. The only part that differs from that chapter is that the id attribute is generated by the database. Here’s how to use the Sql class to retrieve the generated ID:

Person create(Person p) {
    String txt = 'insert into people(id, first, last) values(?, ?, ?)'
    def keys = sql.executeInsert txt, [null, p.first, p.last]
    p.id = keys[0][0]
    return p
}

The executeInsert method returns the collection of generated values, and in this case the new ID is found as the first element of the first row.

The Spock test for the DAO is similar to those shown in chapter 6 on testing or chapter 8 on databases. The only new part is that the when/then block in Spock is repeated to insert and then delete a new Person. When Spock sees a repeat of the when/then pair, it executes them sequentially. Listing 9.3 shows this test, which inserts a row representing Peter Quincy Taggart,[11] verifies that he’s stored properly, and then deletes the row. Recall that the seriously cool old method in Spock evaluates its argument before executing the when block, so it can be compared to the rest of the expression evaluated after the when block is done.

11 Remember him? Commander of the NSEA Protector? “Never give up, never surrender?” That’s Galaxy Quest, a Star Trek parody, but arguably one of the better Star Trek movies. Did you know that the designation of the Protector was NTE-3120, and that NTE stood for “Not The Enterprise”? By Grabthar’s hammer, that’s the kind of research you are obligated to do when you write a Groovy/Java integration book.

Listing 9.3. The Spock test method to insert and delete a new Person
def 'insert and delete a new person'() {
    Person taggart = new Person(first:'Peter Quincy', last:'Taggart')

    when:
    dao.create(taggart)

    then:
    dao.findAll().size() == old(dao.findAll().size()) + 1
    taggart.id

    when:
    dao.delete(taggart.id)

    then:
    dao.findAll().size() == old(dao.findAll().size()) - 1
}

Now that the preliminaries are out of the way it’s time to look at the features provided by the JAX-RS API.

9.2.1. JAX-RS resource and tests

Moving now to the RESTful part of the application, there are several features of the JAX-RS API involved in the implementation. Here I’ll use a PersonResource class to implement the CRUD methods.

Collection and item Resources

Normally two resources are provided: one for a collection of person instances and one for an individual person. In this case both are combined to keep the sample short.

First, each method that’s tied to a particular type of HTTP request uses one of these annotations: @GET, @POST, @PUT, or @DELETE. For example, the findAll method can be implemented as follows:

@GET
public List<Person> findAll() {
    return dao.findAll();
}

A GET request returns the HTTP status code 200 for a successful request. The @Produces annotation identifies to the client the MIME type of the response. In this case I want to return JSON or XML:

@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})

The annotation accepts an array of MediaType instances, which are used for content negotiation based on the Accept header in the incoming request.

If I want to specify the response header, JAX-RS provides a factory class called Response using the builder design pattern. Here’s the implementation of the findById method that uses it:

@GET @Path("{id}")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response findById(@PathParam("id") long id) {
    return Response.ok(dao.findById(id))
        .build();
}

The ok method on the Response class sets the response status code to 200. It takes an object as an argument, which is added to the response. The @PathParam annotation also converts the input ID from a string to a long automatically.

Inserting a new instance is a bit more complicated, because the newly inserted instance needs its own URI. Because in this case the generated URI will contain an ID generated by the database, the resource method is tied to HTTP POST requests, which are neither safe nor idempotent.

Implementation detail

The create method returns a URL that includes the primary key from the database table. That detail is not something you want to expose to the client. Some unique identifier is required; here the ID is used for simplicity.

The new URI is added to the response as part of its Location header. The new URI is generated using the UriBuilder class from JAX-RS, based on the incoming URI:

UriBuilder builder =
    UriBuilder.fromUri(uriInfo.getRequestUri()).path("{id}");

The uriInfo reference in that expression refers to a UriInfo object injected from the application context. This is added as an attribute to the implementation:

@Context
private UriInfo uriInfo;

In general, the response from any insert method in a REST application is either “no content” or the entity itself. Here in the create method I decided to use the entity, because it includes the generated ID in case the client wants it.

Putting it all together, the create method is as follows:

@POST
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response create(Person person) {
    dao.create(person);
    UriBuilder builder =
        UriBuilder.fromUri(uriInfo.getRequestUri()).path("{id}");
    return Response.created(builder.build(person.getId()))
        .entity(person)
        .build();
}

The @POST annotation sets the HTTP status code in the response to 201.

The URL patterns for the resource are summarized as follows:

  • The base resource pattern is /people. A GET request at that URL returns all the Person instances. The plural form of Person is used for this reason.
  • A POST request at the same URL (/person) creates a new Person, assigns it a URL of its own, and saves it in the database.
  • A sub-resource at /people/lastname/{like} uses a URL template (the like parameter) to do an SQL-like query and find all Person instances who have a last name satisfying the clause.
  • A sub-resource using the URL template {id} supports a GET request that returns the Person instance with that ID.
  • PUT and DELETE requests at the {id} URL update and delete Person instances, respectively.

The following listing shows the complete PersonResource class for managing Person instances.

Listing 9.4. Java resource class for Person POJO

To verify that everything is working properly, I’ll again present a test class using Spock. Testing a RESTful API requires a server where the application can be deployed. The Jersey reference implementation includes a server called Grizzly for that.

The Spock test methods setupSpec and shutdownSpec are executed once each, before and after the individual tests, respectively. They therefore become the appropriate places to start and stop the server, as shown:

@Shared static HttpServer server

void setupSpec() {
    server = GrizzlyHttpServerFactory.createHttpServer(
        'http://localhost:1234/'.toURI(), new MyApplication())
}
void cleanupSpec() {
    server?.stop()
}

The createHttpServer method starts a server on the specified URI and deploys a RESTful application to it. The MyApplication class is very simple:

public class MyApplication extends ResourceConfig {
    public MyApplication() {
        super(PersonResource.class, JacksonFeature.class);
    }
}

The class MyApplication extends a JAX-RS class called ResourceConfig, which has a constructor that takes the desired resources and features as arguments. The JacksonFeature used here provides the mechanism to convert from PersonResource instances to JSON and back.[12]

12 As soon as I mention JSON, I’m talking about representations, not resources. Again, I’ll discuss that in section 9.5 on hypermedia.

Note the convenience of the safe-dereference operator, ?., used when shutting down the server. That will avoid a null pointer exception when the server fails to start properly.

The first actual test verifies that the server is up and running, using the isStarted method on the HttpServer class:

def 'server is running'() {
    expect: server.started
}

Again, the isStarted method is invoked using the standard Groovy idiom of accessing a property. There’s no reason you couldn’t call the method instead, though, if you prefer.

The rest[13] of the test methods require a client to generate the HTTP request using the proper verb. GET requests are trivial with Groovy, because you can take advantage of the getText method that the Groovy JDK adds to the java.net.URL class. So the request to retrieve all the instances could be written as

13 Again, no pun intended.

'http://localhost:1234/people'.toURL().text

While that would work, the response would then need to be parsed to get the proper information. Often that isn’t a problem, but here I’m using an alternative.

The class RESTClient is part of the HttpBuilder (http://groovy.codehaus.org/modules/http-builder/) project. I’ll discuss that further in section 9.4 on Groovy clients, but for now let me say it defines Groovy classes that wrap Java classes supplied by Apache’s HttpClient project. The test therefore contains an attribute of type RESTClient, as follows:

RESTClient client
    new RESTClient('http://localhost:1234/', ContentType.JSON)

The client points to the proper endpoint, and the second argument specifies the content type for the Accept header in the request. A GET request using this client returns an object that can be interrogated for header properties as well as data:

def 'get request returns all people'() {
    when:
    def response = client.get(path: 'people')

    then:
    response.status == 200
    response.contentType == 'application/json'
    response.data.size() == 5
}

Other finder methods are tested similarly. To keep the tests independent, the insert and delete methods are tested together; first a person is inserted, then it’s verified, and then it’s deleted again. The test uses another feature of Spock: each block (when/then/expect, and so on) can be given a string to describe its purpose. It’s not exactly behavior-driven development, but it’s as close as Spock comes at the moment.

The insert and delete test looks like the following:

def 'insert and delete a person'() {
    given: 'A JSON object with first and last names'
    def json = [first: 'Peter Quincy', last: 'Taggart']

    when: 'post the JSON object'
    def response = client.post(path: 'people',
        contentType: ContentType.JSON, body: json)

    then: 'number of stored objects goes up by one'
    getAll().size() == old(getAll().size()) + 1
    response.data.first == 'Peter Quincy'
    response.data.last == 'Taggart'
    response.status == 201
    response.contentType == 'application/json'
    response.headers.Location ==
        "http://localhost:1234/people/${response.data.id}"

    when: 'delete the new JSON object'
    client.delete(path: response.headers.Location)

    then: 'number of stored objects goes down by one'
    getAll().size() == old(getAll().size()) - 1
}

Given a JSON object representing a person, a POST request adds it to the system. The returned object holds the status code (201), the content type (application/json), the returned person object (in the data property), and the URI for the new resource in the Location header. Deleting the object is done by sending a DELETE request to the new URI and verifying that the total number of stored instances goes down by one.

Updates are done through a PUT request. To ensure that PUT requests are idempotent, the complete object needs to be specified in the body of the request. This is why PUT requests aren’t normally used for inserts; the client doesn’t know the ID of the newly inserted object, so POST requests are used for that instead.

The complete test is shown in the next listing.

Listing 9.5. A Spock test for the PersonResource with a convenient test server

The JAX-RS annotations are easy enough to use. Building a URL-driven API with them isn’t difficult. The 2.0 version of the spec also includes a client-side API, but that’s not shown here.

Lessons learned (JAX-RS)

  1. JAX-RS 2.0 is part of the Java EE specification and, like most of the recent specs, is annotation-based.
  2. It’s very easy to build a hyperlink-driven database using JAX-RS.
  3. Hypermedia mechanisms do exist in JAX-RS, but they’re well hidden.

Instead, I want to illustrate the Groovy implementation of the same specifications, mostly to illustrate the code simplifications. After that I’ll deal with the issue of hypermedia.

9.3. Implementing JAX-RS with Groovy

Groovy doesn’t change JAX-RS in any fundamental way, though as usual it simplifies the implementation classes. JAX-RS is already simplifying the implementation by providing its own kind of DSL, so the Groovy modifications are minimal.

The previous section used Groovy implementations but didn’t present them. Here I’ll show just enough to illustrate the Groovy features.

To begin, here’s the Person POGO. Note the @XmlRootElement annotation, used to control the serialization of the Person for the response. Normally that’s used for Java API for XML Binding (JAXB), but the presence of the Jackson JSON parser causes the serialization process to produce JSON objects instead:

@XmlRootElement
@EqualsAndHashCode
class Person {
    Long id
    String first
    String last

    String toString() { "$first $last" }
}

Getters, setters, and constructors are all generated in the normal manner. The @EqualsAndHashCode AST transformation takes care of equals and hashCode method implementations. The @ToString annotation could also have been used, but the desired toString method is barely longer than that, so I just wrote it out.

Speaking of AST transformations, the @Singleton annotation is applied to the JdbcPersonDAO class when implemented in Groovy. That automatically implements and enforces the singleton property on the class by making the constructor private, adding a static instance variable, and so on. That class implements the same interface as before. Here’s the beginning of the class:

@Singleton
class JdbcPersonDAO implements PersonDAO {
    static Sql sql = Sql.newInstance(
        url:'jdbc:h2:db', driver:'org.h2.Driver')

    static {
        sql.execute 'drop table if exists people'
        ...
    }
...
}
Groovy and Java interfaces

Java tools prefer Java interfaces. Most Java/Groovy integration problems vanish if you use Java interfaces with Groovy implementations.

There’s one slight syntax variation required by the switch from Java to Groovy. The @Produces and @Consumes annotations take a list of media types that they support. In the Java implementation this is expressed as an array, using the braces notation:

@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})

In Groovy, braces indicate closures. Square brackets delimit a list, however, so the Groovy implementation just replaces the braces with brackets.

Braces vs. Brackets

Groovy uses curly braces for closures, so the literal notation to define a Java array should use square brackets for a java.util .ArrayList instead.

The complete PersonResource implementation in Groovy is shown in the next listing.

Listing 9.6. A Groovy implementation of the PersonResource class
@Path('/people')
class PersonResource {
    @Context
    private UriInfo uriInfo

    PersonDAO dao = JdbcPersonDAO.instance

    @GET
    @Produces([MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML])

    List<Person> findAll() {
        dao.findAll();
    }

    @GET @Path("lastname/{like}")
    @Produces([MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML])
    List<Person> findByName(@PathParam("like") String like) {
        dao.findByLastName(like);
    }

    @GET @Path("{id}")
    @Produces([MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML])
    Response findById(@PathParam("id") long id) {
        Response.ok(dao.findById(id))
            .build()
    }

    @POST
    @Consumes([MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML])
    @Produces([MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML])
    Response create(Person person) {
        dao.create(person);
        UriBuilder builder =
            UriBuilder.fromUri(uriInfo.requestUri).path("{id}")
        Response.created(builder.build(person.id))
            .entity(person)
            .build()
    }

    @PUT @Path("{id}")
    @Consumes([MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML])
    @Produces([MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML])
    Person update(Person person) {
        dao.update(person)
        person
    }

    @DELETE @Path("{id}")
    Response remove(@PathParam("id") long id) {
        dao.delete(id);
        Response.noContent().build()
    }
}

Most discussions of JAX-RS end at this point, with a working, URL-driven database. True REST is more flexible than that, however. A RESTful service is supposed to act like the web, in that it presents a single URL to the client, which accesses it and receives additional links in return. This is known as HATEOAS, or simply hypermedia.

Lessons learned (JAX-RS with Groovy)

  1. Groovy doesn’t significantly change JAX-RS.
  2. The real Groovy simplifications are in the POGO and DAO classes. The resource implementation is essentially the same in both languages.

Hypermedia links are exposed to clients, which consume them. JAX-RS 1.x doesn’t include a client-side API. Version 2.0 does, and there’s a convenient project in the Groovy ecosystem known as HttpBuilder for performing HTTP requests. Both are the subjects of the next section.

9.4. RESTful Clients

Accessing a RESTful web service involves creating an HTTP request of the proper type and adding any necessary information to the body. One of the biggest changes in JAX-RS when moving from version 1 to version 2 is the addition of a standard client API. The API includes Client and WebTarget classes, which are used as follows:

Client cl = ClientBuilder.newClient()
WebTarget target = cl.target('http://localhost:1234/people/3')
def resp = target.request().get(Response.class)

A Client instance is created from a ClientBuilder, which in turn leads to a Web-Target. A GET request uses the get method, whose argument is the data type of the returned object. This example is taken from a hypermedia test, shown in the next section.

In Groovy, the Groovy JDK makes GET requests trivial. Groovy adds the toURL method to java.lang.String, which converts a String into an instance of java.net.URL. The Groovy JDK also adds the getText method to java.net.URL. Pulling information from the web can therefore be as simple as

String response = 'http://localhost:1234/people/3'.toURL().text

Making POST, PUT, and DELETE requests is done in Groovy the same way it’s done in Java, which isn’t fun. Instead, client access is best done through a library.

One of the most popular HTTP libraries is the open source Apache HTTP Client library (http://hc.apache.org/httpcomponents-client-ga/index.html), which is part of the Apache HttpComponents project.

Rather than show the details of that library I’d rather focus on the corresponding Groovy project, HttpBuilder. The HttpBuilder project (http://groovy.codehaus.org/modules/http-builder/) follows the classic Groovy idiom: wrap a Java library and make it easier to use. While the documentation on the website isn’t bad, I recommend looking at the test cases in the source code for guidance on how to use the API.

Like most cool projects, the source code is hosted at GitHub at https://github.com/jgritman/httpbuilder. The API includes a convenient class for REST applications called RESTClient, which I used in the tests in this chapter. The corresponding test class, RESTClientTests, shows how to access Twitter using all the standard HTTP verbs.

I used the RESTClient class in the PersonResourceSpec tests. The RESTClient class has a constructor that takes two arguments, the base URL and a content type:

RESTClient client = new RESTClient(
    'http://localhost:1234/', ContentType.JSON)

In this case I’m running the Grizzly test server on port 1234, and for this demo the data is in JSON form. The test for the GET method produces the following:

def response = client.get(path: 'people')
response.status == 200
response.contentType == 'application/json'
response.data.size() == 5

The RESTClient provides a get method that takes a path parameter. The response comes back with special properties for (most of) the typical headers. Other headers can be retrieved either by requesting the allHeaders property or by calling get-Header("...") with the required header. Any returned entity in the body of the response is in the data property.

See the rest[14] of the PersonResourceSpec class for examples of POST, PUT, and DELETE requests.

14 Again, sorry. At some point (and that may already have happened), when I say, “No pun intended,” you’re simply not going to believe me.

Lessons learned (REST clients)

  1. JAX-RS 2.0 includes classes for building REST clients.[15]

    15 The JAX-RS client classes are very easy to use, too, which is unfortunate when you’re trying to show how cool Groovy is, but helpful for users. Oh well.

  2. The Groovy project HttpBuilder wraps the Apache HttpClient project and makes it easier to use.

Both the RESTClient and the JAX-RS 2.0 client are used in the test cases in the hypermedia section, which is as good a segue as any to finally discuss HATEOAS in Java.

9.5. Hypermedia

A series of resource URLs is not a RESTful web service. At best, it’s a URL-driven database. Yet applications like that, which claim to be RESTful services, are all over the web.

A true[16] REST application understands that specific resource URLs may evolve, despite attempts to keep them as stable as possible. The idea therefore is to make requests that discover the subsequent URLs to follow. We’re so accustomed to having a fixed API that this can be a difficult concept to adopt. Instead of knowing exactly what you’re going to get back from any given request, you know how to make the first request and interrogate the result for whatever may come next. This is similar to the way we browse the web, which is no coincidence.

16 The word true here is defined as “at least trying to follow the principles in Roy Fielding’s thesis.”

It does place a higher burden on the client and the server, though. The server needs to add some sort of metadata to explain what the subsequent resources are and how to access them, and the client needs to read those responses and interpret them correctly.

This section will illustrate the ways you can add links to the service responses. I’ll start by showing an example from a public API, then demonstrate how to add links to the HTTP response headers or to the response bodies, and finally demonstrate how to customize the output however you want.

9.5.1. A simple example: Rotten Tomatoes

As a simple example, consider the API provided by the movie review website Rotten Tomatoes used in chapter 8 on Groovy with databases. The Rotten Tomatoes API only supports GET requests, so it isn’t a full RESTful service.[17]

17 RESTful services that only support GET can be called GETful services. If they’re stateless, too, doesn’t that make them FORGETful services? Thank you, thank you. I’ll be here all week. Try the veal, and don’t forget to tip your waitresses.

Using the site’s URL-based API to query for movies including the word trek looks like this:

api.rottentomatoes.com/api/public/v1.0/movies.json?q=trek&apikey=3...

Out of the resulting 151 (!) movies,[18] if I select Star Trek Into Darkness, I get a JSON object that looks like the following (with a lot of parts elided):

18 Including one called, I kid you not, Star Trek versus Batman. The Enterprise goes back in time to the 1960s and gets taken over by the Joker and Catwoman. Seriously.

{
    "id": "771190753",
    "title": "Star Trek Into Darkness",
    "year": 2013,
    ...,
    "synopsis": "The Star Trek franchise continues ...",
    ...,
    "links": {
       "self": "http://api.rottentomatoes.com/.../771190753.json",
       "cast": "http://api.rottentomatoes.com/.../771190753/cast.json",
       "clips": "http://api.rottentomatoes.com/.../771190753/clips.json",
       "reviews": "http://api.rottentomatoes.com/.../771190753/reviews.json",
       "similar": "http://api.rottentomatoes.com/.../771190753/similar.json"
    }
}

The movie object (a resource using a JSON representation) includes an entry called links, which itself is a map of keys and values. The keys in the links objects all point to additional resources, such as a full cast listing or reviews.

The Rotten Tomatoes service adds links to the individual resources rather than appending them to the response headers. The site uses its own format rather than some other standard.[19] It also handles content negotiation by embedding the “.json” string in the URL itself.

19 Attempts at standardizing JSON links include www.subbu.org/blog/2008/10/generalized-linking and www.mnot.net/blog/2011/11/25/linking_in_json.

The client, of course, needs to know all of that, but by including a links section in the response the server is identifying exactly what’s expected next. The client can simply present those links to the user, or it can try to place them in context, which requires additional understanding.

Generating a good client for a hypermedia-based RESTful service is not a trivial task.

Notice one interesting point: the entire API uses JSON to express the objects. So far in this chapter I’ve used the term resource to represent not only the server-side object exposed to the client, but also how it’s expressed. Formally, the term representation is used to describe the form of the resource.

Representation

A representation is an immutable, self-descriptive, stateless snapshot of a resource, which may contain links to other resources.

The most common representations are XML and JSON, with JSON becoming almost ubiquitous.

The Richardson maturity model: a rigged demo

The Richardson Maturity Model (RMM) is based on a 2008 presentation made by Leonard Richardson, who described multiple levels of REST adoption.

RMM has four levels, numbered from zero to three:

  • Level 0: Plain old XML (POX) over HTTP— HTTP is merely a transport protocol, and the service is essentially remote procedure calls using it. Sounds a lot like SOAP, doesn’t it? That’s no accident.
  • Level 1: Addressable resources— Each URI corresponds to a resource on the server side.
  • Level 2: Uniform interface— The API utilizes only the HTTP verbs GET, PUT, POST, and DELETE (plus maybe OPTIONS and TRACE).
  • Level 3: Hypermedia— The representation of the response contains links defining additional steps in the process. The server may even define custom MIME types to specify how the additional metadata is included.

Now, honestly, I have no objections to this model. It’s fundamental to Roy Fielding’s thesis to include all of it; you’re not really adopting REST unless you have hypermedia, too.

The word maturity, however, carries a lot of emotional baggage. Who wants their implementation to be less mature? It also can’t be a coincidence that SOAP is considered maturity level 0. The model is fine, but there’s no need to load it down with judgmental overtones that make it feel like a rigged demo.

Hypermedia[20] in JAX-RS works through links, which come in two types:

20 Believe it or not, neither the words hypermedia nor HATEOAS appears at all in the JSR 339 specification. I have no explanation for this.

  • Transitional links in HTTP headers
  • Structural links embedded in the response

Figure 9.4 shows both in a single HTTP response.

Figure 9.4. Transitional links appear in the HTTP response headers, while structural links are part of the response objects. In each case the links can be used to access other resources from this one.

Version 2.0 of the JAX-RS specification supports transitional links using the Link and LinkBuilder classes, and structural links using a special JAXB serializer.

To illustrate both, I’ll continue with the Person example from earlier by adding links to each instance. Each person has three possible links:

  • A self link, containing the URL for that person
  • A prev link, pointing to the person with an ID one less than the current person
  • A next link, pointing to the person with an ID one greater than the current person

This is a rather contrived case, but it has the advantage of simplicity.

First I’ll add the links to the HTTP headers and show how to use them. Then I’ll use structural links instead, using the JAXB serializer. Finally, I’ll take control of the output generation process and customize the output writer using Groovy’s JsonBuilder.

9.5.2. Adding transitional links

To create transitional links, the JAX-RS API starts with the inner class Response .Response-Builder in the javax.ws.rs.core package. ResponseBuilder has three relevant methods:

public abstract Response.ResponseBuilder link(String uri, String rel)
public abstract Response.ResponseBuilder link(URI uri, String rel)
public abstract Response.ResponseBuilder links(Link... link)

The first two add a single Link header to the HTTP response. The third adds a series of headers to the response. Here’s an example from the PersonResource class:

@GET @Produces(MediaType.APPLICATION_JSON)
Response findAll() {
    def people = dao.findAll();
    Response.ok(people).link(uriInfo.requestUri, 'self').build()
}

The link method in this case uses the request URI as the first argument and sets the rel property to self. The corresponding test accesses the link as follows:

def 'get request returns all people'() {
    when:
    def response = client.get(path: 'people')

    then:
    response.status == 200
    response.contentType == 'application/json'
    response.headers.Link ==
        '<http://localhost:1234/people>; rel="self"'
}

This example returns only a single Link header. For multiple links (for example, the three transitional links prev, next, and self for each individual person), the method getHeaders('Link') retrieves them all.

In the PersonResource the links are set with a private method, shown in the next listing.

Listing 9.7. Setting prev, self, and next link headers for each person

So-called “self” links are generated for each person. Next and previous links are generated for those elements between the first and last. The links themselves are simply generated by string manipulation.

Adding the links to the resource is done with the links method:

Response findById(@PathParam("id") long id) {
    Person p = dao.findById(id)
    Response.ok(p)
        .links(getLinks(id))
        .build()
}

It turns out that converting the Link headers into something useful isn’t simple with the RESTClient. In this case the JAX-RS Client class works better. The Client class has a method called getLink, which takes a string argument, in which the string is the relation type. That method returns an instance of the javax.ws.rs .core.Link class, which corresponds to specification RFC 5988, Web Linking, of the IETF.

I’ll demonstrate the hypermedia capability by walking through the links one by one in a client. The following listing is a JUnit test case, written in Groovy, that accesses the next links.

Listing 9.8. Walking through the data using link headers

The client uses the getLink method with the relation type (next or prev), which returns a Link instance. The getUri method then returns an instance of java.net.URI, which can be followed by the client on the next iteration.[21]

21 I have to mention that this is probably one of the only times in the last decade that I really could have used a do/while loop. Ironically, that’s just about the only Java construct not supported by Groovy.

If you would rather put the links in the body of the response, you need a different approach, as described in the next section.

9.5.3. Adding structural links

Structural links in JAX-RS are instances of the Link class inside the entity itself. Converting them to XML or JSON then requires a special serializer, which is provided by the API.

Here’s the Person class, expanded to hold the self, next, and prev links as attributes:

@XmlRootElement
@EqualsAndHashCode
class Person {
    Long id
    String first
    String last

    @XmlJavaTypeAdapter(JaxbAdapter)
    Link prev

    @XmlJavaTypeAdapter(JaxbAdapter)
    Link self

    @XmlJavaTypeAdapter(JaxbAdapter)
    Link next
}

The prev, self, and next links are instances of the javax.ws.rs.core.Link class, as before. Link.JaxbAdapter is an inner class that tells JAXB how to serialize the links.

Setting the values of the link references is done in the resource, this time using an interesting Groovy mechanism:

Response findById(@PathParam("id") long id) {
    Person p = dao.findById(id)
    getLinks(id).each { link ->
        p."${link.rel}" = link
    }
}

The same getLinks private method is used as in the headers section, but this time the links are added to the Person instance. By calling link.rel (which calls the getRel method) and injecting the result into a string, the effect is to call p.self, p.next, or p.prev, as the case may be. In each case, that will call the associated setter method and assign the attribute to the link on the right-hand side.

A test of the structural links using the RESTClient looks like this:

def 'structural and transitional links for kirk are correct'() {
    when:
    def response = client.get(path: 'people/3')

    then:
    'James Kirk' == "$response.data.first $response.data.last"
    response.getHeaders('Link').each { println it }
    assert response.data.prev.href == 'http://localhost:1234/people/2'
    assert response.data.self.href == 'http://localhost:1234/people/3'
    assert response.data.next.href == 'http://localhost:1234/people/4'
}

The response wraps a Person instance, accessed by calling getData. Then the individual links are retrieved as the prev, self, and next properties. The result is a Link instance whose getHref method can be used to verify the links.

There’s only one problem, and it’s more of a nuisance than anything else. In the Rotten Tomatoes example at the beginning of the hypermedia section, the links were not top-level attributes of the movies. Instead, each movie representation contained a JSON object whose key was links, and which contained the list of individual links and relations. Here’s the snippet from the Rotten Tomatoes response:

"links": {
    "self": "http://api.rottentomatoes.com/.../771190753.json",
    "cast": "http://api.rottentomatoes.com/.../771190753/cast.json",
    "clips": "http://api.rottentomatoes.com/.../771190753/clips.json",
    "reviews": "http://api.rottentomatoes.com/.../771190753/reviews.json",
    "similar": "http://api.rottentomatoes.com/.../771190753/similar.json"
}

In the JAX-RS approach using the serializer, the relation is the attribute name. What if I want to make a collection of links as shown in the movie example? For that I need to take control of the serialization process.

9.5.4. Using a JsonBuilder to control the output

To customize output generation, JAX-RS includes an interface called javax.ws.rs.ext .MessageBodyWriter<T>. This interface is the contract for converting a Java type into a stream. It contains three methods to be implemented.

The first method is called isWriteable, and it returns true for types supported by this writer. For the Person class the implementation is simple:

boolean isWriteable(Class<?> type, Type genericType,
       Annotation[] annotations, MediaType mediaType) {
    type == Person && mediaType == MediaType.APPLICATION_JSON_TYPE
}

The method returns true only for Person instances and only if the specified media type is JSON.

The second method is called getSize, and it’s deprecated in JAX-RS 2.0. Its implementation is supposed to return -1:

long getSize(Person t, Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
    return -1;
}

The writeTo method does all the work. Here I use groovy.json.JsonBuilder to generate the output in the form I want, as shown in the following listing.

Listing 9.9. Using a JsonBuilder to produce nested links

One special quirk is notable here. The method calls toString on the individual Link instances. As the JavaDocs for Link make clear, the toString and valueOf(String) methods in Link are used to convert to and from strings.

The MessageBodyReader interface is quite similar. In that case there are only two methods: isReadable and readFrom. The implementation of isReadable is the same as the isWriteable method:

public boolean isReadable(Class<?> type, Type genericType,
        Annotation[] annotations, MediaType mediaType) {
    type == Person && mediaType == MediaType.APPLICATION_JSON_TYPE
}

The readFrom method uses a JsonSlurper to convert string input into a Person, as shown in the next listing.

Listing 9.10. Parsing a Person instance from a string
public Person readFrom(Class<Person> type, Type genericType,
        Annotation[] annotations, MediaType mediaType,
        MultivaluedMap<String, String> httpHeaders,

        InputStream entityStream)
            throws IOException, WebApplicationException {

    def json = new JsonSlurper().parseText(entityStream.text)
    Person p = new Person(id:json.id, first:json.first, last:json.last)
    if (json.links) {
        p.prev = Link.valueOf(json.links.prev)
        p.self = Link.valueOf(json.links.self)
        p.next = Link.valueOf(json.links.next)
    }
    return p
}

The readFrom method uses the JsonSlurper’s parseText method to convert the input text data into a JSON object and then instantiates a Person based on the resulting properties. If links exist in the body, they’re converted using the valueOf method.

To use the MessageBodyWriter, I need to add an @Provider annotation to the implementation class and make sure it’s loaded in the application. The latter is done by adding the provider to the MyApplication class:

public class MyApplication extends ResourceConfig {
    public MyApplication() {
        super(PersonResource.class, PersonProvider.class,
              JacksonFeature.class);
    }
}

In this case both the PersonProvider and the JacksonFeature are used. The Person provider converts individual Person instances to JSON, and the JacksonFeature handles collections. A test of the resulting structure looks like this:

def 'transitional links for kirk are correct'() {
    when:
    def response = client.get(path: 'people/3')

    then:
    'James Kirk' == "$response.data.first $response.data.last"
    Link.valueOf(response.data.links.prev).uri ==
         'http://localhost:1234/people/2'.toURI()
    Link.valueOf(response.data.links.self).uri ==
         'http://localhost:1234/people/3'.toURI()
    Link.valueOf(response.data.links.next).uri ==
         'http://localhost:1234/people/4'.toURI()
}

The response body now has a links element, which contains prev, self, and next as child elements.

Lessons learned (hypermedia)

  1. JAX-RS mostly ignores hypermedia but does make some methods available for it.
  2. Transitional link headers are added by the link and links methods in Response-Builder.
  3. Structural links in the body are added through a special JAXB annotation.
  4. You can manage the parsing and response generation stages yourself by writing a provider class that implements MessageBodyReader and/or Message-BodyWriter.

Between the transitional links, the structural links with the JAXB serializer, and the Groovy JsonBuilder, hopefully you now have enough mechanisms to implement hypermedia links in any way your application requires. The choice of which to use is largely a matter of style, but there are some guidelines:

  • Structural links are contained in the response, so the client has to parse the response to get them.
  • Transitional links are in the HTTP headers. That gets them out of the response but forces the client to parse the HTTP response headers to retrieve them.
  • Custom links can be anything, so they must be clearly documented.

Examples of all three approaches can be found on the web.

9.6. Other Groovy approaches

There are three other approaches in the Groovy ecosystem that I should mention for RESTful web services. Here I’ll specifically discuss groovlets, the Ratpack project, and Grails.

9.6.1. Groovlets

Groovlets are discussed in chapter 10 on web applications as well as the simple example in chapter 2, but essentially they’re groovy scripts that receive HTTP requests and return HTTP responses. Groovlets contain many implicit variables, including request, response, session, and params (to hold input variables).

In a groovlet you can use the getMethod method on the request object to determine if the request is a GET, PUT, POST, or DELETE. Then you can build the response accordingly.

The book source code has a project in chapter 10 called SongService, which demonstrates how to use a groovlet. The service itself is a groovlet, which is shown in the following listing.

Listing 9.11. A groovlet that processes and produces XML

The groovlet uses request.method in a switch statement to determine the correct implementation. Then it uses a built-in MarkupBuilder called html to produce XML, and an XmlSlurper to convert XML to song instances. Now that groovlets have a builtin JsonBuilder as well,[22] JSON could easily be used instead.

22 That’s my great contribution to Groovy—the implicit json object in groovlets, which I not only added, but with which I managed to break the build in the process. Sigh. If you’re interested, details can be found at http://mng.bz/5Vn6.

This approach is pretty low-level, but it may be useful for quick-and-dirty implementations or if you need such detailed control.

9.6.2. Ratpack

The second alternative is to look at the Ratpack project (https://github.com/ratpack/ratpack). Ratpack is a Groovy project that follows the same ideas as the Sinatra[23] project in the Ruby world. Ratpack is called a “micro” framework, in that you write simple Groovy scripts that govern how to handle individual requests.

23 Sinatra, Ratpack, get it? If nothing else, it’s a great name.

For example, a simple Ratpack script looks like this:

get("/person/:personid") {
    "This is the page for person ${urlparams.personid}"
}

post("/submit") {
    // handle form submission here
}

put("/some-resource") {
    // create the resource
}

delete("/some-resource") {
    // delete the resource
}

The project shows a lot of promise, and Sinatra is very popular in the Ruby world, so it’s probably worth a look. The project has recently come under the control of Luke Daley, who is a major player in the Groovy world, so I expect significant improvements soon.

9.6.3. Grails and REST

Finally, Grails has REST capabilities as well. For example, in a Grails application you can edit the URLMappings.groovy file as follows:

static mappings = {
    "/product/$id?"(resource:"product")
}

The result is that GET, POST, PUT, and DELETE requests for products will be directed to the show, save, update, and delete actions in the ProductController, respectively. Grails also automatically parses and generates XML and/or JSON, as desired.

There’s also a JAX-RS plugin available for Grails. At the moment it’s based on JAX-RS version 1, but the implementation can use either the Jersey reference implementation or Restlets. Of course, once again, nothing is said about hypermedia in either case, though anything you can do in Groovy you can, of course, do in Grails as well.

REST capabilities are a major design goal of Grails 3.0, so by then the situation will no doubt change.

9.7. Summary

The topic of RESTful web services is very hot these days, for good reason. The REST architecture enables developers to build flexible, highly decoupled applications that take advantage of the same features that made the web itself so successful.

In the Java world many libraries are available for implementing the REST architecture. This chapter focused on the JAX-RS 2.0 specification and how Groovy can be used with it. In addition to the basic URL-driven database, hypermedia can be implemented using transitional links in the HTTP headers, structural links in the entity bodies, or even through a Groovy JsonBuilder. Hopefully some combination of techniques in this chapter will enable you to build the service you want.

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

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