Wild card tiles defs with spring MVC

A while ago I wrote about using Spring MVC to automatically scan for xml tiles definitions. This works beautifully but I later realised that you could go one better and do away with individual definitions in favour of wildcards. Essentially implementing your own convention over configuration.

Prerequisites
The spring setup is the same as in the previous article. You need to have:

  • A tiles view resolver
  • A spring bean of type TilesConfigurer with completeAutoload set to true
  • You need to include tiles 2.2 or greater and tiles-extras.jar

This is explained in greater detail in the previous post so here’s the link again if you need more detail.
Auto scanning tiles defs with spring MVC

Setting up wildcards definitions
First you need to decide on your folders structure. I’ve put everything under WEB-INF/jsp. Under this folder I have one folder called template which holds all the jsp fragments for headers and footers and the few xml tiles defs. The tiles definitions will still be automatically scanned, there will just be fewer of them. The second folder is called views which contains the actual pages with body content.

I’m using a base definition which I plan to override for different sections of the site. Each section can then have it’s own sidebar coming from a different jsp fragment. For example the admin section of the site will override the base definition with an admin specific sidebar. All admin related pages should use this admin definition.

Here’s the tiles definitions file with a simple base def and the overridden admin def.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
       "http://tiles.apache.org/dtds/tiles-config_2_0.dtd">
<tiles-definitions>

    <definition name="base.definition" template="/WEB-INF/jsp/template/layout.jsp">
        <put-attribute name="title" value="" />
        <put-attribute name="header" value="/WEB-INF/jsp/template/header.jsp" />
        <put-attribute name="sidebar" value="/WEB-INF/jsp/template/sidebars/sidebar.jsp" />
        <put-attribute name="body" value="" />
        <put-attribute name="footer" value="/WEB-INF/jsp/template/footer.jsp" />
    </definition>
    
    <definition name="admin.definition" extends="base.definition">
        <put-attribute name="sidebar" value="/WEB-INF/jsp/template/sidebars/admin-sidebar.jsp" />
    </definition>
    
    <definition name="WILDCARD:admin/*" extends="admin.definition">
       <put-attribute name="title" value=" - {1}" />
       <put-attribute name="body" value="/WEB-INF/jsp/views/admin/{1}.jsp" />
    </definition>
    
</tiles-definitions>

