Archive for March 6th, 2013

This Week in Grails (2013-09)

Wednesday, March 06th, 2013

We released Grails 2.1.4 and 2.2.1 this week; they are primarily bug fix releases.

Registration for GR8Conf Europe 2013 is now open. Be sure to sign up soon while the prices are very low.

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 an initial implementation available of a plugin and will add some docs and a sample application soon.


If you want to keep up with these “This Week in Grails” posts you can access them directly via their category link or in an RSS reader with the feed for just these posts.


Translations of this post:



Plugins

There was one new plugin released:

and 7 updated plugins:

  • coffeescript-compiler version 0.8.1. Compiles .coffee source files into .js files
  • geasyui version 0.4. Supplies jQuery EasyUI resources and taglibs
  • jquery-validation-ui version 1.4.2. Client Side Validation without writing JavaScript
  • kickstart-with-bootstrap version 0.9.5b. Start your project with a good looking frontend, with adapted scaffolding templates for standard CRUD pages using Twitter Bootstrap
  • localizations version 1.4.4.10. Store i18n strings in a database
  • recaptcha version 0.6.0. Integrates ReCaptcha and Mailhide services
  • redis version 1.3.3. Provides integration with a Redis datastore

Interesting Tweets

User groups and Conferences


Grails Dropwizard Plugin

Wednesday, March 06th, 2013

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

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.