Basic Tiles Integration

Version 7.1 by Ken McWilliams on 2012/03/20 23:24

About Apache Tiles

This is background for people new to templates. If you know you want to integrate tiles skip to the heading Adding Tiles to your Struts2 Project.

If you are here you are aware of the problem: Web pages share a lot of commonality. Being Java programmers we are well versed in the problems of code duplication (because Object Oriented Programming has excellent tools to combat this issue we are ever vigilent.)  Simply, it makes reuse and maintenance difficult.  Two tools we use without hardly a thought in our Object Oriented arsenal is Composition and Inheritance.

Think then of a Template as an Object, which has as its properties a body and values which are inserted into that body.

If we want to reuse a template, it would be nice to say: I would like to create a new template exactly like my other template, but I want to override part of it with another implementation (that is I want to substitute the body portion with other data). If so, tiles is for you!

So we can think of a template as being like a Java Object.  We can use composition to group templates together for complicated page layout and we can use inheritance to reuse a template by changing just the portions we want to suit our needs.

In this way we can remove all the code duplication.

With Apache Tiles we reap the following benefits (I'm assuming JSPs as the view but Freemarker and Velocity are also supported, although you may need to upgrade to tiles 2.2.2 which is the subject of another tutorial):

  • JSPs reflect only relevant content.  For instance, it is a lot easier to understand a page which just concerns the body content than trying to understand a page marked up for both content and presentation and vice versa it is a lot easier to understand a template that is not loaded with content.
  • It is easy to create templates in parts. ie: You can have a template to layout the head (html head element - to bring in required java script and css resources), page header, menu, body and footer. Then you can have several templates specifically for each of the previous components.
  • Increases the separation between designers and programmers. There should be more separation between JSPs which acquire and display data and JSPs which act as templates, the later of which the designers are more interested in. 
  • Tiles xml provides a source from which all template definitions can be viewed at once (this is the main strength over JPS includes and will be expanded upon in the next section).

Apache Tiles vs SiteMesh vs JSP includes

SiteMesh and Apache Tiles differ in how they construct pages.  Tiles primarely uses a composition pattern while Sitemesh Uses a decorator pattern for a better discussion of the difference see here. To complicate matters and narrow the distance Apache Tiles has added decorator features as well but this is currently beyond the scope of these tutorials.

The Decorator pattern is the more powerful of the two but it is also the more conceptually complicated, requires more computational overhead and may not ease the separation between designers and programmers (actually it can nessesitate that designers are programmers)... for these reasons I recommend looking at Apache Tiles first.  It is a rather simple system which is easy to initially learn, if you find you need more power then look to SiteMesh.  It should be noted that you can use both Tiles and SiteMesh in a Struts2 application and if decoration is required then that is the recommended way (By Antonio Petrelli see here).

Apache Tiles offers more than JSP includes.  The main usage difference is that the tiles.xml file provieds a single source of definition, which defines what resources are brought together to produce a view.  JSPs on the other hand mix the resources of the view (the JSP it self) with the content that must be included.  What this means, is if a complicated layout has all the commonality factored out both JSPs using includes and JSPs assembled with Tiles, there would as many JSPs using either system however to understand the JSPs using includes you would need to visit each page and then visit each of those pages in turn to see what is being included.  It's very much like Rusian Dolls, to know what you are getting you'll need to open each one up.  Contrast this with tiles: Each JSP should be terminal and not include any other resources on their own (it is the job of Apache Tiles to insert the resources into the pages as defined in the tiles.xml and marked in the JSPs by Tiles tags). The result is that you should be able to find the resource which needs modification more readily with Tiles. Put another way, Apache Tiles reaches out, gathers all the resources it needs puts them together and spits out the result. It is easy to understand because the definition for everything it needs is defined in one place.

The Tiles XML file and Tiles tags do add a little overhead but with any new tool there is a cost of learning. The tags are quite simple and the XML is easy enough to understand that designers can definitly derive benifits from adding the management of the tiles.xml file to their list of responsibilities (it will make their lives easier). A site wide overhaul will be far easier with tiles than it would be without it.

Adding Tiles to your Struts2 Project

This tutorial only requires we have a working Struts2 project, to set one up see here.

First: Add struts2-tiles-plugin-VERSION.jar to your classpath, where VERSION is the same version as struts2-core

Modify your web.xml to include the new sections (in bold)

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <listener>
        <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class>
    </listener>
    <context-param>
        <param-name>tilesDefinitions</param-name>
        <param-value>/WEB-INF/tiles.xml</param-value>
    </context-param>
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Set up your struts.xml to process the action

[TODO: provide annotation based version as well]

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

<struts>
    <constant name="struts.devMode" value="true" />
    <constant name="struts.ui.theme" value="simple" />
    <package  name="basicstruts2"  namespace="/" extends="tiles-default">
        <result-types>
            <result-type name="tiles" default="true" class="org.apache.struts2.views.tiles.TilesResult"/>
        </result-types>
        <action name="test" >
            <result>tilesWorks</result>
        </action>
    </package>
</struts>

Add the following example /WEB-INF/tiles.xml to your project:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
       "http://tiles.apache.org/dtds/tiles-config_2_1.dtd">
<tiles-definitions>
    <definition name="baseLayout" template="/WEB-INF/tiles/template.jsp">
        <put-attribute name="title" value="My Title"/>
        <put-attribute name="header" value="/WEB-INF/tiles/header.jsp"/>
        <put-attribute name="body" value="/WEB-INF/tiles/body.jsp"/>
        <put-attribute name="footer" value="/WEB-INF/tiles/footer.jsp"/>
    </definition>
    <definition name="tilesWorks" extends="baseLayout">
    </definition>
</tiles-definitions>

Create the following JSPs required by tiles.xml

Create file /WEB-INF/tiles/template.jsp

<%@taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@taglib prefix="s" uri="/struts-tags"%>
<%@page contentType="text/html" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title><tiles:insertAttribute name="title"/></title>
    </head>
    <body>
<tiles:insertAttribute name="header"/>
<tiles:insertAttribute name="body"/>
<tiles:insertAttribute name="footer"/>
    </body>
</html>

Create file /WEB-INF/tiles/body.jsp

<div>

    This is the default body.

</div>

Create file /WEB-INF/tiles/header.jsp

<div>

    This is the default header.

</div>

Create file /WEB-INF/tiles/footer.jsp

<div>

    This is the default body.

</div>

Test

You should now be able to run the application supplying test as the action and you should see tiles in action.