Grails Dropwizard Plugin
I’ve been seeing a lot of buzz about Dropwizard as a framework for creating REST services. I thought it would be interesting to integrate it with Grails, but it ended up being a lot more work than I expected since the two approaches are very different, and very opinionated. I have released an initial Grails plugin that integrates Dropwizard: dropwizard
. The source is here
and the docs are here
.
To make thing more clear, I created a simple test application. You can download it here.
Available URLs
The application has a simple Person domain class with a dynamically scaffolded controller, so typical URLs like http://localhost:8080/dropwizardtest/person/list
work as expected. The Dropwizard resources are mapped under the default “dropwizard” context path, so you can make a GET request (for example in a browser) for http://localhost:8080/dropwizard/people
to see the JSON response for all Person instsances; there is one created in BootStrap.groovy and you can easily add more.
You can also view a single instance by id, for example http://localhost:8080/dropwizard/people/1
which should display
{"id":1,"fullName":"Person Jones","jobTitle":"Jefe Grande"}
Since there is only one user, a GET request for http://localhost:8080/dropwizard/people/42
should display
No such user.
You can create a new Person with a POST request to http://localhost:8080/dropwizard/people
– do this with a REST client, or the Firefox Poster addon, or even using curl from the commandline:
curl -H "Content-Type: application/json" -H "Accept: application/json" -X POST -d '{"fullName":"Other Person","jobTitle":"Other Title"}' http://localhost:8080/dropwizard/people
Now if you view all instances with http://localhost:8080/dropwizard/people/
it should look like
[{"id":1,"fullName":"Person Jones","jobTitle":"Jefe Grande"},{"id":2,"fullName":"Other Person","jobTitle":"Other Title"}]
or you can use curl again:
curl http://localhost:8080/dropwizard/people/
There is a simple hello-world endpoint which replies with a hello message, and uses a specified name if provided. If you GET http://localhost:8080/dropwizard/hello-world
you will see a response similar to
{"id":1,"content":"Hello, Stranger!"}
and if you add a name parameter – http://localhost:8080/dropwizard/hello-world?name=Ralph
– you will see
{"id":2,"content":"Hello, Ralph!"}
You can also POST to /hello-world with the data for a Saying
instance:
curl -H "Content-Type: application/json" -H "Accept: application/json" -X POST -d '{"id":123,"content":"test saying"}' http://localhost:8080/dropwizard/hello-world
and it will log the information using Logback:
INFO [2013-03-05 08:35:28,334] grails.app.dropwizard.com.example.helloworld.resources.HelloWorldDropwizardResource: Received a saying: id: 123, content: test saying
The plugin comes with support for Freemarker and Mustache templates. GET http://localhost:8080/dropwizard/views/utf8.ftl
to see a Freemarker response, and http://localhost:8080/dropwizard/views/utf8.mustache
to see Mustache.
There is also a simple endpoint under http://localhost:8080/dropwizard/info
that you can make GET requests for:
Application is running on port : 8080 connectorType : blocking
To test support for tasks, POST to http://localhost:8081/dropwizard/tasks/gc
to run a garbage collection using the default “gc” task:
curl -H "Accept: application/json" -X POST http://localhost:8081/dropwizard/tasks/gc
and the response should be
Running GC... Done!
You can run the sample HelloDropwizardTask
too; send a POST request to http://localhost:8081/dropwizard/tasks/hello-task
:
curl -H "Accept: application/json" -X POST http://localhost:8081/dropwizard/tasks/hello-task
and it should display
my task complete.
The admin URIs are available on port 8081. GET http://localhost:8081/dropwizard/metrics
or http://localhost:8081/dropwizard/metrics?pretty=true
to see current metrics:
{ "jvm" : { "vm" : { "name" : "Java HotSpot(TM) 64-Bit Server VM", "version" : "1.7.0_11-b21" }, "memory" : { ...
GET http://localhost:8081/dropwizard/healthcheck
to run all health checks:
* DataSource: OK * deadlocks: OK * template: OK
GET http://localhost:8081/dropwizard/threads
to view a thread dump:
main id=1 state=WAITING - waiting on <0x38a13ac9> (a java.lang.Object) - locked <0x38a13ac9> (a java.lang.Object) at java.lang.Object.wait(Native Method) at java.lang.Object.wait(Object.java:503) at org.eclipse.jetty.util.thread.QueuedThreadPool.join(QueuedThreadPool.java:391) at org.eclipse.jetty.server.Server.join(Server.java:413) at grails.plugin.dropwizard.GrailsService.start(GrailsService.java:131) ...
And finally you can GET the simple http://localhost:8081/dropwizard/ping
URL to check that everything is functional:
pong
Application notes
Dropwizard typically loads resources from the classpath, so there are a couple of ways to make files available. You can put files in src/java
or grails-app/conf
and non-source files will be copied to the classpath. This can clutter up those directories though, so you might want to add in a new folder for these files. The sample application has a src/resources
folder for this, and adds an event callback in scripts/_Events.groovy
to ensure that the files are available:
eventCompileStart = { ant.copy(todir: buildSettings.resourcesDir, failonerror: false, preservelastmodified: true) { fileset(dir: 'src/resources') { exclude(name: '*.groovy') exclude(name: '*.java') } } }
You can put banner.txt
there if you want a banner displayed and don’t set the Config.groovy
property, the YAML config file, Freemarker and Mustache template files, and static resources mapped with AssetsBundle
s.
As mentioned in the plugin documentation, serialization of domain classes doesn’t work. The plugin explicitly converts Person
instances to and from PersonDTO
instances (e.g. see PeopleDropwizardResource
). GORM isn’t required, so you’re free to use regular POJO/POGO based persistence. There is a somewhat old dto plugin that may or may not help here.
All of the test application’s classes (resources, health checks, etc.) are written in Groovy and are in grails-app/dropwizard
, but as described in the docs you can write them in Java and register them yourself rather than using convention-over-configuration and letting them be auto-discovered. Classes in grails-app/dropwizard
support dependency injection (for example the dataSource
bean in DataSourceHealthCheck
), and can conveniently access Config.groovy
values with the ConfigValue
annotation (see TemplateHealthCheck
for an example of this).
Good work Burt! Just out of a meeting where Grails lost in favour of DropWizard but now it seems they are not quite the opposite ends of the spectrum as it seemed.
Burt, this seems to be a fantastic start to what I hope will become better REST support in Grails. I don’t agree with the very opinionated choices that Dropwizard makes, but if the core REST functionality of Dropwizard could be pulled out and into a plugin that integrates well into Grails, I would be dancing in the streets, possibly literally.
In particular, the Jetty, Logback, Metrics, YAML, and Hibernate Validator dependencies seem like they would be better off as individual plugins that adhere to an interface, so that people can use them if they like, or use something else they already have, or not use it at all and the REST plugin just ignores that part of itself.
In any case, great work and I hope this is just the beginning of a much more REST-ful Grails.
[…] plugin questa settimana che fornisce il supporto Dropwizard per Grails. Dai un’occhiata al post introduttivo che spiega suo utilizzo e il progetto di […]
Burt,
Great post and thanks for putting the plugin together!
How would you package and deploy an app that uses this plugin? My understanding was dropwizard bundles and embedded version of jetty, so building a grails WAR seemed backwards. Should we be able to just add a Main-Class to the WAR manifest and run it via:
java -jar target/dropwizardtest-0.1.war server
Rafal
I haven’t had a chance to go back and look at running this in production, but in theory running ‘grails prod run-dropwizard’ would work. That doesn’t pre-compile GSPs, so you’d take a hit there with a slight delay loading each the first time, plus the added permgen.
What about running it completely standalone, outside of grails? Hence my attempt to run it directly w/ java and the war (which I thought should contain all the jar dependencies needed to run).
Using run-dropwizard is very close to how regular DropWizard apps run. The big difference is that the Grails context is added to the embedded Jetty. But there’s no overhead there unless you make requests on the Grails side. If you just want to use DropWizard and take advantage of GORM, writing some classes in Groovy, etc. then I’d go with ‘grails prod run-dropwizard’.