(Quick Reference)

9 Advanced Features

Version: 3.1.0

9 Advanced Features

9.1 Generate WADL

Automatically generated WADL functionality is only available in version 0.4 of the jaxrs plugin or higher, and requires the Jersey implementation.

A WADL document for resources managed by the plugin can be generated by sending a GET request to /application.wadl. The result should look like:

<application xmlns="http://research.sun.com/wadl/2006/10">
  <doc xmlns:jersey="http://jersey.dev.java.net/" jersey:generatedBy="Jersey: 1.1.4.1 11/24/2009 01:30 AM"/>
  <resources base="http://localhost:8080/">
    <resource path="/api/test">
      <method name="GET" id="getTestRepresentation">
        <request>
          <param xmlns:xs="http://www.w3.org/2001/XMLSchema" type="xs:string" style="query" name="name"/>
        </request>
        <response>
          <representation mediaType="text/plain"/>
        </response>
      </method>
    </resource>
  </resources>
</application>

9.2 Scaffolding Domains

The jaxrs plugin also supports scaffolding. It allows you to generate a RESTful service interface for one or more domain classes based on JAX-RS resource classes. Supported representation formats are XML and JSON. The following sections walk through a simple example.

Create a domain class

To create a Person domain class go to the project's root directory and enter:

grails create-domain-class person

Open the generated Person.groovy file (under grails-app/domain) and add two properties, firstName and lastName.

package hello

class Person {

static constraints = { }

String firstName

String lastName

}

Generate the REST API

To generate JAX-RS resources that implement the RESTful service interface for that domain class enter:

grails generate-resources hello.Person

This will generate two resource classes, PersonCollectionResource.groovy and PersonResource.groovy (in the hello package) that support HTTP POST, GET, PUT and DELETE operations for creating, reading, updating and deleting Person objects, respectively. PersonCollectionResource.groovy is related to Person lists, PersonResource.groovy is related to individual Person instances. Let's take a look at how to use the generated RESTful service interface.

Use the REST API

Start the hello application with:

grails run-app

New person objects can be created by POSTing to http://localhost:8080/api/person. The following request POSTs an XML representation of a person object.

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

<person> <firstName>Sam</firstName> <lastName>Hill</lastName> </person>

The Content-Type header must be set either to application/xml. After sending the request, the server creates a new person object in the database and returns an XML representation of it.

HTTP/1.1 201 Created
Content-Type: application/xml
Location: http://localhost:8080/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>

The client explicitly requested an XML representation via the Accept request header. Note that the returned representation differs from the submitted representation by an id attribute in the <person> element. This id is also contained in the Location response header, the URL of the created resource. The response code is 201 (CREATED). Let's create another person object using a JSON representation. Here's the request:

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

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

The response also contains a JSON representation of the created person (see Accept request header). The id of the created person object is 2.

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

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

Content negotiation via Content-Type and Accept headers works for other HTTP methods as well. To GET a list of created persons, open a browser (Firefox in our example) and enter the URL http://localhost:8080/api/person. This returns an XML representation of the list of persons stored in the database.

<list>
    <person id="1">
        <firstName>Sam</firstName>
        <lastName>Hill</lastName>
    </person>
    <person id="2">
        <firstName>Fabien></firstName>
        <lastName>Barel</lastName>
    </person>
</list>

An XML representation is returned because Firefox sets an Accept=text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 request header. To get the representation of a single person, specify the id in the URL. For example, to get the person with id 1 use http://localhost:8080/api/person/1

<person id="1">
    <firstName>Sam</firstName>
    <lastName>Hill</lastName>
</person>

If you try to get a person that doesn't exist, an error message (with a status code 404) is returned.

<error>Person with id 3 not found</error>

In the next step we update the first name of person 1 by PUTting a new representation to http://localhost:8080/api/person/1.

PUT /api/person/1 HTTP/1.1
Content-Type: application/xml
Accept: application/xml
Host: localhost:8080
Content-Length: 85

<person> <firstName>Samuel</firstName> <lastName>Hill</lastName> </person>

The response is a new representation of the updated person.

HTTP/1.1 200 OK
Content-Type: application/xml
Transfer-Encoding: chunked
Server: Jetty(6.1.14)

<?xml version="1.0" encoding="UTF-8"?> <person id="1"> <firstName>Samuel</firstName> <lastName>Hill</lastName> </person>

GETting the person list again shows the update of person 1.

<list>
    <person id="1">
        <firstName>Samuel</firstName>
        <lastName>Hill</lastName>
    </person>
    <person id="2">
        <firstName>Fabien></firstName>
        <lastName>Barel</lastName>
    </person>
</list>

Finally, we delete person 1 by sending a DELETE request to http://localhost:8080/api/person/1.

DELETE /api/person/1 HTTP/1.1
Accept: application/xml
Host: localhost:8080

GETting the person lists again shows that person 1 has actually been deleted.

<list>
    <person id="2">
        <firstName>Fabien></firstName>
        <lastName>Barel</lastName>
    </person>
</list>

9.3 Using GORM

This section explains what's happening behind the scenes of the scaffolding example and how GORM inside JAX-RS resource classes.

PersonCollectionResource.groovy

Here's the source code for PersonCollectionResource.groovy.

package hello

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

import javax.ws.rs.Consumes import javax.ws.rs.GET import javax.ws.rs.Produces import javax.ws.rs.Path import javax.ws.rs.PathParam 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 dto) { created dto.save() }

