Spring MVC and Apache Tiles integration example

Note: This is a re-edit of the post Spring 3 and Tiles 2 Integration. It uses now the latest version of Apache Tiles (at the time of the writing 3.0.1) and presents how Apache Tiles is used on top of Spring/Spring MVC to construct the layout of the Podcastpedia.org website

1. Why Apache Tiles?

Well, because it

  • is a free open-sourced templating framework for modern Java applications.  Based upon the Composite pattern it is built to simplify the development of user interfaces.
  • remains, for complex web sites, the easiest and most elegant way to work alongside any MVC technology.

Tiles allows authors to define page fragments which can be assembled into a complete pages at runtime. These fragments, or tiles, can be used as simple includes in order to reduce the duplication of common page elements or embedded within other tiles to develop a series of reusable templates. These templates streamline the development of a consistent look and feel across an entire application.

Octocat Source code for this post is available on Github - podcastpedia.org is an open source project.

2. Necessary artifacts

Make sure you have the Tiles jars in the classpath. They can be downloaded directly from the official website, or by adding the proper dependencies to the pom.xml file, if you use Apache Maven. I use the latter.

You can use one dependency to download all Tiles supported technologies with the following dependency declaration:

<dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-extras</artifactId>
    <version>${tiles.version}</version>
</dependency>

or, as in my case where I didn’t need all the extras, you can put in your pom.xml only the ones you need:

<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-jsp</artifactId>
	<version>${tiles.version}</version>
</dependency>
<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-servlet</artifactId>
	<version>${tiles.version}</version>
</dependency>
<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-template</artifactId>
	<version>${tiles.version}</version>
</dependency>
<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-el</artifactId>
	<version>${tiles.version}</version>
</dependency>

Note: The tiles.version is currently 3.0.1. For this to work you need to use at least version 3.2 of Spring MVC.

3. Spring application context configuration for Tiles

For better readability and separation of concerns the Spring Tiles configuration has been placed in a separate application context file pcm-tiles.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
		http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- Views mapped in views.properties (PDF, XLS classes, and others) -->
    <bean id="contentNegotiatingResolver"
              class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="order"
                  value="#{T(org.springframework.core.Ordered).HIGHEST_PRECEDENCE}" />
         <property name="favorPathExtension" value="true"/>
        <property name="contentNegotiationManager">
            <bean class="org.springframework.web.accept.ContentNegotiationManager">
                <constructor-arg>
                    <bean class="org.springframework.web.accept.PathExtensionContentNegotiationStrategy">
                        <constructor-arg>
                            <map>
                              <entry key="html" value="text/html"/>
                              <entry key="pdf" value="application/pdf"/>
                              <entry key="xsl" value="application/vnd.ms-excel"/>
                              <entry key="xml" value="application/xml"/>
                              <entry key="json" value="application/json"/>
                              <entry key="atom" value="application/xml"/>
                            </map>
                        </constructor-arg>
                    </bean>
                </constructor-arg>
            </bean>
        </property>
     </bean>
    <bean id="tilesViewResolver"
     class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView" />
        <property name="order" value="#{contentNegotiatingResolver.order+1}" />
    </bean>

    <bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
      <property name="basename" value="views"/>
      <property name="order" value="#{tilesViewResolver.order+1}" />
    </bean>

    <!-- Helper class to configure Tiles 3.x for the Spring Framework -->
    <!-- See http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/servlet/view/tiles2/TilesConfigurer.html -->
    <!-- The actual tiles templates are in the tiles-definitions.xml  -->
    <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
        <property name="definitions">
            <list>
                <value>/WEB-INF/tile-defs/templates.xml</value>
                <value>/WEB-INF/tile-defs/definitions.xml</value>
            </list>
        </property>
    </bean>
</beans>

