(Quick Reference)
JAX-RS Project
Authors: Bud Byrd, Donald Jackson, Martin Krasser, Davide Cavestro, Noam Y. Tenne
Version: 3.1.0
1 Introduction
The
jaxrs project is a set of Grails plugins 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.
Features
- Makes the JSR 311 (JAX-RS) available to Grails applications for developing RESTful web services.
- New Grails artefact types, Resource and Provider, for JAX-RS classes.
- JAX-RS Resource classes under
grails-app/resources
are auto-detected and can be modified at runtime.
- JAX-RS Provider classes under
grails-app/providers
are auto-detected and can be modified at runtime.
- Extended Grails command line interface
- Create new resources and unit test templates via
grails create-resource <resource name>
.
- Generate ready-to-use resources from domain objects via
grails generate-resources <domain class name>
.
- Scaffolding
- Generate RESTful service interfaces for Grails domain objects.
- Content negotiation support for XML and JSON representations.
- Ability to use any Grails feature within JAX-RS resources and providers such as:
- Entity providers
- Domain object providers that convert between Grails domain objects and XML or JSON representations.
- Support classes for developing custom entity providers.
- Support for content negotiation based on the
Accept
request header.
- Easy integration testing of JAX-RS resources and providers.
- Plugin users may choose between Jersey and Restlet as JAX-RS implementations by means of configuration.
- jaxrs applications can be deployed to Google App Engine (GAE).
2 Quick Start
Create The Application
Use Grails 3.x to create a new application. As an example, let's create an application called
jaxrs-example:
[budjb@laptop Projects]$ grails create-app jaxrs-example
| Application created at /Users/budjb/Projects/jaxrs-example
Add JAX-RS Plugin to Dependencies
In the
build.gradle
file, add the plugin to the project's dependencies. For the example,
we'll use the Jersey 1.x implementation.
dependencies {
compile "org.grails.plugins:jaxrs-jersey1:1.0.0" // other dependencies below…
}
Be sure to use the updated version number of the plugin you are using. The version number
above is likely out of date.
Create a Resource
The plugin comes bundled with a helper script to generate a basic resource, which we'll use here.
[budjb@laptop jaxrs-example]$ grails create-resource com.budjb.TestBUILD SUCCESSFUL| Rendered template Resource.groovy to destination grails-app/resources/com/budjb/TestResource.groovy
The resulting file should look like the class below. Notice the path information in the generated Resource.
package com.budjbimport javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces@Path('/api/test')
class TestResource {
@GET
@Produces('text/plain')
String getTestRepresentation() {
'Test'
}
}
Start the Application
Everything we need is in place, so now we can start the application.
[budjb@laptop jaxrs-example]$ ./gradlew bootRun
Grails application running at http://localhost:8080 in environment: development
> Building 97% > :jaxrs-example:bootRun
Try It Out!
Point your web browser to
http://localhost:8080/api/test
, and you should be greeted with
the word
Test
.
3 Installing
The plugin itself is split into several smaller plugins. The
jaxrs-core plugin contains the bulk
of the classes necessary for the plugin to work, but there are many different implementations of
JSR-311 that exist. The plugin was broken up to better manage the library dependencies that come
with adding many different implementations.
When adding the
jaxrs plugin to an application, one of the implementation-specific plugins should
be used.
The jaxrs-core plugin should not be included as a dependency of an application directly. It will
automatically be included as part of the dependencies of the implementation that is chosen.
The table below details the various plugins that are available and what they do.
Name | Description |
---|
jaxrs-core | Contains the core classes necessary to run the jaxrs plugin. This plugin should not be included directly in applications. |
jaxrs-jersey1 | Implements the jersey server 1.x JAX-RS library. |
jaxrs-restlet | Implements the restlet JAX-RS library. |
jaxrs-integration-test | Provides a mocked environment superclass useful for integration tests that target resources. |
4 Resources
4.1 What Are Resources
Root resources are classes that contain RESTful API services as defined by the JSR-311 JAX-RS standard. Such classes
contain methods that service an API endpoint. These classes and methods make extensive use of annotations to define
their properties and behavior, such as the path in a URL, what content types endpoints consume and produce, and what
HTTP methods each endpoint supports.
Oracle has an excellent article explaining how to create root
resource classes and detail the annotations provided byJAX-RS.
4.2 Creating Resources
Any
*Resource.groovy
file created under grails-app/resources is assumed to be a JAX-RS resource and auto-detected by the
jaxrs plugin. These resources are checked for the presence of JAX-RS annotations as defined by JAX-RS 1.1
specification, section 3.1. Resources that aren't properly annotated are ignored by the plugin.
When running applications locally, resource classes are watched and reloaded when any change to them occurs.
4.3 Generating Resources
As a convenience, the
jaxrs plugin provides a script that will create a simple resource skeleton that can be built
upon. The default template generates a simple root resource class with a single method implementing a
GET
API. The
script can be called as follows:
grails create-resource class.path.Name
In the above example, a new resource class will be generated at the path
grails-app/resources/class/path/NameResource.groovy
with an API root path of
/api/name
.
The default template can be overridden by applications by placing a new template in
src/main/templates/artifacts/Resource.groovy
. Use the
default template
as a reference on the variables used to build the resource class.
A more powerful script that will generate root resources for a domain class is documented in the
Scaffolding Domains section.
5 Entity Providers
Entity providers bind representation formats to Java classes. They are used to factor out marshalling and unmarshalling
code from resource classes.
The
jaxrs plugin provides some default entity providers that are presented in the following subsections. The
custom providers section explains how to implement custom entity providers.
5.1 Domain Object Providers
Domain object providers convert between Grails domain classes and their XML or JSON representations and support content
negotiation.
- Converting an XML or JSON representation to a domain object is done by the
org.grails.jaxrs.provider.DomainObjectReader
. This provider is used whenever a Grails domain class is used as resource method parameter.
- Converting a domain object to an XML or JSON representation is done by the
org.grails.jaxrs.provider.DomainObjectWriter
. This provider is used whenever a Grails domain object (collection) is returned from a resource method.
The behaviour of domain object providers can be customized as described in the
custom providers
section.
5.2 XML Providers
XML providers are superseded by domain object providers since
jaxrs version 0.3.
org.grails.jaxrs.provider.XMLReader
converts an XML representation of a domain object into a java.util.Map
. This map can be used for constructing domain objects.
org.grails.jaxrs.provider.XMLWriter
converts an grails.converters.XML
object into an XML representation.
Usage example:
import grails.converters.XMLclass PersonResource {
@PUT
@Consumes('application/xml')
@Produces('application/xml')
XML update(Map dto) {
Person person = new Person(map)
// … do something with person
return person as XML
}
}
5.3 JSON Providers
JSON providers are superseded by domain object providers since
jaxrs version 0.3.
org.grails.jaxrs.provider.JSONReader
converts a JSON representation of a domain object into a java.util.Map
. This map can be used for constructing domain objects.
org.grails.jaxrs.provider.JSONWriter
converts an grails.converters.JSON
object into a JSON representation.
Usage example:
import grails.converters.JSONclass PersonResource {
@PUT
@Consumes('application/json')
@Produces('application/json')
JSON update(Map dto) {
Person person = new Person(map)
// … do something with person
return person as JSON
}
}
5.4 Custom Providers
Applications can implement their own entity providers by placing them into the
grails-app/providers
directory. In
order to be auto-detected by
jaxrs they:
- must be annotated with
javax.ws.rs.ext.Provider
.
- must have a file name matching
*Reader.groovy
if the corresponding class implements javax.ws.rs.ext.MessageBodyReader
.
- must have a file name matching
*Writer.groovy
if the corresponding class implements javax.ws.rs.ext.MessageBodyWriter
.
Custom Domain Object Providers
For customizing the conversion between Grails domain objects and their XML or JSON representations, one has to disable
the default domain object providers first. To disable the default domain object reader and writer, the following entries
must be added to application configuration:
org.grails.jaxrs.doreader.disable = true
org.grails.jaxrs.dowriter.disable = true
In the following example a custom domain object writer is implemented, therefore, only the default domain object writer
needs to be disabled. A custom XML creation should be done for the
Person
domain class (see
scaffolding example), for all other classes the default XML creation should occur. Here's the custom
provider.
package helloimport javax.ws.rs.Produces
import javax.ws.rs.ext.Provider
import groovy.xml.MarkupBuilder
import org.grails.jaxrs.support.DomainObjectWriterSupport@Provider
@Produces(['text/xml', 'application/xml', 'text/x-json', 'application/json'])
class CustomDomainObjectWriter extends DomainObjectWriterSupport {
protected Object writeToXml(Object obj, OutputStream entityStream, String charset) {
if (obj instanceof Person) {
def writer = new OutputStreamWriter(entityStream, charset)
def builder = new MarkupBuilder(writer)
builder.person {
id(obj.id)
fullName("${obj.firstName} ${obj.lastName}")
}
} else {
super.writeToXml(obj, entityStream, charset)
}
}
}
The custom provider overrides the
writeToXml
method and generates custom XML using a
MarkupBuilder
. To test this
provider, create an application as described in the scaffolding example, create a folder
grails-app/provider/hello
,
and place this custom provider there. The plugin will auto-detect the provider. To create a new person object in the
database, send the following request:
POST /api/person HTTP/1.1
Content-Type: application/xml
Accept: application/xml
Host: localhost:8080
Content-Length: 83<person>
<firstName>Custom</firstName>
<lastName>Tester</lastName>
</person>
The response entity is a custom XML representation created by the custom provider:
HTTP/1.1 201 Created
Content-Type: application/xml
Location: http://localhost:8080/api/person/3
Transfer-Encoding: chunked
Server: Jetty(6.1.14)<person>
<id>3</id>
<fullName>Custom Tester</fullName>
</person>
There are several other protected
DomainObjectWriterSupport
methods for customizing the domain object marshalling, for
example
writeToJson
to create custom JSON representations or
isWriteable
to narrow the set of domain classes that a
custom domain object writer accepts. Refer to the the API docs for details.
Further Entity Provider Support
For simple use cases, the
jaxrs plugin additionally provides the abstract classes:
org.grails.jaxrs.support.MessageBodyReaderSupport<T>
org.grails.jaxrs.support.MessageBodyWriterSupport<T>
These base classes can also be used for classes other than domain classes. Implementors define the supported Java type
with a type parameter. For example, the following class is a
MessageBodyWriter
that supports conversions for a
Note
class.
@Provider
@Produces('application/xml')
class NoteWriter extends MessageBodyWriterSupport<Note> {
void writeTo(Note entity, MultivaluedMap httpHeaders, OutputStream entityStream) {
def builder = new MarkupBuilder(new OutputStreamWriter(entityStream))
builder.note {
// create custom XML here …
}
}
}
Similarly, given a POJO named
UserDto
, the following reader provides object instances from their JSON representation:
@Provider
@Consumes("application/json")
class UserDtoReader extends MessageBodyReaderSupport<UserDto> {
@Override
public UserDto readFrom(MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException,
WebApplicationException {
return new JsonSlurper().parse(new InputStreamReader(entityStream))
}
}
For details about the
MessageBodyWriterSupport
and
MessageBodyReaderSupport
classes refer to the API docs.
Alternatively, you may of course write JAX-RS providers from scratch by using the JAX-RS API directly.
6 Configuration
While the plugin works well out of the box without any configuration, there are several configuration options available
to change certain behaviors of the plugin.
JAX-RS resource scope
By default, JAX-RS resource classes are instantiated with every request which corresponds to the following entry in the
application configuration:
org.grails.jaxrs.resource.scope = 'prototype'
Since this is the default you can omit this entry as well. On the other hand, if you prefer that your JAX-RS resources
are singletons, add the following configuration entry.
org.grails.jaxrs.resource.scope = 'singleton'
Domain object providers
From version 0.3 onwards the
jaxrs plugin comes with JAX-RS providers for converting between Grails domain objects
and XML/JSON representations. Domain object providers are explained in detail in the domain object providers section.
Domain object readers and writers can be disabled by adding the following entries to the application configuration.
org.grails.jaxrs.doreader.disable = true
org.grails.jaxrs.dowriter.disable = true
This is useful in situations where applications implement custom providers. Another domain object provider configuration
property,
org.grails.jaxrs.dowriter.require.generic.collections
, is explained in the domain object collections
section.
Additional Providers
This feature is only available when the plugin uses the Jersey implementation.
By default the
jaxrs plugin scans the
grails-app/providers
directory for custom providers. JAX-RS provider
implementations located elsewhere (e.g. in 3rd party libraries) are ignored. This can be changed by defining extra paths
where the plugin should scan for additional providers. For example by adding the following to the application
configuration the plugin additionally scans the packages
com.foo
and
com.bar
for providers.
org.grails.jaxrs.provider.extra.paths = 'com.foo;com.bar'
You can also define extra paths by setting the corresponding init parameter.
org.grails.jaxrs.provider.init.parameters = ['com.sun.jersey.config.property.packages': 'com.foo;com.bar']
Init Parameters
Init parameters for the servlet of the underlying JAX-RS implementation can be set via the
org.grails.jaxrs.provider.init.parameters
configuration property in the application configuration, as in the following
example.
org.grails.jaxrs.provider.init.parameters = [
'com.sun.jersey.config.property.packages': 'com.foo;com.bar',
'another.key': 'another.value'
]
7 Swagger Integration
With the
jersey1 implementation of the JAX-RS plugin,
Swagger 2.0 support
is built-in, although it is disabled by default.
When Swagger is enabled, the
/swagger.json
endpoint is exposed containing the
details about the application's API services, assuming the resources are
annotated with the proper
Swagger annotations.
Configuration
All of the
Swagger configuration options are set under the path
org.grails.jaxrs.swagger
. The following options are available.
Configuration Property | Required | Description | Type | Default |
---|
enabled |  | If true, /swagger.json will be exposed with details regarding the API service. | Boolean | false |
beanConfigClassName | | The fully qualified class name of a custom instance of BeanConfig used to provide information about the API service. This is useful when applications need to provide additional information, such as security requirements. | String | |
resourcePackage |  | The package that swagger should scan for JAX-RS resources. Multiple packages may be provided as a comma-separated list. | String | |
version | | The API version number. | String | 1 |
title | | The title of the API service. If this is not set, the application name is used. | String | Application name. |
description | | The description of the API service. | String | |
contact | | Contact information. | String | |
license | | License name. | String | |
licenseUrl | | URL to information about the license. | String | |
scan | | Whether Swagger should scan for resources. | Boolean | true |
baseUrl | | The base URL to the API service. This is useful when the application lives behind a load balancer. | String | The application's discovered base URL. |
Swagger UI
When the
jaxrs-swagger-ui
plugin is included in an application, the
Swagger
UI is exposed with the path
/showRestApi
. The UI has been modified only in
that it is hardcoded to query the application itself for the
swagger.json
information.
8 Integration Testing
The
jaxrs-integration-test plugin contains classes that make integration testing resources much easier.
Integration tests for resources are created in the same way that other integration tests are created, except
that the test spec classes should extend
JaxrsIntegrationSpec
. This class sets up a mocked servlet context
that tests can use to send mocked requests through the JAX-RS servlet stack, effectively emulating the path
real requests would take.
Integration tests will use the JAX-RS implementation that is included in the project.
JaxrsIntegrationSpec
An example of a test may look something like:
package com.budjbimport grails.test.mixin.integration.Integration
import org.grails.plugins.jaxrs.test.JaxrsIntegrationSpec
import org.grails.plugins.jaxrs.test.JaxrsRequestProperties@Integration
class TestResourceSpec extends JaxrsIntegrationSpec {
def 'Ensure GET /api/test returns the correct content'() {
when:
def response = makeRequest(new JaxrsRequestProperties(method: 'GET', uri: '/api/test')) then:
response.bodyAsString == 'Test'
response.status == 200
} /**
* Return the list of additional resources to build the JAX-RS servlet with.
*
* @return
*/
@Override
List getResources() {
return []
}
}
Classes that extend
JaxrsIntegrationSpec
are required to implements the
getResources
method. This method
is expected to return a list of classes that represent additional resources or providers that should be loaded by the
JAX-RS implementation. This method is useful when there are test-specific resources or providers that tests require to
operate. The advantage is that these test-specific classes need not be present on the classpath when the application is
deployed, and so do not need to exist in the
src/main
folder.
Making Requests
The
JaxrsIntegrationSpec
contains a
makeRequest
method that should be used to make requests to a resource. This
method sets up servlet requests and response objects, and hands the request off properly. The
makeRequest
method takes
a
JaxrsRequestProperties
object as its parameter. This object contains several properties that make up a request.
Property | Description |
---|
uri | Path of the request. This does not need the whole hostname of running application, but only the path to the resource being tested. |
method | The string HTTP method to use with the request. Common values are GET , POST , PUT , and DELETE . |
contentType | The content type of the body of the request. |
accept | The requested content type of the body of the response. |
headers | A map of headers, where the key is the name of the header and the value is the value of the header. Supports multi-valued headers. |
characterEncoding | Character encoding of the request. Defaults to UTF-8 . |
The
makeRequest
method returns a
JaxrsResponseProperties
object containing important properties of the response, as
well as a couple of convenience methods for converting the response body.
Properties
Property | Description |
---|
status | The HTTP status code of the response. |
body | The body of the response, if applicable. This property is a byte array. |
contentType | The content type of the response, if applicable. |
headers | The headers of the response. |
Methods
Method | Description |
---|
getBodyAsString() | Returns the body of the response as a String . |
getBodyAsJson() | Parses the body of the response as JSON, and returns either a List or a Map . |
getBodyAsXml() | Parses the body of the response as XML, and returns a GPathResult . |
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 helloclass 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:
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 helloimport 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 helloimport 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.Responseimport 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 helloclass 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:
- 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.
$APPENGINE_HOME/bin/appcfg.sh update ./target/war
%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.
10 Acknowledgements
The
jaxrs plugin has a long history and has had many contributions. This section will serve to acknowledge those
contributions and give proper credit and thanks for everyone's work.
The biggest thanks go to
Martin Krasser,
Davide Cavestro, and
Noam Y. Tenne for their work on the
development and maintenance of the plugin. The plugin may have gone through some structural changes, but the code and
logic is either mostly intact or derived from their work. Additionally, the documentation includes significant sections
of the original plugin documentation from the
GitHub wiki.
Thank you also to
Donald Jackson for his work on starting the adaption of the plugin
for Grails 3.
11 Changelog
11.1 Grails 3.x
Version 3.1.0
- Upgrade to support Grails 3.3.x (huge thanks Alex Stoia!)
Version 3.0.4
- Integrate Swagger into the
jaxrs-jersey1
plugin.
- Introduce the
jaxrs-swagger-ui
plugin.
Version 3.0.3
- Added missing
jersey-client
dependency for the jersey1 plugin.
(#25)
- Refactored JAX-RS servlet initialization order due to call order differences between the embedded tomcat
server and the standalone tomcat server. (#26)
Version 3.0.2
- Add
ResourceRegistrar
and ScanningResourceRegistrar
so that applications and plugins can provide a set of
resources or providers outside of the normal artefact scanning process.
- Move integration tests out of the
jaxrs-core
plugin and into the jaxrs-integration-test
plugin.
- Add tests to the
jaxrs-restlet
plugin.
- Changed how URL mappings work:
- URL mappings are no longer configured statically via application configuration.
- URL mappings are compiled by scanning resource classes and their methods, and a new URL mapping entry will be added
per unique path. This works for both resource classes present in
grails-app/resources
and other resource classes
registered via other means.
- Update restlet to 2.3.6.
Version 3.0.1
- Fix jaxrs-core plugin descriptor class name.
- Compile the plugin with JDK 1.7 compatibility.
- Update authors.
- Fill in missing information in plugin descriptor files.
- Lowered Grails version requirement to 3.0.0.
Version 3.0.0
This is the initial release of the
jaxrs project for Grails 3. Structurally, this is a major refactor from the
existing codebase. Procedurally, the plugin has not changed much.
Major changes include:
- Plugin has been broken up into several plugins.
- jaxrs-core contains the base logic for the plugin but will not work standalone.
- jaxrs-jersey1 uses the Jersey 1.9 implementation.
- jaxrs-restlet uses the Restlet 2.1.4 implementation.
- jaxrs-integration-test provides base classes to test resources in integration tests.
- Most source files have been changed from
*.java
to *.groovy
files.
- Many class packages have changed, and all class packages have been moved under
org.grails.plugins.jaxrs
.
- Grails-generated documentation has been created.
- Ownership and maintenance of the project has been transferred.
11.2 Grails 2.x
Version 0.11
Version 0.11 of the plugin is a release of the Grails 2.4.x compatibility branch and is probably not compatible with any
previous versions.
- Grails 2.4.x compatibility modifications.
Version 0.10
Version 0.10 of the plugin is a release of the Grails 2.3.x compatibility branch and is probably not compatible with any
previous versions.
- Re-introduced Spock testing infrastructure using Spock bundled with Grails 2.3.x
- Includes all fixes and features added to the release of version 0.9.
- Grails 2.3.x compatibility modifications.
- Upgrade to Restlet 2.1.4.
Version 0.9
To improve the compatibility of this plugin across Groovy and Grails versions, the Spock test infrastructure has been
removed and is now available through the new Grails JAXRS-Spock plugin.
Please review the new plugin's README file for installation instructions.
- Added exclusions for un-required dependencies.
- Modified the order of the servlet configuration; the load-on-startup field is now specified last.
- Add the ability to specify query parameters in the test request path.
- Take Encoding into consideration when decoding the input stream.
- Remove copy-pasted MockHttpServletRequest from plugin.
- Allow for multiple resources creation at once.
- Downgraded the Grails version requirement to 2.0.x.
Version 0.8
This version enables the plugin to be used by Grails 2.2.x applications but breaks backwards compatibility for versions
prior to 2.2.x.
Version 0.7
- Enhancement: Added infrastructure to enable Spock integration testing
- Fix: Cannot use jaxrs 0.6 with servlet 2.5 (it needs servlet api 3.0).
- Fix: Removed usages of the deprecated Grails ConfigurationHolder.
Version 0.6
- Upgrade to Grails 2.0.0
- Upgrade to Jersey 1.8
- Enhancement: Allow applications to configure init parameters for the
JerseyServlet
- Enhancement: Separate service class used by generated resources
- Enhancement: Transaction boundaries in generated resource code
- Enhancement: Support deep object conversion in domain object providers
- Fix: Support for alphanumeric domain object identifiers
- Fix: POST method doesn't work
- Fix:
java.lang.IllegalStateException: getOutputStream() has already been called for this response
Version 0.5-m1
- New integration test framework
- Upgrade to Grails 1.3.7
- Upgrade to Jersey 1.5
- Enhancement: Add support for configuring Jersey with additional provider paths
- Enhancement: Switch to Grails' dependency management mechanism
- Enhancement:
JaxrsController
moved to package org.grails.jaxrs
- Fix: Responses with Content-Type
text/html
eaten by Grails
- Fix: Service injection into resources doesn't work in integration tests
Version 0.4
- Automated generation of WADL documents (with some known limitations)
- Upgrade to Grails 1.3.1
- Upgrade to Jersey 1.2
- Upgrade to Restlet 2.0-RC3
- Fix: Object ids in XML or JSON requests are not set on the domain object.
- Fix: Unmarshalling from JSON (and XML) to nested domain objects doesn't work properly
There's a bug (issue 971) in Restlet 2.0-RC3 that forces implementors of MessageBodyReader
and MessageBodyWriter
to
directly implement these interfaces. Extending a class that implements these interfaces doesn't work. Restlet will
ignore the provider in this case.
Version 0.3
- Domain object providers
- Convert between Grails domain objects and XML or JSON representations
- Allow usage of Grails domain classes in resource method signatures
- Support for content negotiation using the Accept request header
- Can be disabled by means of configuration
- Custom entity provider support
- Base classes for custom domain object providers
org.grails.jaxrs.support.DomainObjectReaderSupport
org.grails.jaxrs.support.DomainObjectWriterSupport
- Base classes for more general entity providers (improved)
org.grails.jaxrs.support.MessageBodyReaderSupport
org.grails.jaxrs.support.MessageBodyWriterSupport
- Scaffolding enhancements
- XML and JSON representations
- Content negotiation support
- Auto-detection of JAX-RS resource classes better aligned with the JAX-RS specification (incl. support for annotation
inheritance)
- Auto-detection of JAX-RS provider classes better aligned with the JAX-RS specification
- Default URL mapping for
JaxrsController
changed
- Upgrade to Grails 1.1.2
- Upgrade to Jersey 1.1.4.1
- Upgrade to Restlet 2.0-M6
Version 0.2
In version 0.2 the way how JAX-RS resources and providers are auto-detected has changed. In version 0.1 plain Spring
mechanisms were used (
<context:component-scan />
,
Component
annotation, etc.) whereas in version 0.2 the plugin
follows Grails conventions regarding how to detect and manage resources. Now, there's no need to provide a custom Spring
application context. The
jaxrs plugin is now making these changes behind the scenes.
Similar changes have been introduced for injection of other beans into JAX-RS resources and providers. In version 0.1
plain Spring mechanisms were necessary such as the
Autowired
annotation whereas in version 0.2 other beans are
auto-injected by name similar to Grails controllers, for example.