Skip to main content

Building RESTful web services with JAX-RS, JAXB and Groovy

Posted by lamine_ba on May 20, 2012 at 11:14 AM PDT

Any intelligent fool can make things bigger, more complex, and more violent
But it takes a touch of genius and a lot of courage to move in the opposite direction.
Albert Einstein

Introduction

Ladies and Gentlemen, beyond the fact that I'm going today to share with you, most of the things a developer should know when building a JAX-RS web service with Groovy, this blog post is first and foremost a farewell to the Java language. It was a fun ride but now I think it is high time for me to move on. And how odd and ridiculous is it to build the dynamic parts of my application with a static language?

How to set up the environment?

Unless you are using Grails and its grails-jaxrs plugin or a proprietary solution, I have no answer to how to write a groovy service which gets compiled and registered at runtime within the JAX-RS system. But in the other hand, if you are asking me how to do so in my own framework, my answer would be that : " there is nothing to configure, everything has already been set up and the core classes of JAX-RS and JAXB have been transparently imported". So the next step is just to write a groovy web service to see what we will get...

  1. CompilerConfiguration configuration=new CompilerConfiguration();
  2. ImportCustomizer customizer=new ImportCustomizer();
  3. customizer.addStarImport("javax.ws.rs""javax.ws.rs.core","javax.xml.bind.annotation");
  4. configuration.addCompilationCustomizers(customizer);

The Problem

Beyond the classical hello word service which might be written with groovy like this :

  1. @Path("/hello")
  2. class HelloWorldService {
  3.  
  4.   @GET
  5.   @Path("{name}")
  6.   def String hello(@PathParam("name") String name) {
  7.         "Hello $name"
  8.   }
  9.  
  10. }

And beyond the simple statement that any String with an embedded expression is a GString, today we are going to follow my dear friend Joe in his obsession to make the list of his customers available through REST. And in the same time, we will also look at the potential errors he might encounter in the resolution of his problem and the set of techniques he has really under his belt.

  1. class Customer {
  2.  
  3.    Long id     
  4.    String firstName
  5.    String lastName
  6.  
  7. }
  1. class CustomerDao {
  2.  
  3.    def customers=[]
  4.  
  5.    CustomerDao() {
  6.  
  7.         customers << new Customer(id:1,firstName:"Bob",lastName:"Lee")
  8.         customers << new Customer(id:2,firstName:"Jim",lastName:"Dry")
  9.         customers << new Customer(id:3,firstName:"Joe",lastName:"Hart")
  10.         customers << new Customer(id:4,firstName:"Frank",lastName:"Lucas")
  11.        
  12.    }
  13.  
  14. }

Sending Representations to the target client using the Groovy Builders

XML with MarkupBuilder

  1. @Path("/customers")
  2. class Service {
  3.        
  4.         @Produces(["application/xml"])
  5.         @GET
  6.         def getCustomers() {
  7.              def writer = new StringWriter()
  8.              def builder = new groovy.xml.MarkupBuilder(writer)
  9.              builder.customers { dao.customers.each() { entity ->
  10.                      customer(){
  11.                          id  entity.id
  12.                          firstName entity.firstName
  13.                          lastName  entity.lastName
  14.                       }
  15.                 }
  16.              }
  17.              writer.toString()
  18.         }
  19. }

Output

  1. <customers>
  2.  
  3.    <customer>
  4.      <id>1</id>
  5.      <firstName>Bob</firstName>
  6.      <lastName>Lee</lastName>
  7.    </customer>
  8.  
  9.    <customer>
  10.     <id>2</id>
  11.     <firstName>Jim</firstName>
  12.     <lastName>Dry</lastName>
  13.    </customer>
  14. ....
  15. </customers>

JSON with JsonBuilder

  1. @Path("/customers")
  2. class Service {
  3.        
  4.         @Produces(["application/json"])
  5.         @GET
  6.         def getCustomers() {
  7.              def builder = new groovy.json.JsonBuilder()
  8.              builder(customers : dao.customers)
  9.              builder.toString()
  10.         }
  11. }

Output

  1. {"customers":[{"firstName":"Bob","id":1,"lastName":"Lee"},{"firstName":"Jim","id":2,"lastName":"Dry"},
  2. {"firstName":"Joe","id":3,"lastName":"Hart"},
  3. {"firstName":"Frank","id":4,"lastName":"Lucas"},{"firstName":"Mark","id":5,"lastName":"Zuckerberg"},{"firstName":"Alexis","id":6,"lastName":"Sanchez"}]}

