Search This Blog

Saturday, January 28, 2012

Spring MVC 3.1 with Jamon Templating

Quite clearly as my BLOGs show, I have been working with my favorite technology stack recently, i.e., Spring. In particular, I have been working on Spring MVC and am looking at finding the right view technology to use with Spring. I am biased toward FreeMarker as it appears to be a super set of Velocity and can work with JSPs if required. That said, I have noticed on some Spring Forum postings queries on using Jamon with Spring.  There does not appear to be a concrete example of the same on the web. There are many that talk about performance monitoring of Spring with its namesake JAMon however. So here goes my attempt at integrating Spring with Jamon.

So what is Jamon? Quoting the words of the creators of Jamon, "Jamon is a text template engine for Java, useful for generating dynamic HTML,XML, or any text-based content. In a typical Model-View-Controller architecture, Jamon clearly is aimed at the View (or presentation) layer".

One of the constructs that is central to Jamon is type safety. Jamon on the view tier provides compile time type safety that is quite lost when using for example, JSPs, and the expression language libraries that are associated with it.

With technologies like JSP, errors are detected by a developer at runtime and they fix the same as they develop the application. Detecting an error at compile time however is really beneficial if the cost of starting an application and testing a request path is significant.Jamon templating with the compile time type safety it provides the presentation tier, mitigates the turn around time for development and refactoring. I will not delve into the details of Jamon as the same can be read on the Jamon website.

An interview on the web with one of the creators of Jamon describes how templating languages like FreeMarker and Velocity only partially fill the void of type safety when working with a framework like Spring MVC and infact have a hole that can lead to non type safe behavior.

Consider the following snippet using Spring MVC with FreeMarker for rendering the view:
@Controller
public class LoginController {
  public ModelAndView login(User user, BindingResult result) {
     ModelAndView mav = new ModelAndView("user");
     ......
     if (result.hasErrors()) {
       mav.addObject("user", user); 
       ....
       return mav;
     }
     ...
  }
}
The corresponding FreeMarker template, login.ftl, might look like:
 <@spring.bind "user.*"/>
 <@spring.formInput "user.username", id="username"/>
With the above, all is well and the runtime binding works just fine. However, if a developer accidentally forgot to:
  • Add the user object to the model
  • Or misspelled the "key" to the user object as "user" but as "users"
  • Or misspelled "user.username" on the FreeMarker template as "user.usrname"
  • Or placed a wrong type in the value for the ModelAndView instead of the User object
They would experience failures at runtime and even a strong templating language like FreeMarker would be powerless to detect the issues mentioned due to the loose coupling between the Spring MVC controller and the Template. Objects added to the ModelAndView object are of signature addObject(String, Object), ie., quite a few rooms for accidents. This decoupling nature of Spring MVC would hurt even a presentation tier written using Jamon right? Well would it really ? What if  type safety could be maintained all the way from the Spring Controller tier to the view tier with Jamon? Lets say for the sake of discussion, we desire to render a login page using Jamon with Spring MVC.  A Jamon Template for the user login titled LoginView.jamon is shown below in which the User object is explicitly passed in to the Jamon template. There are no iffs or butts regarding what is available to the Template here, it is the User object and only the User object that is made available to the template. It is possible to pass in null due to a programming mistake or runtime time error in which case all bets are off and this concern transcends compile time safety. At compile time a LoginView.class is made available:
 <%import>
com.welflex.model.User;
</%import>
<%args>
User user;
</%args>

<form....
    ...
    <input type="text" id="userName" name="userName" value="<% user.getUserName() %>">
    ....
    <input type="submit" name="Login" value="Login"/>
 </form>
The Login Controller is designed to return a Jamon Renderer rather than a ModelAndView as shown below:
@Controller
public class LoginController {
  /**
   * @return Return a Jamon Renderer
   */
  @RequestMapping(value = "/login.html", method = RequestMethod.GET, produces = MediaType.TEXT_HTML_VALUE)
  @ResponseBody
  public Renderer login() {
    User user = new User("Enter user name", "Enter password");
    return new LoginView().makeRenderer(user);
  }
}
Nice and dandy, so how does one tell Spring MVC to handle the Jamon Renderer? Look no further than below where a custom HttpMessageConverter is created for the same:
public class JamonTemplateHttpConverter extends AbstractHttpMessageConverter<Renderer> {

