Writing json output from a groovlet

I’ve been working with groovlets for years and recently had to dig into them in some detail, and that lead me to a situation I’m simultaneously very pleased and rather horrified about.

The appeal of groovlets is both their simplicity and the environment in which they run. They’re simple because they’re just Groovy scripts. The convenience comes from the environment, which has a whole set of implicit variables available, like params for the request parameters, headers for the request headers, and the normal request, response, session, and application references.

The way groovlets work is from a combination of classes in Groovy called groovy.servlet.GroovyServlet and groovy.lang.ServletBinding. The GroovyServlet instantiates a GroovyScriptEngine and a ServletBinding, sets up the implicit variables, finds the groovlet, and then executes it. That’s why groovlets are deployed as simple scripts, in source code form, rather than as compiled classes. They’re loaded and executed by the GroovyScriptEngine inside the GroovyServlet.

Here’s my simple example, taken from my Groovy Baseball application (less than 30 days to baseball season and counting). First I set up the GroovyServlet in the usual way.
[sourcecode language=”xml”]
Here is the groovlet that I use in the application, called GameService.groovy:
[sourcecode language=”groovy”]
import beans.GameResult
import beans.Stadium
import service.GetGameData

def month = params.month
def day = params.day
def year = params.year

m = month.toInteger() < 10 ? ‘0’ + month : month
d = day.toInteger() < 10 ? ‘0’ + day : day
y = year

results = new GetGameData(month:m,day:d,year:y).games
response.contentType = ‘text/xml’
html.games {
results.each { g ->
outcome:"$g.away $g.aScore, $g.home $g.hScore",
The classes GameResult, Stadium, and GetGameData are internal to my application. For a given date, the application goes to the corresponding games directory maintained by Major League Baseball, determines which games were played that day, and downloads the box scores for each game in XML form. The main processor then extracts the results and creates a GameResult, which holds the home team name and score, the away team name and score, and a Stadium POGO that I use to hold the address and latitude and longitude.

The interesting part, or at least the part that lead to this blog post, is the last part of the script. The groovlet uses the built-in MarkupBuilder, called html, to write out the results in XML form.

Groovlets have had a built-in MarkupBuilder from the very beginning. The builder generates XML and writes the results directly to the output writer. Some of the early examples used the MarkupBuilder to write out a whole web page, but we’ve known that was a bad idea for years. The builder does have an excellent use case, though, which is to transform Java or Groovy objects into a format that can be read by other languages.

My web page builds a Google Map from the results which contains a dot, centered at the home stadium, for each game*.

*Real map geeks called this process “red dot fever”.

Here’s a picture from today, showing yesterday’s pre-season results.

Why write out my data in XML? The Google Maps API is written in JavaScript, and my result objects are in Groovy. JavaScript can’t read Groovy (or Java) objects, so I need a format to go between them, and hey, everybody can read and write strings. Even better, every language has an XML parsing and generation library. The Document Object Model is a W3C specification, after all. The presence of the MarkupBuilder in groovlets makes the process of converting my Groovy objects into XML trivial, and the resulting XML elements are sufficiently simple that parsing them in the JavaScript code is very easy.

Still, if I was writing this application today, I’d probably prefer JavaScript Object Notation (JSON) instead. JSON objects are native JavaScript, so the JavaScript code can deal with them immediately without further parsing. The problem is that groovlets don’t have a built-in JSON builder.

At least, they didn’t used to. The groovy.json.JsonBuilder class was added in Groovy 1.8. That means I can just import the class into my groovlet, instantiate the builder, and write out the data in JSON format as easily as I currently do in XML format. I suddenly realized, however, that other people might like that capability, it might be easy to implement, and then — here’s the cool part — I could make my first actual commit to the Groovy code base.

I therefore dug into the implementation of GroovyServlet and ServletBinding, and discovered that all I would need to do is to modify about half a dozen lines of code in ServletBinding.java and update the corresponding test class.

The fact that the ServletBinding class is implemented in Java is significant (whoa, holy foreshadowing, Batman).

I was inspired by Tim Yates’s great blog post showing exactly how to contribute to the language. I therefore forked the Groovy repository, created a local branch for my changes, added what I needed, and ran the test case. It took more work than I expected, but I finally got the tests to pass. As Tim suggested, I then raised a JIRA issue and sent a pull request from my GitHub repository.

Today I got my reply, from Guillaume Laforge himself (the head of the Groovy project, and a very nice guy, as his comment will show). Here is what he said:

“I had to modify the pull request a bit, as JsonBuilder being a Groovy class, it’s compiled after ServletBinding.
So on a clean build, the build was broken because of that.
I used a trick of “loading” the class lazily with this.getClass().getClassLoader().loadClass(“groovy.json.JsonBuilder”) which is not ideal – ideally, the Groovy build should compile with the joint compiler for the core sources.”

In other words, two things happened:
1. My cool addition of an implicit object called json of type JsonBuilder was accepted. (Whoo hoo!)
2. I broke the freakin’ build. Yup, the whole groovy core build failed, because of me. (Hangs head in shame)

Okay, I didn’t realize the Groovy core was built by compiling the Java sources first, and then the Groovy sources. I always use the joint compiler for my combined projects and never have an issue like that. So it’s not really my fault.

(Except I should have run the full build and discovered this. Sigh.)

In the end, though, Guillaume just fixed it, thanked me for my contribution (whoo hoo! again), and moved on. Hey, I’m now a contributor to the Groovy language! Combined with my one commit to Grails (thanks to Jeff Brown for that one), I now have contributed to the two open source projects I care most about. And this one I did all by myself (obviously, or I wouldn’t broken the stupid build — see what a mixed result this is?).

For the record, here’s what my groovlet would look like with the new json object available:
[sourcecode language=”groovy”]
// …stuff from before…
response.contentType = ‘application/json’
json.games {
results.each { g ->
outcome:"$g.away $g.aScore, $g.home $g.hScore",
out << json
Unlike the MarkupBuilder, JsonBuilder doesn’t automatically output its results, so I have to send it to the output writer myself.

I have some cool integration tests in the project, run by Gradle, but that’s another blog post. If you want to see the source code for the Groovy Baseball application, it’s part of the GitHub repository for my book. The application is in the ch02 directory (called “Groovy by Example” in the book) in the groovybaseball directory.

I’m not exactly sure which version of Groovy will have the json implicit object in groovlets. It’s part of the current build, which I suppose means Groovy 2.0. Anyway, feel free to use it, and if you do you can both thank me and laugh at me at the same time.

2 responses to “Writing json output from a groovlet”

  1. I’ve followed Tim Yates’ suggestion and used StreamingJsonBuilder instead, which takes a writer in its constructor, so that you can get rid of out << json completely, just like you'd do with the MarkupBuilder.

  2. That’s a great idea. I didn’t know about StreamingJsonBuilder, or I would have used it in the first place. Thanks for the update.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.