Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Quadriga as well as several other of the Java/Spring web applications developed in the Digital Innovation Group use Apache Tiles for managing the layout of the application. This tutorial will you walk through a very basic example of how to implement Apache Tiles with Spring.

...

Code Block
languagexml
titleTiles dependencies
<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-template</artifactId>
	<version>3.0.1</version>
</dependency>
<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-core</artifactId>
	<version>3.0.1</version>
</dependency>
<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-api</artifactId>
	<version>3.0.1</version>
</dependency>
<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-servlet</artifactId>
	<version>3.0.1</version>
</dependency>
<dependency>
	<groupId>org.apache.tiles</groupId>
	<artifactId>tiles-jsp</artifactId>
	<version>3.0.1</version>
</dependency>

...


<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
	<version>1.7.5</version>
</dependency>

Next, open the servlet-context.xml file in your WEB-INF > spring folder. From there, remove the InternalResourceViewResolver bean declaration and add the following code:

Code Block
languagexml
titleTiles beans declaration
<beans:bean id="tilesViewResolver" 
	class="org.springframework.web.servlet.view.UrlBasedViewResolver" >
	<beans:property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
</beans:bean>
<beans:bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
    <beans:property name="definitions">
         <beans:list>
             <beans:value>/WEB-INF/tiles-defsdef.xml</beans:value>
         </beans:list>
    </beans:property>
</beans:bean>

Your rootservlet-context.xml file should now look like this:

Code Block
languagexml
titlerootservlet-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="edu.asu.diging.tutorial.spring" />

    <bean id="tilesViewResolver" 
		class="org.springframework.web.servlet.view.UrlBasedViewResolver" >
		<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">
            <list>
                <value>/WEB-INF/tiles-defsdef.xml</value>
            </list>
        </property>
	</bean>
</beans>

You declared two beans in this piece of XML. First, you specify that you want to use a UrlBasedViewResolver to resolve your views. As a property you give the resolver a TilesView. The second bean, the TilesConfigurer, "simply configures a TilesContainer using a set of files containing definitions, to be accessed by TilesView instances. This is a Spring-based alternative (for usage in Spring configuration) to the Tiles-provided ServletContextListener (e.g. CompleteAutoloadTilesListener for usage in web.xml.)" (Spring TilesConfigurer Javadoc)

Inside the "definitions" property, you specify where Tiles can find the definitions telling it what templates to render. In our example, the definitions are in a file called "tiles-def.xml" inside the WEB-INF folder.

Tiles Definitions

Ok, now let's create the tiles-def.xml file we just told Tiles about. Create a new file inside your WEB-INF folder, and paste the following code in it:

Code Block
languagexml
titletiles-def.xml
linenumberstrue
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!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>
	<!-- Base definition -->
	<definition name="base.definition" template="/WEB-INF/tiles/layouts/skeleton.jsp">
		<put-attribute name="title" value="" />
		<put-attribute name="currentPage" value="home" />
	</definition>
	<!-- Home pages -->
	<definition name="home" extends="base.definition">
		<put-attribute name="title" value="Quadriga - Login" />
		<put-attribute name="content" value="/WEB-INF/views/home.jsp" />
		<put-attribute name="footer" value="/WEB-INF/views/footer.jsp" />
	</definition>
</tiles-definitions>

For now, let's leave it with that. We'll come back to it later with more explanations.

The Skeleton

So far, we have talked about how to use Apache Tiles, however, we haven't said anything about why we would use it. Apache is a templating framework with the goal to make it easier to write and maintain webpages across an application. Remember, how in the first tutorial we wrote jsp pages like this:

...

Code Block
languagexml
titleSkeleton.jsp
linenumberstrue
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib prefix="sec"
	uri="http://www.springframework.org/security/tags"%>
<!DOCTYPE HTML>
<html>
<head>
	<title><tiles:insertAttribute name="title" /
<!DOCTYPE HTML>
<html>
<head>
	<title><tiles:insertAttribute name="title" /></title>
	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
</head>
<body>
	<tiles:importAttribute name="currentPage" scope="request" />

	<!-- Main -->
	<tiles:insertAttribute name="content" />

	<!-- Footer -->
	<div>
		<tiles:insertAttribute name="footer" />
	</div>
</body>
</html>

You just create the "base.definition" from line 6 of your tiles-def.xml file. The skeleton defines 4 attributes: title, currentPage, content, and footer. When Tiles renders a page, it'll will take the skeleton and replace the specified attributes with the values found in your tiles-def.xml. However, you will notice that the "base.definition" in our tiles-def.xml only defines two attributes: title and currentPage. This is due to the fact, that "base.definition" is intended to be extended by other definitions. It only defines basic default values that can be overridden by the extending definitions.

The second definition in tiles-def.xml specifies a complete page:

