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