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”]
<servlet>
<servlet-name>GroovyServlet</servlet-name>
<servlet-class>groovy.servlet.GroovyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>GroovyServlet</servlet-name>
<url-pattern>*.groovy</url-pattern>
</servlet-mapping>
[/sourcecode]
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 ->
game(
outcome:"$g.away $g.aScore, $g.home $g.hScore",
lat:g.stadium.latitude,
lng:g.stadium.longitude
)
}
}
[/sourcecode]
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 ->
game(
outcome:"$g.away $g.aScore, $g.home $g.hScore",
lat:g.stadium.latitude,
lng:g.stadium.longitude
)
}
}
out << json
[/sourcecode]
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.
Leave a Reply