Thursday, October 23, 2008

Creating RESTful services with Jersey and Groovy

It's been a while since I have put anything of substance on here, so I thought I would get back to it. I've been doing a lot of development with Groovy as of late, which I absolutely love. I wanted to combine that with another API that I really like, Jersey. Jersey is the open source JAX-RS (JSR 311) Reference Implementation for building RESTful Web services. So, for a simple service to create I decided on an Announcements service. This service when invoked would look for a file located in the User Home directory and create some HTML to return that would get rendered in the browser. This example also shows why I might want to use Groovy and Jersey together, as I will leverage Groovy's MarkupBuilder to generate the HTML that gets returned. I won't get into the details of how to setup Jersey as they have lots of samples that you can find here, and instead I'll just jump right in to what the source code would look like for my announcement service.


import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.ws.rs.Produces

@Path("/announcements")
class AnnouncementService {
def announcements = "announcements.txt"
def errorReadingFileText = " - Error reading Announcements, Announcement File may not exist."
def noAnnouncementsText = " - No Announcements for Today"

@GET
@Produces (value=["text/html"])
String getHtmlResponse() {
// Return some cliched textual content
return getAnnouncements()
}

String getAnnouncements() {
def announcement = new File(System.getProperty("user.home") + File.separator + announcements)
def writer = new StringWriter()
def result = new groovy.xml.MarkupBuilder(writer);
result.html {
body {
h1(align: "center", "As of ${date()}")
table(width: "100%", height: "100%") {
td {
if (!announcement.exists()) {
tr(errorReadingFileText)
} else if (announcement.text.length() <= 0) {
tr(noAnnouncementsText)
} else {
announcement?.eachLine {
tr(" - $it")
}
}
}
}
}
}
return writer.toString()
}
}

As you can see from the code above that we annotate our class with the @Path annotation. This basically defines your jersey resource. So if you wanted to invoke this resource you URL would be something like http://localhost:8080/sample/announcements where sample is the name of your war you deployed to your application server. In the code you can also see that our method getHtmlResponse() has been annotated with @GET which tells Jersey to call this method when the HTTP Request is a GET Request. So given the same URL noted above, you could type that into a browser and hit enter and it will invoke the announcement resource with a GET request and invoke our method. One other thing to note is the @Produces annotation. This annotation defines the mime type to return your result. In our case we want the result to render as html so we set the type to text/html. This annotation is one that I had to do a little diffrent with Groovy. In Java the annotation would look like @Produces("text/html"), whereas in Groovy I have to specifically call out the value property and enclose the value in brackets like this, @Produces (value=["text/html"]). If I didn't enclose the property in the proper way I got this error when compiling:

Annotation list attributes must use Groovy notation [el1, el2]

The good thing is my IDE (I was using Netbeans) caught this before compile time, and I refrenced this issue to figure out how to get around it.

So, as you can see from my source code that the bulk of the work is taking place in the getAnnouncements() method. This was the reason that I wanted to use Groovy, I could very easily read a file, and based on the content create some html markup that would be returned to the browser. Not much to discuss here, except that MarkupBuilder is very cool. Alright, I think that's it, Good Luck. One last thing is I used Jersey 1.0 and Groovy 1.5.6 to work this example.