Code Block
languagexml
titleExtending Definition
linenumberstrue
<definition name="home" extends="base.definition">
	<put-attribute name="title" value="Home" />
	<put-attribute name="content" value="/WEB-INF/views/home.jsp" />
	<put-attribute name="footer" value="/WEB-INF/views/footer.jsp" />
</definition>

In line 1, you can see two attributes "name" and "extends". Name specifies the name of the definition (we will use the name later to specify what definition we want to use for a page). The extends attribute specifies that the current definition extends "base.definition". It therefore inherits all the attributes from base.definition. Line 2 overrides the title attribute from the extended definition and sets it to "Home". Line 3 and 4 specify what jsp page should be used for the content and footer section of the webpage.

Home and Footer

Now, let's create two more jsp pages to satisfy the "home" definition. Go to the folder WEB-INF > views and create two jsp pages: home.jsp and footer.jsp. Your folder structure should look like this now:

Image Added

Paste the following code into home.jsp:

Code Block
languagexml
titlehome.jsp
<h2>Hello World!</h2>

I am feeling: ${ mood.feeling }. See <a href="explanation/${mood.feeling}">here</a> to find out why.

You might notice, this is the exact same code that we used in index.jsp before. Next, paste the following code into footer.jsp:

Code Block
languagexml
titlefooter.jsp
This page was generated by me.

The Controller

Now, we're all set up on the jsp side! Let's take a look at the controller. Open the HomeController in the "web" package. The home method should currently return "index" (or "index2" depending on how you ended tutorial #1). Change this to return "home". The home method should now look like this:

Code Block
languagejava
titleHomeController
@RequestMapping(value = "/")
public String home(ModelMap map) {
    map.addAttribute("mood", service.getCurrentMood());
    return "home";
}

Run it!

Ok, time for a first try. Let's run the application on a server (or restart the server if you already deployed the application before) and go to http://localhost:8080/tiles (or http://localhost:8080/one if you use the same project as in tutorial #1). You should see something like this:

Image Added

Whoohoo! Our page is up. However, when clicking on the "here" link will bring up an error message saying:

"javax.servlet.ServletException: Could not resolve view with name 'explanation' in servlet with name 'dispatcher'"

What's happening? Well, let's take a look at the controller that backs the second page. In the ExplanationController we can see that the method for showing the explanation, returns "explanation":

Code Block
languagejava
titleExplanationController
@RequestMapping(value="/explanation/{mood}")
public String showExplanation(@PathVariable("mood") String mood, Model model) {
	model.addAttribute("mood", mood);
	model.addAttribute("explanation", moodService.getExplanation());
	return "explanation";
}

The error message tells us exactly where the problem is: there is no definition with name "explanation", so Tiles doesn't know what page to render. In the HomeController we returned "home". Tiles used that string to lookup what definition would describe that view (the definition with name "home"). For "explanation", however, there is no definition. Let's create one!

Adding a new definition

Open the file tiles-def.xml, and add the following lines after the home definition:

Code Block
languagexml
titletiles-def.xml
linenumberstrue
<!-- Explanation page -->
<definition name="explanation" extends="base.definition">
	<put-attribute name="title" value="Explanation" />
	<put-attribute name="content" value="/WEB-INF/views/explanation.jsp" />
	<put-attribute name="footer" value="/WEB-INF/views/footer.jsp" />
</definition>

Similar to the home definition, we're overriding the title attribute with the string "Explanation", we use the same footer.jsp page for the footer attribute, but we use a different jsp page for the content attribute. In other words, the page should look exactly like the Home page, but have a different content (given that we didn't do add layout to the page, it's not difficult to create a page that looks exactly like the first one, but it's the principle that counts here!).

What's still missing is the explanation.jsp page. Or actually, if you called the explanation page in tutorial #1 "explanation.jsp", then you already have that! If so, open that jsp page. Delete everything that is not inside the "body" tag. It should look similar to this:

Code Block
languagexml
titleexplanation.jsp
<h2>So you want to know why...</h2>
I am feeling ${ mood } because ${explanation}
<p>
<a href="/tiles">Maybe my mood has changed?</a>

If you don't have a explanation.jsp file yet, go to the folder WEB-INF > views and create a file called "explanation.jsp". Then post the above content in it (of course taking into account changes necessary for you specific application).

Restart the server and try it again. You should now be able to happily switch between your two pages!

You can find the code for this tutorial here.

Now what?

This was just a very short  introduction to Tiles. In this simple example, it might not seem like much, but image you have 100 jsp pages all with the same layout, and at least some using the same components (like menus or comment sections). Tiles can help you manage the different components independent of each other, avoiding repetitions in your code.

If you'd like to work a little more with Tiles, try to implement the following:

  • Add a third "About" page similar to the explanation page.
  • Add a menu to each page that links to the Home and About page.
  • On every page, only show the links in the menu that are not the current page (e.g. show only a link to Home on the About page, but not to About).