Showing posts with label jersey. Show all posts
Showing posts with label jersey. Show all posts

Thursday, February 23, 2012

Using JAXB for XML and JSON APIs in Scala Web Applications

In the past, I already mentioned several times the implementation of RESTful XML and JSON APIs in Scala web applications using JAXB, without going into details. In this blog post I want to shed more light on this approach together with some links to more advanced examples. A JAXB-based approach to web APIs can be useful if you want to support both XML and JSON representations but only want to maintain a single binding definition for both representations. I should also say that I'm still investigating this approach, so see the following as rather experimental.

First of all, JAXB is a Java standard for binding XML schemas to Java classes. It allows you to convert Java objects to XML documents, and vice versa, based on JAXB annotations on the corresponding Java classes. JAXB doesn't cover JSON but there are libraries that allow you to convert Java objects to JSON (and vice versa) based on the very same JAXB annotations that are used for defining XML bindings. One such library is Jersey's JSON library (jersey-json) which internally uses the Jackson library.

As you'll see in the following, JAXB can also be used together with immutable domain or resource models based on Scala case classes. There's no need to pollute them with getters and setters or Java collections from the java.util package. Necessary conversions from Scala collections or other type constructors (such as Option, for example) to Java types supported by JAXB can be defined externally to the annotated model (and reused). At the end of this blog post, I'll also show some examples how to develop JAXB-based XML and JSON APIs with the Play Framework.

Model

In the following, I'll use a model that consists of the single case class Person(fullname: String, username: Option[String], age: Int). To define Person as root element in the XML schema, the following JAXB annotations should be added.


@XmlRootElement makes Person a root element in the XML schema and @XmlAccessorType(XmlAccessType.FIELD) instructs JAXB to access fields directly instead of using getters and setters. But before we can use the Person class with JAXB a few additional things need to be done.
  • A no-arg constructor or a static no-arg factory method must be provided, otherwise, JAXB doesn't know how to create Person instances. In our example we'll use a no-arg constructor.

  • A person's fullname should be mandatory in the corresponding XML schema. This can be achieved by placing an @XmlElement(required=true) annotation on the field corresponding to the fullname parameter.

  • A person's username should be an optional String in the corresponding XML schema i.e. the username element of the complex Person type should have an XML attribute minOccurs="0". Furthermore, it should be avoided that scala.Option appears as complex type in the XML schema. This can be achieved by providing a type adapter from Option[String] to String via the JAXB @XmlJavaTypeAdapter annotation.

We can implement the above requirements by defining and annotating the Person class as follows:


Let's dissect the above code a bit:
  • The no-arg constructor on the Person class is only needed by JAXB and should therefore be declared private so that it cannot be accessed elsewhere in the application code (unless you're using reflection like JAXB does).

  • Placing JAXB annotations on fields of a case class is a bit tricky. When writing a case class, usually only case class parameters are defined but not fields directly. The Scala compiler then generates the corresponding fields in the resulting .class file. Annotations that are placed on case class parameters are not copied to their corresponding fields, by default. To instruct the Scala compiler to copy these annotations, the Scala @field annotation must be used in addition. This is done in the custom annotation type definitions xmlElement and xmlTypeAdapter. They can be used in the same way as their dependent annotation types XmlElement and XmlJavaTypeAdapter, respectively. Placing the custom @xmlElement annotation on the fullname parameter will cause the Scala compiler to copy the dependent @XmlElement annotation (a JAXB annotation) to the generated fullname field where it can be finally processed by JAXB.

  • To convert between Option[String] (on Scala side) and String (used by JAXB on XML schema side) we implement a JAXB type adapter (interface XmlAdapter). The above example defines a generic OptionAdapter (that can also be reused elsewhere) and a concrete StringOptionAdapter used for the optional username parameter. Please note that annotating the username parameter with @xmlTypeAdapter(classOf[OptionAdapter[String]]) is not sufficient because JAXB will not be able to infer String as the target type (JAXB uses reflection) and will use Object instead (resulting in an XML anyType in the corresponding XML schema). Type adapters can also be used to convert between Scala and Java collection types. Since JAXB can only handle Java collection types you'll need to use type adapters should you want to use Scala collection types in your case classes (and you really should). You can find an example here.

We're now ready to use the Person class to generate an XML schema and to convert Person objects to and from XML or JSON. Please note that the following code examples require JAXB version 2.2.4u2 or higher, otherwise the OptionAdapter won't work properly. The reason is JAXB issue 415. Either use JDK 7u4 or higher which already includes this version or install the required JAXB version manually. The following will write an XML schema, generated from the Person class, to stdout:


The result is:


Marshalling a Person object to XML can be done with


which prints


Unmarshalling creates a Person object from XML.


We have implemented StringOptionAdapter in a way that an empty <username/> element or <username></username> in personXml1 would also yield None on Scala side. Creating JSON from Person objects can be done with the JSONJAXBContext from Jersey's JSON library.


which prints the following to stdout:


Unmarshalling can be done with the context.createJSONUnmarshaller.unmarshalFromJSON method. The JSONConfiguration object provides a number of configuration options that determine how JSON is rendered and parsed. Refer to the official documentation for details.

Play and JAXB

This section shows some examples how to develop JAXB-based XML and JSON APIs with the Play Framework 2.0. It is based on JAXB-specific body parsers and type class instances defined in trait JaxbSupport which is part of the event-sourcing example application (Play-specific work is currently done on the play branch). You can reuse this trait in other applications as is, there are no dependencies to the rest of the project (update: except to SysError). To enable JAXB-based XML and JSON processing for a Play web application, add JaxbSupport to a controller object as follows:


An implicit JSONJAXBContext must be in scope for both XML and JSON processing. For XML processing alone, it is sufficient to have an implicit JAXBContext.

XML and JSON Parsing

JaxbSupport provides Play-specific body parsers that convert XML or JSON request body to instances of JAXB-annotated classes. The following action uses a JAXB body parser that expect an XML body and tries to convert it to a Person instance (using a JAXB unmarshaller).


If the unmarshalling fails or the request Content-Type is other than text/xml or application/xml, a 400 status code (bad request) is returned. Converting a JSON body to a Person instance can be done with the jaxb.parse.json body parser.


If the body parser should be chosen at runtime depending on the Content-Type header, use the dynamic jaxb.parse body parser. The following action is able to process both XML and JSON bodies and convert them to a Person instance.


JaxbSupport also implements the following related body parsers
  • jaxb.parse.xml(maxLength: Int) and jaxb.parse.json(maxLength: Int)

  • jaxb.parse(maxLength: Int) and jaxb.parse(maxLength: Int)

  • jaxb.parse.tolerantXml and jaxb.parse.tolerantJson

  • jaxb.parse.tolerantXml(maxLength: Int) and jaxb.parse.tolerantJson(maxLength: Int)

XML and JSON Rendering

For rendering XML and JSON, JaxbSupport provides the wrapper classes JaxbXml, JaxbJson and Jaxb. The following action renders an XML response from a Person object (using a JAXB marshaller):


whereas


renders a JSON response from a Person object. If you want to do content negotiation based on the Accept request header, use the Jaxb wrapper.


Jaxb requires an implicit request in context for obtaining the Accept request header. If the Accept MIME type is application/xml or text/xml an XML representation is returned, if it is application/json a JSON representation is returned. Further JaxbSupport application examples can be found here.

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.

Wednesday, July 8, 2009

JSR 311 plugin for Grails

Project homepage: grails-jaxrs

Recently, I wanted to make a full two-weeks mountain bike tour through the Alps but bad weather on a few days caused me to make detours on the less rocky roads of JSR 311 (JAX-RS: The Java API for RESTful Web Services). I took the chance reading the spec, did some hacking with Jersey and I must really say I like to work with it. I liked it so much that I started to use it inside Grails to combine it with features such as GORM and XML and JSON marshalling.

From my experiments I factored out what I think is reusable into a Grails plugin named grails-jaxrs and made it open source. The plugin takes care of initializing Jersey inside a Grails application, implements a controller that does the dispatch to Jersey and provides some Grails-specific entity provider implementations.

Here's a very simple example of a resource class that represents a collection of notes and that makes use of Grails object relational mapping (GORM) and XML marshaling:

import grails.converters.*

import javax.ws.rs.Consumes
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.PathParam
import javax.ws.rs.POST
import javax.ws.rs.Produces
import javax.ws.rs.core.Response
import javax.ws.rs.core.UriBuilder

@Path('/notes')
class NotesResource {

@POST
@Consumes('text/plain')
@Produces('text/xml')
Response addNote(String text) {

// Create new Note object and store save it to DB
def note = new Note(text:text).save()

// Construct the URI for the newly created note
URI uri = UriBuilder.fromPath(note.id as String).build()

// Return an XML representation of the note object
// along with a Location response header with the URI
Response.created(uri).entity(note as XML).build()
}

@GET
@Produces('text/xml')
Response getNotes() {

// Find all notes in the database and return
// an XML representation of the note list
Response.ok(Note.findAll() as XML).build()
}

}

A note is an object that contains some text. Notes are instances of the Note Grails domain class. A collection of notes are made available through a RESTful service interface using the above NotesResource class and the grails-jaxrs plugin. A POST to http://host:port/notes/ creates a new note. A GET to http://host:port/notes returns an XML repsresentation of all existing notes in the database. For more details refer to the getting started tutorial.