Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Kenneth A. Kousen - Making Java Groovy - 2014.pdf
Скачиваний:
50
Добавлен:
19.03.2016
Размер:
15.36 Mб
Скачать

Implementing JAX-RS with Groovy

239

when:

def response = client.put(path: "people/${kirk.data.id}", contentType: ContentType.JSON, body: json)

then:

"$response.data.first $response.data.last" == 'James T. Kirk'

}

private List getAll() { client.get(path: 'people').data

}

void cleanupSpec() { server?.stop()

}

}

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)

1JAX-RS 2.0 is part of the Java EE specification and, like most of the recent specs, is annotation-based.

2It’s very easy to build a hyperlink-driven database using JAX-RS.

3Hypermedia 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.3Implementing 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" }

}

www.it-ebooks.info

240

CHAPTER 9 RESTful web services

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

www.it-ebooks.info

Implementing JAX-RS with Groovy

241

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)

1Groovy doesn’t significantly change JAX-RS.

2The real Groovy simplifications are in the POGO and DAO classes. The resource implementation is essentially the same in both languages.

www.it-ebooks.info

242

CHAPTER 9 RESTful web services

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.4RESTful 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 WebTarget. 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)

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]