We’re using the extends attribute to inherit as much as possible from the base definition. The admin base definition just overrides the sidebar location. We could if we wanted use this as a base for defining individual tiles views. Instead we have a definition with a name of “WILDCARD:admin/*”. This means any tiles view requested where the name starts admin/* will use this definition. The rest of the tile name is used to replace the {1} replacement variables.

So for example, if your controller returns a string of “admin/user” spring will look for a tile with this name. It should find the wildcard def and insert the title as “user” and the body jsp as /WEB-INF/jsp/views/admin/user.jsp.

There’s a bit more info in the tiles documentation.

Advertisements

Auto scanning tiles defs with spring MVC

The merits of Tiles vs Sitemesh
If you’re using Spring MVC with JSP then the chances are you’re going to want some kind of templating framework to build the pages. I started using Apache Tiles first but became frustrated with the amount of XML config required. Each page to be rendered via tiles needs to be defined in an XML file, if you have a lot of views this file will quickly become unwieldy.

Looking for an alternative I came across a framework called sitemesh. This takes a different approach to tiles resulting in far less configuration. The main difference is that tiles is a composite view framework, whereas, sitemesh is a decorator. A tiles view is composed of many smaller components, these might be the header, sidebar and body content. Each component in the definition will be a JSP containing markup for just that part of the page. Sitemesh on the other hand can take an entire webpage and merge it with a decorator page to get the final result.

I tried Sitemesh because I was drawn to the minimal configuration but after a while I found I wanted some of the old Tiles functionality. Specifically I found I wanted to create different sidebar fragments for different groups of pages. You could do this using Sitemesh by creating different decorators based on url patterns but this would place restrictions on the urls. I decided to take another look at tiles and see if there was a more convenient way of organising the tiles defs.

Configuring tiles with Spring MVC
As it turns out Spring MVC provides a neat way of configuring tiles. First we need to setup a view resolver for tiles based views. You can configure as many view revolvers as you like with an order of priority so I have a JSP resolver that is used if a tiles definition cannot be found.

Here is a snippet from the context-servlet.xml.

  <bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    <property name="viewClass">
      <value>org.springframework.web.servlet.view.tiles2.TilesView</value>
    </property>
    <property name="order" value="0" />
  </bean>

  <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
    <property name="order" value="1" />
  </bean> 

Now when my Spring MVC controllers return a String (view name), first it will check for a tile definition with a matching name and if that fails it then looks for a jsp in /WEB-INF/jsp/***viewname***.jsp. This gives me the flexibility of being able to display plain old JSP’s if I want too. This is the typical tiles configuration that I had used previously.

The cool bit is the way we load the tiles definitions. As of Tiles 2.2 there is a new auto-loader which automatically configures Tiles and turns on all the new features. It’s kind of like Tiles’ answer to mvc:annotation-driven. This means we no longer have to manually enter locations for the tiles xml configurations. Instead they will be scanned automatically providing we use the correct naming convention. This allows us to separate the definitions into as many files as is convenient whilst not having to maintain a list of the xml files in the context-servlet.xml.

  <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
    <property name="completeAutoload" value="true" />
  </bean> 

See Spring doc

This tells spring to defer configuring tiles to the new auto-loader in Tiles 2.2. To use this you’ll need to make sure you have tiles 2.2 or greater and also tiles-extras.jar. This is the tiles part of my pom if you’re using Maven.

  <dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-servlet</artifactId>
    <version>2.2.2</version>
  </dependency>

  <dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-extras</artifactId>
    <version>2.2.2</version>
  </dependency>
		
  <dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-jsp</artifactId>
    <version>2.2.2</version>
  </dependency>

From the Tiles documentation here is a list of the features this will turn on.

  • Freemarker and Velocity support
  • using EL, OGNL and MVEL as evaluation languages
  • using Wildcards and Regular expression as pattern matching languages
  • loading Tiles 1.x definition files
  • loads all the files named “tiles*.xml” under /WEB-INF and under every META-INF in any part of the classpath

A hierarchy of tiles
So now I have a folder structure under WEB-INF which contains all of my tiles views. At the top level I have a basic template.

    <definition name="base.definition" template="/WEB-INF/jsp/template/layout.jsp">
        <put-attribute name="title" value="" />
        <put-attribute name="header" value="/WEB-INF/jsp/template/header.jsp" />
        <put-attribute name="sidebar" value="/WEB-INF/jsp/template/sidebars/sidebar.jsp" />
        <put-attribute name="body" value="" />
        <put-attribute name="footer" value="/WEB-INF/jsp/template/footer.jsp" />
    </definition>

I then override this basic template with child templates that contain the correct sidebar to suit the context. E.G. for the admin section:

    <definition name="admin.definition" extends="base.definition">
        <put-attribute name="sidebar" value="/WEB-INF/jsp/template/sidebars/admin.jsp" />
    </definition>

Finally I can create my admin views based on the admin template.

    <definition name="admin" extends="admin.definition">
        <put-attribute name="title" value=" - Admin" />
        <put-attribute name="body" value="/WEB-INF/jsp/views/admin/admin.jsp" />
    </definition>

I could have setup my tiles defs like this before without the auto-loader. The only difference now is it’s easier for me to split the defs up into different tiles.xml files. This allows you to put the tiles defs in the same folder as the related JSP’s and have the whole lot organised in a logical folder structure. It also means you’re less likely to have to maintain one monster of an xml file that contains all your definitions or have to keep dipping into the context.xml to keep adding a new tiles.xml location.

Note: in the next post you can combine this with wildcards to really minimize the xml configuration required.
Wild card tiles defs with spring MVC

Enum translations in Spring MVC

I recently came across this requirement but it wasn’t immediately obvious how to implement it. I have an enum in my application which defines the locales supported. For instance you might setup enum values of EN, FR and ES. (if English, French and Spanish are the languages you have translation properties files for) I then wanted to display a dropdown on the user screen with the values of this enum as available languages to select.

First stab
Here is the enum in question and my first go at displaying it.

/**
 * Languages supported with translations for the UI. A user can view the application in one 
 * of these languages.
 * 
 * @author Ben Thurley
 */