If you are not impressed and shocked in the same time by the brutality of Groovy, Ladies and Gentlemen, this time, I don't even know what to say to you but please always remember this : As a rule of thumb, a service should always be decoupled from the code that marshall and unmarshall the data. So if you are aware of the concept of a MessageBodyWriter, I think you know perfectly how to refactor the code. And also one more thing, maybe we can use a XmlSlurper or a JsonSlurper for the opposite operation?

XML

  1. def c = """<customer>
  2.              <id>1</id>
  3.             <firstName>John</firstName>
  4.             <lastName>Doe</lastName>
  5.           </customer>
  6.        """
  7. def customer = new XmlSlurper().parseText(c)

JSON

  1. def slurper = new JsonSlurper()
  2. def result = slurper.parseText('{"customer":{"id":"1","firstName":"John","lastName":"Doe"}}')

Using JAXB for the Marshaling/Unmarshaling

That is where the funny part is and also where all the real problems start.

  1. @XmlRootElement
  2. class Customer {
  3.  
  4.   Long id
  5.   String firstName
  6.   String lastName
  7.  
  8.  
  9. }
  1. @Path("/customers")
  2. class Service {
  3.        
  4.         @GET
  5.         @Produces([MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML])
  6.         def getCustomers() {
  7.              dao.customers
  8.         }
  9.        
  10. }

When running this example and when accessing through its url, the service deployed on CloudBees, here is the first explicit error we will get : A message body writer for Java class java.util.ArrayList was not found . And this error is caused by the following declaration in the Dao :

  1. class CustomerDao {
  2.  
  3.    def customers=[]
  4.  
  5. }

and by the fact that we haven't explicitly set the return type in the Service.

  1. @Path("/customers")
  2. class Service {
  3.        
  4.         @GET
  5.         @Produces([MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML])
  6.         def List<Customer> getCustomers() {
  7.             dao.customers
  8.         }
  9.        
  10. }

Groovy making the type optional does not mean that JAX-RS does it also, so you must pay attention to that. The object type and the media type will ultimately determine how to select a MessageBodyWriter or a MessageBodyReader. Now to close this post, Let's run again the example for the last time and let's get this following error : groovy.lang.MetaClass is an interface, and JAXB can't handle interfaces. which we can easily solve by setting the XmlAccessType to NONE and by explicitly doing the mapping of the fields of the domain object by ourselves. So what you have to bear in mind here is that : There is NO MORE configuration by exception.....

  1. @XmlAccessorType(XmlAccessType.NONE)
  2. @XmlRootElement
  3. class Customer {
  4.  
  5.   @XmlElement Long id
  6.   @XmlElement String firstName
  7.   @XmlElement String lastName
  8.  
  9. }

Let's develop in the cloud..

You can find this module deployed in my application in the cloud at this url so that you can complete your knowledge there and download it if you want.


Also don't hesitate to click on the WADL button to see everything that your service can do.


If you click on the customer : application/json link at the right or the another link, all the attributes of your entity will be listed.


And next time for sure, I will show you how to build a web client for this service and how to consume it within a JSF environment with my JAX-RS web application framework : YouControl and an Ajax library like JQuery. So until then, Ladies and Gentlemen, Have a nice week....

Documentation

No one can create something without taking inspiration somewhere........

  1. JAX-RS as the one Java web framework to rule them all?
  2. Leaving JSPs in the dust: moving LinkedIn to dust.js client-side templates
  3. Modern Web Apps using JAX-RS, MongoDB, JSON, and jQuery
  4. RESTful services with jQuery and Java using JAX-RS and Jersey
  5. Tutorial: HTML Templates with Mustache.js
  6. Client-Side MVC frameworks compared
  7. The client-side templating throwdown: mustache, handlebars, dust.js, and more
  8. Asynchronous UIs - the future of web user interfaces



twitter : lamine_ba

 

AttachmentSize
module-controls.png8.08 KB
wadl-information.png5.09 KB
customer-info.png14.21 KB

Comments

Nice blog, it's useful! good conception in YouControl app! ...

Nice blog, it's useful!
good conception in YouControl app! i suggest you to give it a Senegalese name! ;)
wish u good continuation!

Hi Lamine, Your approach to the front end Java ...

Hi Lamine,

Your approach to the front end Java development is a breath of fresh air.
Question: is Youcontrol open source? If yes, where can I download it?

Regards,
- Juliano