Sunday 17 March 2013

Spring MVC with Apache Tiles 3

There is a dearth of tutorials which examine Spring MVC with Apache Tiles 3. Most of them allude to this one, which is insufficient. Having recently integrated Tiles 3 into my Spring MVC project, I have decided to make a short tutorial on how to get everything running.

What you need : Apache Tiles 3, Spring Framework 3.2.2 (earlier versions may not support Tiles 3 by default), Spring MVC

Steps :

Add your Tiles dependency to pom.xml (or download the Tiles 3 jar and add to your classpath if you're not using Maven) :
<dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-extras</artifactId>
    <version>3.0.0</version>
</dependency>
Make sure you're using Spring 3.2.2 :
<properties>
    <java-version>1.6</java-version>
    <org.springframework-version>3.2.2.RELEASE</org.springframework-version>
    <org.aspectj-version>1.6.9</org.aspectj-version>
    <org.slf4j-version>1.5.10</org.slf4j-version>
</properties>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>3.2.2.RELEASE</version>
</dependency>
In your servlet-context.xml (or whatever you named yours) :
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    <!--Don't add suffix or prefix like you do with .jsp files-->
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
</bean>

<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer" >
    <property name="definitions">
        <value>/WEB-INF/tiles.xml</value>
    </property>
</bean>
If you were using the traditional Spring MVC view resolution method with jsp files, your servlet-context.xml might have had something like this :
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
</bean>
Please remove that, since you're using a new mechanism to resolve views.

Create a tiles.xml file under your WEB-INF directory :
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">

<tiles-definitions>

    <definition name="*/*" template="/WEB-INF/tiles/template.jsp">
        <put-attribute name="meta" value="/WEB-INF/tiles/{2}/meta.jsp" />
        <put-attribute name="header" value="/WEB-INF/tiles/{2}/header.jsp" />
        <put-attribute name="body" value="/WEB-INF/tiles/{2}/body.jsp" />
        <put-attribute name="footer" value="/WEB-INF/tiles/{2}/footer.jsp" />

    </definition>

</tiles-definitions>
Here's the directory structure :

Create a template.jsp under WEB-INF/tiles. Here's an example :
<!DOCTYPE HTML>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>

<html>
    <head>
        <tiles:insertAttribute name="meta" />
    </head>
    <body>
        <div id="header">
            <tiles:insertAttribute name="header" />
        </div>
        <div id="body">
            <tiles:insertAttribute name="body" />
        </div>
        <div id="footer">
            <tiles:insertAttribute name="footer" />
        </div>
    </body>
</html>
Whatever java controller you're using, make sure that the view you're returning matches the wildcard pattern in tiles.xml e.g., "/index" will match "*/*" in tiles.xml and you can use {2} to get the "index" string and tiles will render the tiles under the tiles/index folder :
@Controller
public class IndexController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView indexHandler() {
        ...
        return new ModelAndView("/index", "user", new SomeObject());
    }
}
Lastly, you can add your views under the WEB-INF/tiles directory. Remember that you'll need the same jsp files which are described in your tiles.xml file.

Extra :

If you require list attribute inheritance (which was implemented as a "fallback" here, and did not work for me at all), then change your tiles.xml to :
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">

<tiles-definitions>

    <definition name="base" template="/WEB-INF/tiles/template.jsp">
        <put-attribute name="meta" value="/WEB-INF/tiles/common/meta.jsp" />
        <put-attribute name="footer" value="/WEB-INF/tiles/common/footer.jsp" />
    </definition>

    <definition name="*/*" extends="base">
        <put-attribute name="header" value="/WEB-INF/tiles/{2}/header.jsp" />
        <put-attribute name="body" value="/WEB-INF/tiles/{2}/body.jsp" />
    </definition>

</tiles-definitions>
What this essentially does is use the jsp files from the common folder, so you can avoid copying and pasting the same jsp file over and over again in all your subfolders. My directory structure now looks like this :
Notice that I don't need to have the footer.jsp and meta.jsp in all the WEB-INF/tiles subfolders.