  @Override
  protected boolean supports(Class<?> clazz) {
   // Only Renderer classes are supported by this HttpMessageConverter
   return Renderer.class.isAssignableFrom(clazz);
  }
  
  @Override
  public boolean canWrite(Class<?> clazz, MediaType mediaType) {
    return supports(clazz) && MediaType.TEXT_HTML.equals(mediaType);
  }
  ...

  @Override
  protected void writeInternal(Renderer t, HttpOutputMessage outputMessage) throws IOException,
    HttpMessageNotWritableException {
    Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
    t.renderTo(new OutputStreamWriter(outputMessage.getBody(), charset));
  }
  ...
}
Of particular note in the above class is the fact that it knows how to handle Jamon Renderers and also writes the renderer's content to the HttpServlet output stream. The next step involves letting Spring know of the renderer. This operation is accomplished in the Web Configuration class as shown below:
@EnableWebMvc
@Configuration
@Import({ControllerConfig.class})
public class WebConfig extends WebMvcConfigurerAdapter {
  ...  
  @Override
  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(new JamonTemplateHttpConverter());
  }
  ...
}
The above is one way where type safety is preserved using Jamon and Spring MVC. What about if one wishes to use the ModelAndView paradigm from Spring as is standard practice ? Well I could not do the same ! At least not without some customizations. Spring Dispatcher Servlet is strongly tied to the ModelAndView class for its functioning and sadly ModelAndView is not an interface. However, the good thing is that that ModelAndView is not "final" and neither are its methods. In addition, the View part of ModelAndView is an interface so we start by creating a custom implementation of the View whose primary purpose is to render the contents:
public class JamonView implements View {
  private final Renderer renderer;
  
  public JamonView(Renderer renderer) { this.renderer = renderer; }
  
  @Override
  public String getContentType() { return MediaType.TEXT_HTML_VALUE; }

  @Override
  public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {  
    // Note that the Model is never used at all here.
    StringWriter writer = new StringWriter();
    renderer.renderTo(writer);
    response.getOutputStream().write(writer.toString().getBytes());
  }
}
The ModelAndView object is what is central to Spring's DispatcherServlet and as one would probably guess, the only option I had at my disposal (no cglib enhancements please) was to extend ModelAndView as shown below:
public class JamonModelAndView extends ModelAndView {  
  
  public JamonModelAndView(Renderer renderer) { super(new JamonView(renderer)); }
  
  public JamonModelAndView(JamonView view) { super(view); }
  
  // Probably want to ensure that other methods are not invokable by throwing UnsupportedOperationException
  // Sadness that ModelAndView is not an interface or one could use dynamic proxies! Also there is no 
  // higher level abstraction one can use
}
On could also have the JamonModelAndView be a static static class that provided methods to create a ModelAndView with signatures that accept a Renderer or JamonView. With the custom extension to ModelAndView, a resulting Controller class for the Login operation would look like the following where two separate execution paths are demonstrated, one for a successful login and a second where validation errors were detected. God bless Liskov and his substitution principle Also note that the User object is automatically bound and its properties validated with values passed in from the form:
@Controller
public class LoginController {
  ....
  @RequestMapping(value="/login.html", method = RequestMethod.POST)
  public ModelAndView login(@Valid User user,  BindingResult result,  Map<String, Object> model) {
   return result.hasErrors() ?  new JamonModelAndView(new LoginView().makeRenderer(user, result))
      : new ModelAndView("redirect:/welcome.html");    
  }
  
  @RequestMapping(value="/welcome.html")
  public ModelAndView welcome() {
    return new JamonModelAndView(new WelcomeView().makeRenderer());
  }
}
With the above, we are all but done with a type safe rendering approach using Spring MVC and Jamon. What remains is the part where form submission and the input variable mapping has to be defined in the Jamon Template. If one does the following where the userName property is quoted as a non type safe string, we are left with a hole all over again regarding our type safe mantra:
<form....
    ...
    <input type="text" id="userName" name="userName" value="<% user.getUserName() %>">
    ....
    <input type="submit" name="Login" value="Login"/>
 </form>