public enum SupportedLanguage 
{
  EN, FR, ES;
}

I got this piece of JSP from an answer on Stack Overflow

<form:select path="supportedLanguage">
   <form:options/>
</form:select>

This worked ok except the select dropdown just displayed EN, FR and ES. I needed a way of translating these codes into text for the users current selected language. I did see some solutions suggesting doing this in the controller. You could read all values and create a custom map of options with the right piece of translated text. I didn’t like the idea of this though as it was adding translation logic to the controller when for every other element it’s taken care of by Spring. There had to be another way and as it turned out, there was.

Spring custom formatter
Spring MVC allows us to write our own custom formatters. I just needed one that would take my enum code and format it to the translated string equivalent for the users locale.

This is what I ended up with:

public class SupportedLanguageFormatter implements Formatter<SupportedLanguage>
{

  @Resource
  private MessageSource messageSource;
	
  @Override
  public String print(SupportedLanguage object, Locale locale) 
  {
    // Lookup message translation, no args, default of English
    return messageSource.getMessage("supported.language." + object.name().toLowerCase(), null, "English", locale);
  }

  @Override
  public SupportedLanguage parse(String text, Locale locale) throws ParseException 
  {
    return SupportedLanguage.parse(text);
  }
}

This formatter does a two way translation. The print method takes a SupportedLanguage and looks up the relevant translated String. This is easily located using the injected MessageSource. For the reverse operation the parse method takes in a code from the UI (such as EN or FR) and returns an instance of the SupportedLanguage enum.

I tweaked the enum a bit so the parsing was encapsulated within the SupportedLanguage enum. Here’s the final version.

public enum SupportedLanguage 
{
  EN, FR, ES;

  public static List<SupportedLanguage> list() 
  {
    return Arrays.asList(values());
  }
	
  public static SupportedLanguage parse(String value)
  {
    SupportedLanguage lang = null;
    	
    for(SupportedLanguage test : list())
    {
      if(test.name().equalsIgnoreCase(value))
      {
        lang = test;
    	break;
      }
    }
    	
    return lang;
  }  
}

We then register the new formatter with spring.

  <mvc:annotation-driven conversion-service="conversionService">
  </mvc:annotation-driven>

  <!-- Custom formatters -->
  <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatters">
      <set>
        <bean class= "org.languagetool.web.formatter.SupportedLanguageFormatter" />
      </set>
    </property>
  </bean>

Now the SupportedLanguage enum is ready to be used. I did this by registering a ModelAttribute in the controller.

@Controller
@RequestMapping(value="admin/users")
@SessionAttributes({"userForm", "authoritiesList", "supportedLanguagesList"})
public class UserController
{
  ...
  @ModelAttribute("supportedLanguagesList")
  public List<SupportedLanguage> getAllSupportedLanguages() 
  {
    return SupportedLanguage.list();
  }  
  ...
}

This can now be used in the JSP as a set of options, I also added in an empty option if no language is selected on a new user form.

<form:select path="supportedLanguage">
  <spring:message code="option.select" var="selectOptionText" />
  <form:option value="" label="${selectOptionText}" />
  <form:options items="${supportedLanguagesList}" />
</form:select>

That’s almost it, now you just have to remember to add the translations to the relevant properties files.

# Languages
supported.language.en=English
supported.language.es=Spanish
supported.language.fr=French

Spring MVC with restful DataTables

