Monday May 26, 2008

MVCJ

Or Model, View, Controller and Jersey

Perhaps a little know fact (i should have blogged about it sooner) is that Jersey has support for the MVC paradigm and pluggable template processing.

A Controller is a resource class, a model is returned by a resource method, and a view is a template (which is processed by an associated template processor) that operates on the model.

The MVC approach taken by Jersey was inspired by the mechanism in Hudson and it's underlying framework, Stapler.

Jersey supports two types of MVC: explicit; and implicit.

Explcit MVC

A resource method can explicitly return a reference to a template and a model. For example, given the following resource class:

package com.foo;

@Path("foo")
public class Foo {
  @GET
  public Viewable get() {
   return new Viewable("index", "FOO");
  }
}

Foo is the controller, and when the method get is invoked, it returns the model "FOO" and a reference to the view (the template) "index", both of which are encapsulated in the Viewable instance.

The template reference "index" is a relative so Jersey will resolve it to an absolute template reference, using the fully qualified class name of Foo, to the following:

"/com/foo/Foo/index"

Then Jersey will search the registered template processors (more detail on this later) to find a template processor that can resolve it further to a processable template reference. If a template processor is found then the processable template is processed using the model.

If instead the get method is as follows:

  @GET
  public Viewable get() {
   return new Viewable("/index", "FOO")
  }

Jersey will not resolve the template reference as it is considered resolved, since it begins with "/". The reference will however be resolved to a processable template reference as previously described.

All HTTP methods may return Viewable instances. Thus a POST method may return a template reference to a template that produces a view that is the result of processing a form.

Implicit MVC

A resource class can have templates implicitly associated (this is the style used by Hudson). For example, given the following resource class:

@Path("foo") 
public class Foo {
  public String getFoo() {
   return "FOO";
  }
}

if there exists a template reference "index" that resolves to an absolute template reference "/com/foo/Foo/index" that in turn resolves to a processable template reference then a @GET method will be implicitly added to the Foo controller that performs the equivalent of the following explicit resource method:

  @GET
  public Viewable get() {
   return new Viewable("index", this)
  }

Notice that the model is the controller. In this case the template reference "index" is special, it is the template reference associated with the controller itself.

Implicit sub-resources are also allowed, for example, if there is a template reference "bar"  that resolves to an absolute template reference "/com/foo/Foo/bar" that in turn resolves to a processable template reference then a @GET method will be implicitly added to the Foo controller that performs the equivalent of the following explicit sub-resource method:

  @Path("bar") @GET
  public Viewable get() {
   return new Viewable("bar", this);
  }

This all works dynamically (as is the case for explicit MVC) so it is possible (if the deployment system is configured correctly) to add or modify templates while the application is running.

To enable implicit MVC it is necessary to set the following configuration feature to true:

"com.sun.jersey.config.feature.ImplicitViewables"

Furthermore this only works for HTTP GET requests and only if there are no equivalent explicit @GET resource methods (namely explicit resource methods take precedence over implicit resource methods).

The Bookstore example in the Jersey distribution contains a working example using implicit MVC with JSP pages.

JSP support

Jersey provides MVC support for JSP pages. There is a JSP template processor that resolves absolute template references to processable template references that are JSP pages as follows:

  • if the absolute template reference does not end in ".jsp" append it to the reference; and
  • if Servlet.getResource returns a non-null value for the appended reference then return the appended reference as the processable template reference otherwise return null. 

Thus the absolute template reference "/com/foo/Foo/index" would be resolved to "/com/foo/Foo/index.jsp" if there exists the JSP page "/com/foo/Foo/index.jsp" in web the application.

Jersey will assign the model instance to the attribute named "it". So in the case of the implicit example it is possible to access the foo property on the Foo controller as follows:

<h1>${it.foo}</h1> 

Pluggable template processors 

Template processors are providers so it is possible to implement a processor as follows:

@Provider 
public MyTemplateProcessor implements TemplateProcessor {
  String resolve(String path) { ... }

  void writeTo(String resolvedPath, Object model, OutputStream out)
    throws IOException { ... }
}

If using the class or package scanning techniques then such a provider will get automatically registered. In addition such a component can be managed by a registered IoC framework and injection of standard JAX-RS and Jersey components is possible.

If i recall correctly (from the Jersey users list) a developer successfully plugged in Freemarker support in a couple lines in addition to supporting JAXB to XSLT translations. One interesting aspect of using template engines such as Freemarker or Velocity is it is possible to write a set of resource classes that provide their own templates, thus potentially allowing easy way to plug-in extensions to a web application (ala the Hudson style).

When writing template processors it is recommend that you use an appropriate unique suffix for the processable template references. Then it is possible to easily intermix multiple template processors without any conflict.

The implementation of the JSP template processor is presented at the end of this blog entry.


public class JSPTemplateProcessor implements TemplateProcessor {
    @Context ServletContext servletContext;

    public JSPTemplateProcessor() {
        Class<?> c = ServletContext.class;
    }
   
    public String resolve(String path) {
        if (servletContext == null)
            return null;
       
        if (!path.endsWith(".jsp"))
            path = path + ".jsp";

        try {
            if (servletContext.getResource(path) == null) {
                // TODO log
                return null;
            }
        } catch (MalformedURLException ex) {
            // TODO log
            return null;
        }
       
        return path;       
    }

    @Context HttpContext hca;
   
    public void writeTo(String resolvedPath, Object model, OutputStream out) throws IOException {
        HttpResponseContext response = hca.getResponse();
        ((HttpResponseAdaptor)response).forwardTo(resolvedPath, model);
    }
}
About

sandoz

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today