Of interest here are

  • the TilesConfigurer (line 53) entry bean, which simply configures a TilesContainer using a set of files containing definitions (templates.xml and definitions.xml), to be accessed by TilesView instance. This is a Spring-based alternative (for usage in Spring configuration) to the Tiles-provided TilesListener (for usage in web.xml)
    • the tilesViewResolver instance (line 37) which is a org.springframework.web.servlet.View implementation that retrieves the Tiles definitions. The order and the contentNegotiatingResolver properties will be discussed in another post.

    4. Creating and using Tiles pages

    After installing and learning some of Tiles concepts, it’s time to show you how the pages are created for Podcastpedia.org

    4.1. Create a template

    Podcastpedia.org uses sort of a classic layout page structure:

    Template

    Template

    As you can see the layout of the website is made of

    • header which includes the social media connect, language selection logo and search bar
    • navigation bar (menu)
    • content – bulk data (body)
    • footer which includes links to Contact, About Us, Disclaimer pages

    The first step was to create a JSP page that acts as this layout and place it under _/WEB-INF/template/_template.jsp file:

    <!DOCTYPE HTML>
    <%@ include file="/WEB-INF/template/includes.jsp" %>
    
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8" %>
    
    <html>
    	<head>
    		<meta name="google-site-verification" content="xxxxxxxxx" />
    		<title><tiles:insertAttribute name="title" ignore="true"/></title>
    		<meta name="description" content="<tiles:insertAttribute name="page_description" ignore="true"/>">
    		<c:url var="cssURL" value="/static/css/style.min.css"/>
    		<link href="${cssURL}" rel="stylesheet" type="text/css"/>
    		<link rel="icon" href="<c:url value="/static/images/favicon.ico"/>" type="image/x-icon" />
    		<link rel="shortcut icon" href="<c:url value="/static/images/favicon.ico"/>" type="image/x-icon" />
    		<meta charset="utf-8">
    		<meta property="og:image" content="<tiles:insertAttribute name="og_image" ignore="true"/>" />
    		<meta property="og:title" content="<tiles:insertAttribute name="og_title" ignore="true"/>" />
    		<meta property="og:description" content="<tiles:insertAttribute name="og_desc" ignore="true"/>"/>
    		<link rel="stylesheet" href="<tiles:insertAttribute name="jquery_ui_css" ignore="true"/>" />
    	</head>
        <body>
        	<div id="banner">
    			<tiles:insertAttribute name="header" />
    		</div>
    		<div></div>
    		<tiles:insertAttribute name="navigation_bar" />
    		<div></div>
    		<div id="page">
    			<tiles:insertAttribute name="content" />
    		</div>
    		<div></div>
    		<div id="footer_wrapper">
    			<tiles:insertAttribute name="footer" />
    		</div>
    	</body>
    </html>

    You can see highlighted the four attributes header, navigation_bar (menu), content (body) and footer, which resemble the template layout from the picture. Besides these there are still some other Tiles attributes, like title, page_description or og_image, that will get filled when rendering actual pages.

    4.2. Create the composing pages

    In this phase I had to create four JSP pages, that will take place of header, navigation_bar (menu), content (body) and footer attributes in the previously created template.

    4.4. Create a definition

    A definition is a composition to be rendered to the end user; essentially a definition is composed of a template and completely or partially filled attributes.

    • If all of its attributes are filled, it can be rendered to the end user.
    • If not all of its attributes are filled, it is called an abstract definition, and it can be used as a base definition for extended definitions, or their missing attributes can be filled at runtime.

    4.4.1. Create abstract definition

    Initially I created a /<em>WEB-INF/<em>tile-defs/template</em>.xml,</em> with some default .jsp files (defaultHeader.jspdefault_navigation_bar.jspdefaultContent.jsp and defaultFooter.jsp). This file acts as an abstract definition and is extended by all the other Tiles definitions across the Podcastpedia application:

    <?xml version="1.0" encoding="UTF-8" ?>
    
    <!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="defaultTemplate" template="/WEB-INF/template/template.jsp">
            <put-attribute name="header" value="/WEB-INF/template/defaultHeader.jsp" />
            <put-attribute name="navigation_bar" value="/WEB-INF/jsp/navigation_bar/default_navigation_bar.jsp" />
            <put-attribute name="content" value="/WEB-INF/template/defaultContent.jsp"/>
            <put-attribute name="footer" value="/WEB-INF/template/defaultFooter.jsp" />
        </definition>
     ......
    </tiles-definitions>

    4.4.2. Create concrete definitions

    Now let’s see some examples of definitions extending the defaultTemplate defined above. For example the definition rendering the home page of Podcastpedia.org looks like the following:

    <tiles-definitions>
     ......
    	<definition name="startPage_def" extends="defaultTemplate">
    		<put-attribute name="title" value="Podcastpedia, knowledge to go" />
        	<put-attribute name="page_description" value="Educate yourself with selected podcasts from various domains such as science, technology, education, medicine, people, environment, spirituality and much more..."/>
    		<put-attribute name="navigation_bar" value="/WEB-INF/jsp/navigation_bar/start_navigation_bar.jsp" />
    		<put-attribute name="content" value="/WEB-INF/jsp/start/start_page.jsp"/>
        	<put-attribute name="og_title" value="Podcastpedia, knowledge to go"/>
     	    <put-attribute name="og_desc" value="Educate yourself with selected podcasts from various domains such as science, technology, education, medicine, people, environment, spirituality and much more..."/>
     	    <put-attribute name="og_image" value="http://www.podcastpedia.org/static/images/fb_share.png"/>
    	</definition>
     ......
    </tiles-definitions>

    Note that the Tiles attributes are filled with static values specific to the start page, and there are some attributes from the defaultTemplate (like header and footer) that are not overriden – that means they will be inherited from the defaultTemplate (one of the advantages of inheritence).

    Let’s see another example. The  podcastDetails-definition is used to render a podcast details page :

    <tiles-definitions>
     ......
        <definition name="podcastDetails" extends="defaultTemplate">
        	<put-attribute name="title" expression="${podcast.title}"/>
     	<put-attribute name="page_description" expression="${podcast.description}"/>
        	<put-attribute name="navigation_bar" value="/WEB-INF/jsp/navigation_bar/podcast_details_navigation_bar.jsp" />
        	<put-attribute name="content" value="/WEB-INF/jsp/podcastDetails.jsp"/>
        	<put-attribute name="og_title" expression="${podcast.title}"/>
     	<put-attribute name="og_desc" expression="${podcast.description}"/>
     	<put-attribute name="og_image" expression="${podcast.urlOfImageToDisplay}"/>
        </definition>
     ......
    </tiles-definitions>

    Notice here how I use dynamic values for the Tiles attributes – for example the title attribute (line 4) is set dynamically with a value(${podcast.title}) passed from the controller. If you follow the link – The Naked Scientist Podcast – and inspect the source code or the tab name in your browser, you will notice the title of the page was set to “- The Naked Scientists Podcast – Stripping Down Science”

    Octocat Source code for this post is available on Github - podcastpedia.org is an open source project.

    5. Summary

    Well, this is pretty much all you need to do to integrate Tiles in Spring MVC – you’ve learned what libraries you need, how to configure Tiles in the application context, how to create abstract and concrete Tiles definitions, and fill them with both static and dynamic values.

    6. Resources

    1. Spring 3 and Tiles 2 Integration
    2. Upgrade from Tiles 2 to Tiles 3 on Spring MVC application
    3. http://tiles.apache.org/
    4. http://www.springsource.org/
    Podcastpedia image

    Adrian Matei

    Creator of Podcastpedia.org and Codingpedia.org, computer science engineer, husband, father, curious and passionate about science, computers, software, education, economics, social equity, philosophy - but these are just outside labels and not that important, deep inside we are all just consciousness, right?

    How to get the title of a remote web page using javascript and NodeJS

    This post presents how to use web scraping with Cheerio in a NodeJS backend to retrieve the title of a bookmark added in bookmarks.codingpedia.org Continue reading

    How to use Showdown in Angular and NodeJS

    Published on February 21, 2017

    Sharing coding bookmarks

    Published on February 11, 2017