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 AssetsBundles.
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).



