December 22, 2015

Ratpacked: Extending GroovyChain DSL

Russel Hart has a nice example on how to extend the Ratpack Groovy DSL in the hands on ratpack project on Github. We can use the Groovy extension module feature to add new methods to arbitrary classes. If we use this to add new methods to the GroovyChain class we can use those methods in the DSL of our Groovy Ratpack application.

A Groovy extension module is just a class with a definition of the new methods we want to add to a class. The first argument of the method is the class the method is added to and the remaining arguments can be used by the implementation. We also need to create a supporting file org.codehaus.groovy.runtime.ExtensionModule with some information about our extension class. This supporting file needs to be in the classpath in the directory META-INF/services.

Let's first write our extension module class. Here we define the method cors that will accept an arbitrary number of String type arguments that are added to the response header. Finally the next method is called on the implicit Context object to continue with the other handlers.

// File: src/main/groovy/com/mrhaki/ratpack/CorsChainExtension.groovy
package com.mrhaki.ratpack

import groovy.transform.CompileStatic
import ratpack.groovy.handling.GroovyChain

class CorsChainExtension {
    // Add cors method to the GroovyChain class.
    // The method accepts an arbitrary number of
    // String arguments. These arguments must be
    // pairs of valid response header name and value
    // to support CORS.
    static GroovyChain cors(
            final GroovyChain chain, 
            final String... values) {
        // Apply to all requests.
        chain.all {
            // Make pairs of the values passed as arguments
            // and set response header for each pair.
            values.toList().collate(2).each { pair ->
                // Set response header name and value.
            // Continue with next handler.

Next we create the supporting file to describe the extension module:

# File: src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
moduleName = corsChainExtension
moduleVersion = 1.0
extensionClasses = com.mrhaki.ratpack.CorsChainExtension

Now we can use the cors method in our DSL:

// File: src/ratpack/ratpack.groovy
import static ratpack.groovy.Groovy.ratpack

ratpack {
    handlers {
        // Use new method cors.
        cors 'Access-Control-Allow-Origin', '*',
             'Access-Control-Allow-Methods', 'GET'

        get {
            render 'Welcome to Ratpack!'

When we make a request for http://localhost:5050/ we can see in the response headers the names and values we have defined with the cors method:

$ http localhost:5050
HTTP/1.1 200 OK
Access-Control-Allow-Methods: GET
Access-Control-Allow-Origin: *
connection: keep-alive
content-encoding: gzip
content-type: text/plain;charset=UTF-8
transfer-encoding: chunked

Welcome to Ratpack!


In IntelliJ IDEA we even get code completion on our new method:

Written with Ratpack 1.1.1.