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

Advertisements