I have broken my head trying to figure out a type safe way to describe how to specify the input form mappings in a type safe way and ended up with a cheap solution that requires the definition of a constant somewhere that describes the same. Please note that the constant itself is not safe from any refactoring done on the User bean as it not strongly typed to the actual method signature of setUserName(User):
<form....
    ...
    <input type="text" id="userName" name="<% User.USER_NAME_PROPERTY %>" value="<% user.getUserName() %>">
    ....
    <input type="submit" name="Login" value="Login"/>
 </form>
Running the Example:

A simplistic application demonstrating a Login Form with validation is available for download here. Extract the same and execute a "mvn jetty:run" to launch the application. Access the application at http://localhost:9090 and login using any username/pass combo to a simple welcome page. If you are an eclipse user, install the eclipse plugin for Jamon for syntax highlighting and IDE support. I would recommend that any user trying this example attempt to change code and witness for themselves the benefits of compile time type safety that Jamon provides. Please note that if you use the J2EE perspective on your project, you will not be able to see the Jamon configuration options sadly. On the Java perspective however, all is visible when you goto Project->Properties->Jamon.

Conclusion:

The question remains whether Jamon is a suitable candidate for the presentation tier when using something like Spring MVC? My answer as any smart architect would say is, it depends! If the web application is primarily a read-only, i.e, whose focus is to render data for display, then yes, you cannot go wrong with the benefits of Jamon. Where Jamon for the view tier suffers is when an application has a lot of forms requiring user input and the fact that there is no way to guarantee type safety of the input variables without the use of constants as shown above. The mantra of type safety is only partial in such cases and if this is something you are willing to tolerate, more power to you. Jamon cannot be faulted for this deficiency as the Java language itself has no way of describing the property names in a type safe manner. In addition, Jamon does not provide an expression language or tag based support from what I know of. Jamon has a lot going for it and I have personally developed a couple of applications using it for my view tier. Features like macros provided by FreeMarker make development easier and if one is careful and thorough with good unit/automation/manual coverage of ones application, the fear of runtime errors due to lack of type safety can be mitigated in favor of the powerful EL and Macro support provided by the other view technologies for Spring MVC. In addition, there is strong integration support from the Spring Framework for FreeMarker and Velocity with provided macros etc. If any person who stumbles on this BLOG has used FreeMarker or Velocity with Spring, I would like to hear of their experience of same please.In addition, if anyone has integrated their Spring MVC application with Jamon in different way, I'd love to hear the approach.

Lets get the bottom line straight though, Jamon, FreeMarker, Velocity, whatever, in the end remember the following:

Developing with Spring my friends is a fine thing....
Your code will sing, and your projects will fly to successful wins as if they had wings....
There just ain't no going wrong with this thing....
It's not just some temporary fling....
Embrace it, and it will treat you like a king!

Peace!

4 comments:

Dave Stockmann said...

Very interesting. Thanks for sharing. You should incorporate twitter bootstrap for you css.

More information here:
http://raibledesigns.com/rd/entry/refreshing_appfuse_s_ui_with

Especially for those of us who lack some UI skills.

Sanjay Acharya said...

I did take your tip and updated both the Spring and JAX-RS blogs with Twitter bootstrap. Pretty neat CSS template with supporting scripts.

Mark UK said...

I love this tutorial, I don't suppose you know how one would override the Accept header that renders the formats. Further to this, is it possible to provide JAXB with more formats easily enough, or run XSL on its results?

Thanks for sharing your work!

Sanjay Acharya said...

Hi Mark. Thanks. One would pass the accepts header and make it available as an argument on the method signature in the controller. Based of the header, instead of the Jamon View, you could render a XSLTView.
Alternate to Accepts header you could have separate resources for xml and other views. For example orders.xml, orders.json etc.