Easy REST: Groovy, Grails, and JAXRS

August 8, 2011

I needed an easy way to get an API up and running for a project that exposed JMX as an API (in order to easily interoperate with non-Java clients [ie: iOS…more details coming]). There are a variety of tools, resources, languages, etc to do this, but the one that I found easiest was via Grails using a JAXRS plugin which is a Java-based standard for RESTful services.

Grails is already a superb language for building websites and as part of that has strong built-in support for XML and JSON rendering. However, you still have to have controllers to interact with the model, which is to some degree cumbersome. This is where the rich plugin ecosystem that Grails exposes comes in. Grails has a great plugin called JAXRS. It is based on the Java-standard and uses Jersey (or alternatively a few other options) at its core to handle the API paths. Jersey is typically built on Java-based apps, but being Groovy/Grails is JVM-based, it works equally well there. The JAXRS plugin exposes a create-resource command to quickly create a resource artifact. That artifact is a Groovy-based class that uses the built-in Jersey annotations. At that point it is just a matter of interacting with the Grails system (domain classes, services, GORM, etc). The following is an extremely simple way to get up and running (this is based on Grails 1.3.7):

grails create-app test
cd test
grails install-plugin jaxrs
grails create-resource com.znet.test.api.Users
grails create-domain-class com.znet.test.domain.User
grails create-domain-class com.znet.test.domain.Address

At this point you can modify the grails-app/domain/com/znet/test/domain/User.groovy file to add a few properties:

class User {
	int id;
	String firstName;
	String lastName;
	Address address;
}

Also, edit grails-app/domain/com/znet/test/domain/Address.groovy to add its properties:

class Address {
	static belongsTo = [user:User]
 
	int id;
	String city;
	String state;
}

Now, edit the grails-app/resources/com/znet/test/api/UsersResource.groovy

import com.znet.test.domain.*;
 
@Path('/api/users')
public class UsersResource {
	@GET
	@Produces(['application/xml', 'application/json'])
	List<User> getUser() {
		User.list()
	}
}

This class will basically intercept any URL request going to /api/users and invoke the getUser method. That method uses standard GORM to return a list of users.

Before we test though, we need to get some initial content in our development database, add the following to the grails-app/conf/BootStrap.groovy file:

import com.znet.test.domain.*;
 
class Bootstrap {
	def init = { servletContext ->
		Address addr1 = new Address(id:1,city:'New York', state:'NY')
		addr1.save()
 
		User user1 = new User(id:1,firstName:'John',lastName:'Doe',address:addr1)
		user1.save()
 
		Address addr2 = new Address(id:2,city:'Los Angeles',state:'CA')
		addr2.save()
 
		User user2 = new User(id:2,firstName:'Jane',lastName:'Doe',address:addr2)
		user2.save()
	}
}

Running grails run-app should launch the server at which point the following URL should bring up an XML list with both users: http://127.0.0.1:8080/test/api/users

All that work with only a handful of code. It could not get much more easier than that. Add another property to the user object and wallah, it shows up. This model really allows you to quickly build up an API with little effort. Further, using the power and simplicity of the GORM domain model, you can map a database model into a composite domain model into an API model. The best part is you only have one set of classes to do so and Grails/JAXRS takes care of the mappings to/from the database and to/from the API. Further, you get both JSON/XML out of the box. For example, simply configure your browser to support application/json, it would render the output in JSON rather than XML. This is one of the downfalls of the JAXRS plugin however in that you have to tweak your browser manually. I actually created a simple mime-type filter that intercepts requests and sets the Accept header mime-type accordingly. This allows you to suffix a URL with .json or .xml or set a query param such as format=json. Drop me a comment if you are interested in learning more about that filter.

The JAXRS documentation has much more information on other cool facets. One of those is the ability to generate scaffolding of domain-based resources including lists, gets, puts, and posts via the generate-resource command. This truly provides the complete database to API modeling.

That is the simple method. My case, however, did not use GORM or databases, but instead JMX. So what does JAXRS offer us here? The JAXRS actually handles domain classes differently from plain POJOs or POGOs. In fact, out of the box, you cannot simply create a POGO and expose it automatically. However, doing so only involves a few lines of code to expose a custom provider. Create a new directory in grails-app called providers. Within that directory, create a new Groovy class (creating any associated subdirectories per the package name).

@Provider
@Produces(['text/xml', 'application/xml', 'text/x-json', 'application/json'])
class DomainObjectWriter extends DomainObjectWriterSupport {
 
	boolean isWriteable(Class type, Type genericType,
	                    Annotation[] annotations, MediaType mediaType) {
		return true;
	}
 
	protected Object writeToXml(Object obj, OutputStream entityStream, String charset) {
		def xml = obj as XML;
		xml.render(new OutputStreamWriter(entityStream));
	}
 
	protected Object writeToJson(Object obj, OutputStream entityStream, String charset) {
		def json = obj as JSON;
		json.render(new OutputStreamWriter(entityStream));
	}
}

This basically tells the Jersey and JAXRS environment to load the provider class and use it for handling marshaling to JSON and/or XML. The actual implementation only involves delegation to the great built in support exposed by Grails and the XML and JSON converters. Those converters have built-in support for POJOs, POGOs, as well as any other custom registration schemes. For example, to register a custom handler, create a new Groovy class (ie: within src/groovy):

class CustomObjectMarshaller implements ObjectMarshaller<JSON> {
 
	public boolean supports(Object object) {
		// check if this marshaller can handle the specified object
		return true;
	}
 
