Returning Groovy’s GStringImpl in a Jersey endpoint

I started playing around with some technologies to try and increase my productivity when building RESTful APIs. The current setup I am using has Groovy, Spring 4, Jackson 2 and Jersey 2. The project is built by Gradle and runs on the Google AppEngine. A post describing the way this project was setup will follow shortly. I just wanted to share a little piece of code with the world first.

The above setup works nicely for JSON objects and HTML and things like that. I just ran into a little snag when I tried to get a little test running:

class ApiInfo {
    int count = 1;

    @GET
    hello() {
        "Hello world ${count}"
    }
}

This resulted in an Error 500, with the following problem as its cause:

SEVERE: MessageBodyWriter not found for media type=text/plain, type=class org.codehaus.groovy.runtime.GStringImpl, genericType=class java.lang.Object

At first it looks strange that a ‘normal’ String cannot be serialized to text/plain, until you realize you’re working with Groovy and Java’s String is final. So we apparently need a MessageBodyWriter to support this:

import groovy.json.internal.Charsets
import org.codehaus.groovy.runtime.GStringImpl

import javax.ws.rs.WebApplicationException
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.ext.MessageBodyWriter
import javax.ws.rs.ext.Provider
import java.lang.annotation.Annotation
import java.lang.reflect.Type

/**
 * Adds support for Groovy's GStringImpl as a Jersey output.
 * LICENSE: Do-whatever-you-want-but-attribution-would-be-appreciated.
 *
 * @author J.H. Kuperus (www.jhkuperus.nl)
 */
@Provider
class GStringImplMessageBodyWriter implements MessageBodyWriter< GStringImpl > {

    @Override
    boolean isWriteable(Class< ? > type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        GStringImpl.isAssignableFrom(type) && mediaType == MediaType.TEXT_PLAIN_TYPE;
    }

    @Override
    long getSize(GStringImpl gString, Class< ? > type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        gString.length()
    }

    @Override
    void writeTo(GStringImpl gString, Class< ? > type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap< String, Object > httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
        entityStream.write(gString.getBytes(Charset.forName('UTF-8')))
    }
}

Feel free to use this piece of code, send me remarks or correct any mistake(s) 😉

–JH

Posted in Groovy, Tips and Tricks and tagged , , , .