Townsfolk
Townsfolk

Reputation: 1334

Grails 2.5.0 static compilation, controllers and grails features

I am testing out Grails static compilation, specifically GrailsCompileStatic. The documentation is limited in explaining what Grails dynamic features aren't supported. My test Controller is very simple, but I'm running into problems already.

@GrailsCompileStatic
class UserController {

    UserService userService

    def list() {
        def model = [:]
        def model = request.JSON

        withFormat {
            json {
                render(model as JSON)
            }
        }
    }
}

When compiling the application I get two compile time errors. The first about a missing property for JSON on the request object, and a second error about a missing method for json in the withFormat closure.

Seems to me I'm either doing something wrong or GrailsCompileStatic doesn't work with these features?

Upvotes: 2

Views: 326

Answers (1)

Tegi
Tegi

Reputation: 788

About request.JSON

The request object's getJSON() method is added via the ConvertersPluginSupport class. The exact lines are:

private static void enhanceRequest() {
    // Methods for Reading JSON/XML from Requests
    def getXMLMethod = { -> XML.parse((HttpServletRequest) delegate) }
    def getJSONMethod = { -> JSON.parse((HttpServletRequest) delegate)}
    def requestMc = GrailsMetaClassUtils.getExpandoMetaClass(HttpServletRequest)
    requestMc.getXML = getXMLMethod
    requestMc.getJSON = getJSONMethod
}

As you can see it uses the dynamic dispatch mechanism, but fortunately it's not such a big deal. You can simply replicate it by executing JSON.parse(request) anywhere in your controller.

Pay attention though! JSON.parse(HttpServletRequest) returns an Object, which is either a JSONObject or a JSONArray, so if you plan on using them explicitly, and you are compiling statically, you will have to cast it.

You might create a common base class for your controllers:

import org.codehaus.groovy.grails.web.json.JSONArray
import org.codehaus.groovy.grails.web.json.JSONObject
import grails.converters.JSON

@GrailsCompileStatic
class BaseController {
    protected JSONObject getJSONObject() {
        (JSONObject) JSON.parse(request)
    }

    protected JSONArray getJSONArray() {
        (JSONArray) JSON.parse(request)
    }
}

Then in your controller you can simpy invoke getJSONObject() or getJSONArray. It's a bit of a workaround, but results in a staticly compileable code.

About withFormat

This is a bit more complicated. The withFormat construct is really a method, which has a Closure as it's first parameter. The internal implementation then figures out based on the current request or response content type which part of the argument closure is to be used.

If you want to figure out how to do this statically, take a look at the source code.
You could extend this class, then use it's protected methods, but I don't know if it's worth all the hussle, you would loose much of Grails' conciseness. But if you really want to do it, you can. Don't you just love open source projects ? :)

Upvotes: 3

Related Questions