Up until recently I’ve been using jqGrid for the app I’m working on but I wanted to re-work the UI to make use of twitter bootstrap. I’m not a graphic designer and TB certainly gives you a polished looking app with minimal effort. After some research it seemed that jQuerys DataTables plugin had better UI support for TB than jqGrid. jqGrid is reliant on jQuery UI for its look and feel which as of yet doesn’t have a complete TB theme (though there is work in progress).

If you want to know how to get DataTables displaying in a TB style then the main website has a good walkthrough. It’s really not too difficult. http://datatables.net/blog/Twitter_Bootstrap_2

What was slightly trickier was processing the AJAX requests using Spring MVC. Most of the examples on the site are with PHP where it is quite normal to manually retrieve request parameters. In contrast with Spring MVC the aim is to have a POJO controller and map each parameter on the request to a method parameter. Unfortunately when it comes to sort columns DataTables passes a number to tell you how many columns to sort on and then each column is on the request with an index as part of the parameter name.

You end up needing this kind of thing.

$sOrder = "";
if ( isset( $_GET['iSortCol_0'] ) )
{
  $sOrder = "ORDER BY ";
  for ( $i=0 ; $i<intval( $_GET['iSortingCols'] ) ; $i++ )
  {
    if ( $_GET[ 'bSortable_'.intval($_GET['iSortCol_'.$i]) ] == "true" )
    {
      $sOrder .= "`".$aColumns[ intval( $_GET['iSortCol_'.$i] ) ]."` ". mysql_real_escape_string($_GET['sSortDir_'.$i] ) .", ";
    }
  }
  $sOrder = substr_replace( $sOrder, "", -2 );
  if ( $sOrder == "ORDER BY" )
  {
    $sOrder = "";
  }
}

Now you could do something similar by passing the request object into the method and let your method retrieve the parameters manually but this defeats the object a bit and makes unit testing harder. Instead I decided to create a class to model the DataTables parameters and get Spring to populate it automatically. It’s then possible to pass this single object down through the layers to the place that needs to do the DB query. This makes the parameter lists much shorter and easier to maintain since you can simply add an attribute to the containing object rather than having to visit every method signature.

My object to hold the basic paging criteria is a simple POJO. I’ve made it immutable because you shouldn’t be able to override the inputs that the user has set.

public final class PagingCriteria
{
  private final Integer displayStart;
  private final Integer displaySize;
  private final List<SortField> sortFields;
  private final Integer pageNumber;

  public PagingCriteria(Integer displayStart, Integer displaySize, Integer pageNumber, List<SortField> sortFields)
  {
    this.displayStart = displayStart;
    this.displaySize = displaySize;
    this.pageNumber = pageNumber;
    this.sortFields = sortFields;
  }

  public Integer getDisplayStart() {
    return displayStart;
  }

  public Integer getDisplaySize() {
    return displaySize;
  }

  public List<SortField> getSortFields()
  {
    return Collections.unmodifiableList(sortFields);
  }

  public Integer getPageNumber()
  {
    return pageNumber;
  }

}

I’ve also extracted the concept of a sort field and direction into a further class with an enum representing sort directions.

public final class SortField 
{
  private final String field;
  private final SortDirection direction;
	
  public SortField(String field, SortDirection direction)
  {
    this.field = field;
    this.direction = direction;
  }
	
  public SortField(String field, String direction)
  {
    this.field = field;
    this.direction = SortDirection.valueOfCaseInsensitive(direction);
  }

  public String getField() {
    return field;
  }

  public SortDirection getDirection() {
    return direction;
  }

}
public enum SortDirection 
{
  ASC("asc"), DESC("desc");
	
  private String direction;
  private static final Logger logger = LoggerFactory.getLogger(SortDirection.class);
	
  private SortDirection(String direction)
  {
    this.direction = direction;
  }
	
  public String getDirection()
  {
    return this.direction;
  }
	
