1 Mart 2013 Cuma

Spring 3.2 Json-p Message Converter

In Spring framework, there is a message converter for json but there isn't a converter for json-p. I 've seen a few solutions on here.

One of the solution in here, was extending MappingJacksonJsonView class. But I didn't like the idea of checking every 'GET' request whether it has a 'callback' param or not.

And the other solution in here, was extending MappingJackson2HttpMessageConverter class. I preferred this approach. But, I didn't like the idea of implementing an interface (JsonObject) for every request. So, I decided to change it a bit.
I decided to get the callback parameter over the servlet request. So, I neither had to implement JsonObject interface nor had to get the callback parameter with the @RequestParam annotation. Here is my solution:
/**
 * Converter class that we are going to use to create json-p messages.
 *
 * @author sinan.yumak
 *
 */
public class MappingJackson2JsonpHttpMessageConverter extends MappingJackson2HttpMessageConverter {

    private static final String DEFAULT_CALLBACK_PARAMETER = "callback";

    private static final List SUPPORTED_MEDIA_TYPES = new  ArrayList() {{
        add( new MediaType("application", "x-javascript") );
        add( new MediaType("application", "javascript") );
        add( new MediaType("text", "javascript") );
    }};


    public MappingJackson2JsonpHttpMessageConverter() {
        setSupportedMediaTypes(SUPPORTED_MEDIA_TYPES);
    }


    @Override
    protected void writeInternal( Object object, HttpOutputMessage outputMessage )
        throws IOException, HttpMessageNotWritableException {
        JsonGenerator jsonGenerator = getJsonGenerator(outputMessage);

        try {
            String callbackParam = getRequestParam( DEFAULT_CALLBACK_PARAMETER );

            if ( Strings.isNullOrEmpty(callbackParam) ) {
                //if the callback parameter doesn't exists, use the default one...
                callbackParam = DEFAULT_CALLBACK_PARAMETER;
            }

            jsonGenerator.writeRaw(callbackParam);
            jsonGenerator.writeRaw(" (");
            getObjectMapper().writeValue(jsonGenerator, object);
            jsonGenerator.writeRaw(");");
            jsonGenerator.flush();
        } catch (JsonProcessingException ex) {
            throw new HttpMessageNotWritableException("Could not write JSON:"  + ex.getMessage(), ex);
        }
    }


    private JsonGenerator getJsonGenerator( HttpOutputMessage outputMessage ) throws IOException {
        JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
        return getObjectMapper().getFactory().createJsonGenerator(outputMessage.getBody(), encoding);
    }

    /**
     * Returns given parameter from servlet request.
     *
     * @param paramName
     *         Name of the param
     */
    private String getRequestParam( String paramName ) {
        return getServletRequest().getParameter( paramName );
    }

    /**
     * Returns current servlet request.
     */
    private HttpServletRequest getServletRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
    }
}
And you can register your converter like that:
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

    @Override
    public void configureMessageConverters( List> converters ) {
        converters.add( new MappingJackson2JsonpHttpMessageConverter() );

        addDefaultHttpMessageConverters( converters );
    }

}