	public void marshalObject(Object object, JSON json) {
		// render object
		JSONWriter writer = json.getWriter();
		writer.object();
		writer.key('hello');
		json.convertAnother(object.toString());
	}
}

This is a really simple example, but you essentially register a marshaller per mime type (JSON, XML, etc) and then use the custom writers to write out properties and values. Ideally, you would invoke convertAnother using a sub-property of object which would find the associated marshaller for that object.

Going back to our example JMX use case, I can now create composite domain models in src/groovy such as:

class JmxNode {
	String name;
	String description;
	String objectName;
	List<JmxAttribute> attributes;
}
 
class JmxAttribute {
	String name;
	String description;
	String dataType;
}

If I were to build that domain model and expose it to a resource, it would render out the complete domain graph. Note that you will end up with other properties such as class and version which are part of the POJO/POGO. If you want to remove those, simply create a custom handler/marshaller to not render those properties.

These examples are fairly trivial, but as you begin to add business logic, they will begin to become more complex. As resources are grails artifacts and as such spring beans, you can inject them as any other controller. This allows me to keep resources clean and delegate to services. As such I inject services as such:

@Path('…')
class JmxNodeResource {
	// inject the associated service
	def jmxNodeService;
	..
}

The actual service is responsible for building up each part of the domain part. A given service may delegate to other services to build up the composite model. For example;

class JmxNodeService {
	// inject the attribute service
	def jmxAttributeService;
 
	JmxNode getJmxNode() {
		JmxNode node = new JmxNode();
		// build up node
		// get list of JMX attributes
		node.attributes = attrs.collect { attr ->
			delegate building of JmxAttribute to the attribute service
			jmxAttributeService.getJmxAttribute(attr)
		}
	}
}
 
class JmxAttributeService {
	JmxAttribute getJmxAttribute() {}
}

Now, we have clean separation from marshaling, resources, services, and domain. In a sense, MVC for RESTful applications. So far, though, we have only hit the tip of the iceberg, so let’s take one more example and dig a bit deeper.

My API example allows me to specify the JMX host as part of the URL for which I want information on. The API basically acts as a JMX router allowing JMX properties over a common XML/JSON format. We saw earlier on that we use @Path to define the URL. Does that mean I have to have a resource for every single JMX host? Of course not…we use substitution params and inject the values into our methods. For example:

@Path('/api/jmx/{service}/mbeans/{node}')
class JmxMBeanResource {}

This path basically allows me to specify a service as well as an MBean node. For example, the following URL would invoke this resource class:

/api/jmx/serverB:5555/mbeans/java.lang.Management

To actually perform the lookup and handling, I delegate the resource to a JmxMBeanService class:

@Path('/api/jmx/{service}/mbeans{node}')
class JMXMBeanResource {
	def jmxMBeanService; // inject service bean
 
	@GET
	JmxNode getJmxNode() {
		return jmxMBeanService.getJmxNode();
	}
}

The service is a standard service (i.e.: grails create-service) that uses JMX (check out the GroovyMBean helper class) to lookup the MBean information and returns it as a JmxNode with a list of its JmxAttributes. It’s as easy as that and the resource stays very clean. However, we are still missing one part…the injection of the path parameters. Here is where Jersey and its annotations come into play. We inject them into the method signature:

@GET
JmxNode getJmxNode(@PathParam('service') JmxService service, @PathParam('node') String node, 
	           @QueryParam('user') String user, @QueryParam('pass') String pass) {
	return jmxMBeanService.getJmxNode(service, node, user, pass);
}

The first parameter injects a portion of the path as denoted by the ‘service’ substitution variable. In our test URL above that would be ‘serverB:5555‘. However, notice that we are injecting a value of type JmxService rather than String. Jersey accepts any primitive type or pre-defined type (String, Date, etc). If you need a custom type, just make sure that the custom type supports a public constructor taking a single String parameter or supports a static Type valueOf(String) method similar to Integer.valueOf(String).

The third and fourth parameters allow injection of custom query parameters to allow values to be passed in other than via standard path parameters. Jersey actually supports quite a few injectable values including cookies, headers, etc. Jersey will perform type validation and you can even specify formats and regular expressions within the substitution params to add further validation logic.

As you can see we have written only a handful of code, most being the business logic aspect, and have a full defined API. If you would like a complete sample of the code, just let me know. I will most likely be open sourcing the API portion to GitHub later this month.

Thanks and as always enjoy. If you have any questions, drop me a comment below.

Nick

3 Responses to “Easy REST: Groovy, Grails, and JAXRS”

  1. Very interesting post, thanks for sharing your Grails JAXRS experiences.

    If you want to have URL extensions (such as .json and .xml) to determine the media type you can also do that by overriding PackagesResourceConfig.getMediaTypeMappings() as described in

    http://groups.google.com/group/grails-jaxrs-discuss/browse_thread/thread/268cbd1414870d3d/6c20b91a1ace1876?#6c20b91a1ace1876

    You can configure Jersey with that custom implementation as described in

    http://code.google.com/p/grails-jaxrs/wiki/AdvancedFeatures?ts=1301645620&updated=AdvancedFeatures#Init_parameters

    Hope that helps.

    Cheers,
    Martin

  2. […] REST fácil: Groovy, Grails, y JAXRS […]

  3. Hi Nicholas, it’s a very interesting and useful post.

    I am interested to know how your mime-type filter(that intercepts requests and sets the Accept header mime-type) works. Can you give me some pointers?

    Thanks much!