  public static SortDirection valueOfCaseInsensitive(String value)
  {
    String valueUpper = value.toUpperCase();
    SortDirection sd = SortDirection.valueOf(valueUpper);
    logger.debug("SortDirection converted "+value+" to "+sd);
    return sd;
  }
}

The PagingCriteria object is passed into my Spring MVC controller something like this.

@Controller
@RequestMapping(value="customers")
public class CustomerController
{

  @Autowired
  private CustomerService customerService;

  @RequestMapping(value="/get", method=RequestMethod.GET)
  public @ResponseBody WebResultSet<Customer> getCustomers(@TableParam PagingCriteria criteria)
  {
    ResultSet<Customer> customers = this.customerService.getCustomers(criteria);
    return ControllerUtils.getWebResultSet(criteria, customers);
  }
...

Ignore the return type for the moment, we’ll come back to that. This is mapping all GET requests to /customers/get to the getCustomers method. It takes in a single parameter for the paging criteria which has been annotated with @TableParam. This is a custom annotation created for populating PagingCriteria with parameters on the request. The annotation class file itself is pretty straightforward.

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TableParam
{

}

So it’s just a simple marker. We’re using it to annotate method PARAMETER’s and we want to retain this annotation at RUNTIME. Next we need a class that can resolve this annotation and return our PagingCriteria object.

public class TableParamArgumentResolver implements WebArgumentResolver
{

  private static final String S_ECHO = "sEcho";
  private static final String I_DISPLAY_START = "iDisplayStart";
  private static final String I_DISPLAY_LENGTH = "iDisplayLength";
  private static final String I_SORTING_COLS = "iSortingCols";

  private static final String I_SORT_COLS = "iSortCol_";
  private static final String S_SORT_DIR = "sSortDir_";
  private static final String S_DATA_PROP = "mDataProp_";

  public Object resolveArgument(MethodParameter param, NativeWebRequest request) throws Exception
  {
    TableParam tableParamAnnotation = param.getParameterAnnotation(TableParam.class);

    if (tableParamAnnotation!=null)
    {
      HttpServletRequest httpRequest = (HttpServletRequest) request.getNativeRequest();

      String sEcho = httpRequest.getParameter(S_ECHO);
      String sDisplayStart = httpRequest.getParameter(I_DISPLAY_START);
      String sDisplayLength = httpRequest.getParameter(I_DISPLAY_LENGTH);
      String sSortingCols = httpRequest.getParameter(I_SORTING_COLS);

      Integer iEcho = Integer.parseInt(sEcho);
      Integer iDisplayStart = Integer.parseInt(sDisplayStart);
      Integer iDisplayLength = Integer.parseInt(sDisplayLength);
      Integer iSortingCols = Integer.parseInt(sSortingCols);

      List sortFields = new ArrayList();
      for(int colCount=0; colCount<iSortingCols; colCount++)
      {
        String sSortCol = httpRequest.getParameter(I_SORT_COLS+colCount);
        String sSortDir = httpRequest.getParameter(S_SORT_DIR+colCount);
        String sColName = httpRequest.getParameter(S_DATA_PROP+sSortCol);
        sortFields.add(new SortField(sColName, sSortDir));
      }

      PagingCriteria pc = new PagingCriteria(iDisplayStart, iDisplayLength, iEcho, sortFields);

      return pc;
    }

    return WebArgumentResolver.UNRESOLVED;
  }
}

All we do is implement WebArgumentResolver which has a single method resolveArgument. One of the parameters to this method is the request which allows us to retrieve the named request parameters we need. DataTables just passes a column index for sorting but we can use this index to lookup the column name too if mDataProp is used to initialise DataTables.

It’s very important to check first that the annotation being processed is our TableParam and if not to return WebArgumentResolver.UNRESOLVED. The first time through Spring calls each resolver in turn until it finds one that handles that annotation. This is cached and from then on it will always call that resolver for that annotation.

All that’s left now is to register our resolver with spring in the servlet context. This is much easier in Spring 3.1 because of the extra parameters you can now pass to mvc:annotation-driven.

