Saturday, December 12, 2009

New features in grails-jaxrs 0.3

In this blog post I present some new features of the recently released grails-jaxrs 0.3 plugin. A complete list of new features is available in the release notes. A feature overview and links to the complete documentation is on the plugin home page.

grails-jaxrs is a Grails plugin that supports the development of RESTful web services based on the Java API for RESTful Web Services (JSR 311: JAX-RS). It is targeted at developers who want to structure the web service layer of an application in a JSR 311 compatible way but still want to continue to use Grails' powerful features such as GORM, automated XML and JSON marshalling, Grails services, Grails filters and so on. This plugin is an alternative to Grails' built-in mechanism for implementing RESTful web services.

The following example shows how to do content negotiation for Grails domain objects. Grails domain classes like

class Person {
String firstName
String lastName
}

can now be used in JAX-RS resource methods directly (e.g. Person parameter in the create method):

import static javax.ws.rs.core.UriBuilder.fromPath

import javax.ws.rs.Consumes
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.POST
import javax.ws.rs.core.Response

@Path('/api/person')
@Consumes(['application/xml','application/json'])
@Produces(['application/xml','application/json'])
class PersonCollectionResource {

@POST
Response create(Person person) {
person.save() // use GORM
URI uri = fromPath(person.id as String).build()
Response.created(uri).entity(person).build()
}

// ...

}

Content negotiation and conversion between domain objects and their XML or JSON representations is done by domain object providers. There's no need any more for application code to deal with representation formats directly.

The PersonCollectionResource.create method handles POST requests for creating new Person objects in the database. The method uses GORM to persist the domain object. Clients can send either XML or JSON representations for POSTing person data (see Content-Type header):

POST /hello/api/person HTTP/1.1
Content-Type: application/xml
Accept: application/xml
Host: localhost:8080
Content-Length: 78

<person>
<firstname>Sam</firstname>
<lastname>Hill</lastname>
</person>

or

POST /hello/api/person HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: localhost:8080
Content-Length: 58

{"class":"Person","firstName":"Fabien","lastName":"Barel"}

In either case, the plugin will convert it to a Person object, as required by the person parameter. For creating a response the method uses the JAX-RS API. It first creates a URI for the response Location header and uses the Response builder to set the status code to 201 (created) and the response entity. Note that the method itself doesn't create an XML or JSON representation of the response domain object. This is again done by a domain object provider which uses the Accept request header to determine the response representation format. The responses to the above POST requests are:

HTTP/1.1 201 Created
Content-Type: application/xml
Location: http://localhost:8080/hello/api/person/1
Transfer-Encoding: chunked
Server: Jetty(6.1.14)

<?xml version="1.0" encoding="UTF-8"?>
<person id="1">
<firstname>Sam</firstname>
<lastname>Hill</lastname>
</person>

and

HTTP/1.1 201 Created
Content-Type: application/json
Location: http://localhost:8080/hello/api/person/2
Transfer-Encoding: chunked
Server: Jetty(6.1.14)

{"class":"Person","id":"2","firstName":"Fabien","lastName":"Barel"}

The PersonCollectionResource.create method is even more verbose than necessary. It could equally be written as

import static org.grails.jaxrs.response.Responses.*

@Path('/api/person')
@Consumes(['application/xml','application/json'])
@Produces(['application/xml','application/json'])
class PersonCollectionResource {

@POST
Response create(Person person) {
created person.save()
}

// ...

}

using helper methods (a mini-DSL) from org.grails.jaxrs.response.Responses. That's exactly the code that is generated when using scaffolding for the Person domain class i.e.

grails generate-resources person

With the grails-jaxrs scaffolding feature, one can generate RESTful service interface for domain objects supporting the HTTP methods POST, GET, PUT and DELETE. A scaffolding example is given in the Scaffolding section of the grails-jaxrs documentation, a walk through the generated code is in the Using GORM section.

By default, grails-jaxrs uses Grail's XML and JSON converters for converting between domain objects and their XML or JSON representations. Applications can easily customize this conversion logic as explained in the Custom entity providers section.

Besides usage of GORM, grails-jaxrs also supports auto-injection of Grails services into JAX-RS resource and provider classes or usage of Grails filters, to mention a few. With version 0.3 the included JAX-RS implementations have been upgraded to their latest versions: Jersey 1.1.4.1 and Restlet 2.0-M6.

4 comments:

  1. This is great, we're in the early stage of investigating this plugin for use with a new system we're developing in grails. One thing I didn't see in the doc, do you have a suggestion for how to best handle versioning? I've seen proponents of both URI based versioning and content negotiation methods and would like to know:

    1. Do you have a suggestions for one or the other?
    2. Are there plans of building this into the framework?

    ReplyDelete
  2. Jonathan, glad to see your interest in grails-jaxrs. Although I haven't used REST API versioning in my projects so far I have a preference for URI-based versioning because of simplicity, especially when you introduce schema changes that are not backwards-compatible. Using URI-based versioning ensures that existing clients can continue to work on older versions while a content-negotiation-based approach requires clients to always include some kind of vendor MIME media type in the Accept header with additional version information. If the header is omitted the server will likely serve the latest version which might break the client. Furthermore, using a content-negotiation-based approach prevents you to e.g. send links to older versions by email. This is not an issue when using URI-based versioning. Please handle my statements with care :) I don't have much experience with REST API versioning.

    So far I didn't have plans to add versioning support to the plugin but you're welcome to add a feature request to the project's issue tracker (or start a discussion in the grails-jaxrs-discuss Google group).

    Cheers,
    Martin

    ReplyDelete
  3. Hi Martin,

    Many thanks for the post and the plugin. I get stuck with doing unit test with the REST API. Could you please give me some advice?

    Many thanks

    ReplyDelete
  4. @Jianfeng Take a look at the unit and integration tests: http://grails-jaxrs.googlecode.com/svn/trunk/jaxrs/test/

    Hope that helps. If not, post a message with more details to the grails-jaxrs-discussion group at http://groups.google.com/group/grails-jaxrs-discuss

    ReplyDelete