Build a MVC solution based on JAXRS is quite trivial!


Few days ago a user went on #deltaspike IRC channel (on freenode) and asked if there was a replacement for seam rest. There is no one ATM (a bit too early) but this post will show you how simple it is to build a MVC framework based on any template engine you want (velocity for me) and JAXRS.

The gain is quite obvious: you don’t need to learn someting new or import anything else than what you already have (in JavaEE 7 you can even think to use ELProcessor as template engine!). Another point is JAXRS is really advanced so why using another API often not as complete as JAXRS?

Ok but you’ll say me: how to link both? It is not specified. Of course there are some specific (= proprietary) API to do so but nothing standard (yet?). In fact when you think to it you just want to render a JAXRS response as the result of a template evaluation.

To make things a little bit clean you’ll use a “ModelView” class which will just transport the template name and the template attributes. Here is a proposed implementation for this class:

package com.github.rmannibucau.jaxrs.mvc.api;

import java.util.HashMap;
import java.util.Map;

public class ModelView {
    private final String name;
    private final Map<String, Object> attributes = new HashMap<String, Object>();

    public ModelView(final String name) {
        this.name = name;
    }

    public ModelView set(final String name, final Object value) {
        attributes.put(name, value);
        return this;
    }

    public Map<String, Object> attributes() {
        return attributes;
    }

    public String template() {
        return name;
    }
}

We still don’t have a solution to our need but at least we know which kind of usage we want:

@Path("/velocity")
public class VelocityResource {
    @GET
    @Path("/get")
    public ModelView get() {
        return new ModelView("velocity/get.vm")
                            .set("name", "GET");
    }
}

Not that bad right? So how to solve it?

Earlier I said “we just want to render a JAXRS response as the result of a template evaluation”. There is an API for that in JAXRS specification: MessageBodyWriter.

The idea is to simply prepare a VelocityEngine (I did it in the constructor but you can do it lazily too) and then use this engine to render the result in the answer OutputStream. Here is the result:

package com.github.rmannibucau.jaxrs.mvc.provider;

import com.github.rmannibucau.jaxrs.mvc.api.ModelView;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.log.JdkLogChute;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

@Provider
@Produces({ MediaType.TEXT_HTML, MediaType.TEXT_PLAIN })
public class VelocityMessageBodyWriter implements MessageBodyWriter<ModelView> {
    private static final String UTF_8 = "UTF-8";

    private final VelocityEngine velocity;

    public VelocityMessageBodyWriter() {
        velocity = new VelocityEngine();
        velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, JdkLogChute.class.getName());
        velocity.setProperty(RuntimeConstants.ENCODING_DEFAULT, UTF_8);
        velocity.setProperty(RuntimeConstants.INPUT_ENCODING, UTF_8);
        velocity.setProperty(RuntimeConstants.OUTPUT_ENCODING, UTF_8);
        velocity.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE.toString());
        velocity.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE, Boolean.TRUE.toString());
        velocity.setProperty(RuntimeConstants.RESOURCE_LOADER, "mvc");
        velocity.setProperty("mvc." + RuntimeConstants.RESOURCE_LOADER + ".class", ClasspathResourceLoader.class.getName());
    }

    @Override
    public void writeTo(final ModelView modelView, final Class<?> rawType, final Type genericType,
                        final Annotation[] annotations, final MediaType mediaType,
                        final MultivaluedMap<String, Object> httpHeaders,
                        final OutputStream entityStream) throws IOException {
        final OutputStreamWriter writer = new OutputStreamWriter(entityStream);
        velocity.getTemplate(modelView.template()).merge(new VelocityContext(modelView.attributes()), writer);
        writer.flush();
    }

    @Override
    public boolean isWriteable(final Class<?> rawType, final Type genericType,
                               final Annotation[] annotations, final MediaType mediaType) {
        return ModelView.class.equals(rawType);
    }

    @Override
    public long getSize(final ModelView t, final Class<?> rawType,
                        final Type genericType, final Annotation[] annotations,
                        final MediaType mediaType) {
        return -1;
    }
}

And that’s all! Simple right?

If you want to test quickly you can add this maven dependency:

<dependency>
  <groupId>com.github.rmannibucau</groupId>
  <artifactId>jaxrs-mvc</artifactId>
  <version>0.0.1</version>
</dependency>
Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s