  <mvc:annotation-driven>
    <mvc:argument-resolvers>
      <bean class="org.languagetool.web.utils.TableParamArgumentResolver" />
    </mvc:argument-resolvers>
  </mvc:annotation-driven>

That’s pretty much it. Now when a request comes in to getCustomers(), Spring  sees the @TableParam annotation and calls our custom resolver. This wings around the request picking up all the DataTables parameters and puts them in a PagingCriteria which it then returns. The great thing is once you’ve set this up you can easily reuse it throughout your controllers.

But what about the return type on the controller? If we take another look at the controller method.

@RequestMapping(value="/get", method=RequestMethod.GET)
public @ResponseBody WebResultSet<Customer> getCustomers(@TableParam PagingCriteria criteria)
{
  ResultSet<Customer> customers = this.customerService.getCustomers(criteria);
  return ControllerUtils.getWebResultSet(criteria, customers);
}

There are a couple of things here. The service call is returning a ResultSet object but the controller returns a WebResultSet. This has been annotated with @ResponseBody so if the request object has a request type of JSON then this object will be automatically parsed and returned as JSON. Likewise a content type of XML would return XML assuming the correct dependencies are on the classpath (Jackson and JAXB respectively).

My services are returning a ResultSet object which is similar to PagingCriteria in that it simply wraps the returning data. It’s basically a collection but with extra bits needed to display in the grid such as a record count. The ResultSet holds a collection of DomainObject’s, this is my layer super type for JPA entities which I will be posting about at a later date.

public final class ResultSet<T extends DomainObject>
{
  private final List<T> rows;
  private final Long totalDisplayRecords;
  private final Long totalRecords;

  public ResultSet(List<T> rows, Long totalRecords, Long totalDisplayRecords)
  {
    this.rows = rows;
    this.totalRecords = totalRecords;
    this.totalDisplayRecords = totalDisplayRecords;
  }

  public List<T> getRows()
  {
    return Collections.unmodifiableList(rows);
  }

  public Long getTotalDisplayRecords()
  {
    return totalDisplayRecords;
  }

  public Long getTotalRecords()
  {
    return totalRecords;
  }
}

DataTables itself though will be looking for these parameters to have specific names. It also must have the sEcho parameter from the request sent back. This is a “draw count” which datatables increments each time it makes a request. (Thanks to Sean in the comments for explaining this) Luckily we retrieved sEcho behind the scenes into our PagingCriteria so we use the static ControllerUtils.getWebResultSet() method to build us a DataTables friendly resultset.

public static <T extends DomainObject> WebResultSet<T> getWebResultSet(PagingCriteria pc, ResultSet<T> rs)
{
  return new DataTablesResultSet<T>(pc, rs);
}
public class DataTablesResultSet<T extends DomainObject>  implements WebResultSet<T>
{
  private final Integer sEcho;
  private final Long iTotalRecords;
  private final Long iTotalDisplayRecords;
  private final List<T> aaData;

  public DataTablesResultSet(PagingCriteria pc, ResultSet<T> rs)
  {
    this.sEcho = pc.getPageNumber();
    this.aaData = rs.getRows();
    this.iTotalRecords = rs.getTotalRecords();
    this.iTotalDisplayRecords = rs.getTotalRecords();
  }

  public Integer getsEcho() {
    return sEcho;
  }

  public Long getiTotalRecords() {
    return iTotalRecords;
  }

  public Long getiTotalDisplayRecords() {
    return iTotalDisplayRecords;
  }

  public List<T> getAaData() {
    return Collections.unmodifiableList(aaData);
  }
}

So WebResultSet is simply a marker interface that’s used as a return type on the controllers. We then determine in ControllerUtils.getWebResultSet that it’s a DataTablesResultSet we want. In theory we can now tweak both the inputs and outputs from DataTables or any other grid we choose without having to change the controllers or services.  We’ve essentially removed the controllers dependency on the JavaScript grid implementation.

The next step is to wrap the view layer in custom JSP tags