@GET Response readAll() { ok Person.findAll() }

@Path('/{id}') PersonResource getResource(@PathParam('id') String id) { new PersonResource(id:id) } }

It is based on JSR 311 classes and annotations and uses static methods from org.grails.jaxrs.response.Responses. This is a helper class provided by the plugin that implements a very simple DSL consisting of elements created and ok. Supported content types for requests and responses are application/xml and application/json. This is given by the class-level Consumes and Produces annotations.

The PersonCollectionResource class responds to HTTP operations that are related to person lists. The URL of the person list is http://localhost:8080/api/person where the /api/person path is defined by the class-level Path('/api/person') annotation.

  • The create method responds to POST requests by storing a new Person object in the database using GORM. The XML or JSON request entity is converted by the plugin to a Person domain object and passed to the method via a dto parameter. The conversion is done by a domain object provider. The persisted domain object is passed to the created method which creates a response from it using the JAX-RS API (see source code for details). The created method constructs a URI for the Location response header from the domain object id. The Person object is set to the Response entity.
  • The readAll method responds to GET requests and returns a person list. Again we use GORM to get all person objects from the database and pass that list as argument to the ok method. This method uses the JAX-RS API to create a response (see link source code for details).
  • The getResource method creates another JAX-RS resource whenever a request to the URI template http://localhost:8080/api/person/{id} is made. The id path parameter in the template is bound to the id parameter of the getResource method. The created JAX-RS PersonResource is then used by the JAX-RS runtime to handle the request to the person with the given id.

PersonResource.groovy

Here's the source code for PersonResource.groovy.

package hello

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

import javax.ws.rs.Consumes import javax.ws.rs.DELETE import javax.ws.rs.GET import javax.ws.rs.Produces import javax.ws.rs.PUT import javax.ws.rs.core.Response

import org.grails.jaxrs.provider.DomainObjectNotFoundException

@Consumes(['application/xml','application/json']) @Produces(['application/xml','application/json']) class PersonResource {

def id

@GET Response read() { def obj = Person.get(id) if (!obj) { throw new DomainObjectNotFoundException(Person.class, id) } ok obj }

@PUT Response update(Person dto) { def obj = Person.get(id) if (!obj) { throw new DomainObjectNotFoundException(Person.class, id) } obj.properties = dto.properties ok obj }

@DELETE void delete() { def obj = Person.get(id) if (obj) { obj.delete() } } }

The id property is set during construction of the resource and is used for database operations. This class implements the methods read, update and delete to handle GET, PUT and DELETE requests, respectively. It also uses GORM for database operations and relies on helper methods of org.grails.jaxrs.response.Responses to create responses via the JAX-RS API.

If there's no person with given id in the database, a DomainObjectNotFoundException is thrown. This exception class generates a custom 404 response using the JAX-RS API (see source code for details).

9.4 Applying Filters

Grails filters can be applied to JAX-RS resources as well. For example, to add a filter for the /api/test/ URL pattern, create a file TestFilters.groovy under grails-app/conf that looks like:

Filters have been deprecated in Grails 3. This documentation will be updated with details about using interceptors.

class TestFilters {

def filters = {

testUris(uri:'/api/test/**') { before = { // do some preprocessing } after = { // do some postprocessing }

} } }

9.5 Service Injection

Services can be auto-injected into resource and provider objects by name. Assue we have a service class named TestService.groovy in grails-app/services that looks like:

package hello

class TestService {

String greet(String name) { 'Hello ' + (name ? name : 'unknown') } }

We can then auto-inject the service by defining a testService property like in the following resource class.

package hello

@Path('/api/test') class TestResource {

def testService // injected

@GET @Produces('text/plain') String getTestRepresentation(@QueryParam('name') String name) { testService.greet(name) } }

9.6 Google App Engine

This section describes how to get the hello world example running on Google App Engine.

This section has been untested with Grails 3. If this documentation is out of date, please submit an issue or contribute updated documentation in a pull request.
  • Install the Grails app-engine plugin.
  • Create a Grails application and JAX-RS resource as described in the hello world example.
  • Be sure to use the jaxrs-restlet implementation plugin.
  • Add the following entries to the Grails configuration:

// replace <application-name> with the
// actual App Engine application name
google.appengine.application='<application-name>'
  • Open a shell at the root directory of the hello world application.
  • Set the application version to 1 with:

grails set-version 1
  • Run the application locally with grails app-engine run.
  • Enter the URL http://localhost:8080/test?name=Tester into your browser and the browser window should display Hello Tester .
  • Package the plugin with grails app-engine package.
  • Deploy the application with the appcfg command-line tool from your App Engine SDK.
    • On Linux, enter:

$APPENGINE_HOME/bin/appcfg.sh update ./target/war
    • On Windows, enter:

%APPENGINE_HOME%binappcfg.cmd update .targetwar
  • When prompted enter email and password to authenticate at Google App Engine.
  • Once deployment is done go enter the URL http://<application-name>.appspot.com/test?name=Tester into your browser and the browser window should display Hello Tester . It may take 10-20 seconds for Google App Engine to initialize the Grails application the first time. Subsequent requests are served much faster.

Using scaffolding together with the gorm-jpa plugin is not supported at the moment. A related feature request has already been added to the issue tracker.