Java Development News:

Tiles 101/201

By Patrick Peak

01 Apr 2003 | TheServerSide.com

Introduction

Many developers familiar with Java Open Source projects should be familiar with Struts, the jakarta.apache Java web application framework. Struts' primary focus is to provide a control layer for the web application, reducing both construction time and maintenance costs. By itself Struts doesn't handle page layout. That responsibility falls to a recent addition to Struts, the Tiles Framework.


Goal

Many technical articles focus on all the things you can do with a software package. My goal with this article is to describe things you should do to create a well-organized, maintainable application using Struts and Tiles. The article is divided into two sections. In Tiles 101, I'm going to talk about how to create a standard layout using tiles. In Tiles 201, I'm going to cover ways to use Tiles Controllers to improve the overall architecture of a web app.


Tiles 101 - Standardizing your layout


The basics - What is The Tiles Framework?

The Tiles Framework is an advanced version of <jsp:include/> that comes bundled with the Struts web application framework. Its purpose is to reduce the duplication between jsp pages as well as make layouts flexible and easy to maintain. It integrates with Struts using the concept of named views or definitions.


Best Practices

The best way to organize a StrutsTiles site is to put the "layout" logic in a Tiles Definition (usually called the tiles-def.xml) file. This one file contains all the layouts for the entire site. When you add a new page, you add a new Struts mapping (the Controller), add a new Tiles definition (the View's Layout) and the new jsp (the View). For a site with 20 pages, you would have 22 files (1 tiles-def.xml, aSiteLayout.jsp, + 20 content pages). Each of the content pages has no other responsibility other than displaying their own data.


The Model

One of the things that Struts doesn't handle at all is how to organize your model and/or your database. For these examples, I'm going to use a very simple Active Record pattern. With an Active Record each object represents a single row in the database. My model consists of Penguin objects (Plain Old Java Objects - POJOs) with static methods for querying for penguins from the database. So a call like Penguin.findAll() would return a list of all the Penguin objects in the database. The underlying persistence mechanism that talks to the database can be JDBC, Hibernate or what ever else you choose. In a real application, the main benefit of this is a very simple API for the Struts' actions to call. Here, it makes the examples easier to understand.


Welcome to the Penguin Store

As an example, lets start with a fictional example site, the Penguin Store (my specialized franchise of the J2EE petstore). On our home page we want to display a list of penguins that are on sale. So we add our struts mapping.

<action type="penguinstore.LoadPenguinsAction" path="/Home">
     <forward name="success" path=".view.penguins" />
</action >

The LoadPenguinsAction fetches a list of penguins and sticks them in a request attribute called "penguins". The forward routes to a Tiles definition called ".view.penguins". This is how Struts and Tiles integrate. Struts lets you forward to a definition instead of a jsp. Note: the ".view.penguins" is just a logical name, and the . syntax is just a naming convention for definitions I borrowed from Struts in Action.


The Definition

So then we add our definition to our tiles-def.xml file.

<definition name=".view.penguins" extends=".basic.layout">
     <put name="title" value="Welcome to the Penguin Store."/>
     <put name="content" value="/jsp/viewPenguins.jsp"/>
</definition >

It has several important parts. First off the definition name, ".view.penguins" is a unique named view within our application. (i.e. I shouldn't create two of them). Second, the "put" elements stick attributes into the definition that the jsp page is going to use. In this case, the title of the page, and the content jsp to use. Third, it extends another definition, which means it inherits that definition's attributes. (Similar to class inheritance). We'll come back to extensions later, but extensions are one of the ways Tiles reduce duplication between pages.


The content page

So let's look at the content page jsp, /jsp/viewPenguins.jsp. It looks like this.

<c:forEach var="penguin" items="${penguins}">
     <c:out value="${penguin.name}"/> : 
</c:forEach>

That's it. Note, I used the JSTL taglib syntax (c:forEach) rather that than the Struts taglib syntax (logic:iterate). I'm also not including any taglib declaration in the jsps.


Where's the rest of the Html?

Where's the header, footer and navigation menu of the web page? That is another definition called ".basic.layout" that I mentioned before. It is also in the same tiles-def.xml file our ".view.penguins" definition is in. It looks like.

<definition name=".basic.layout" path="/jsp/basicLayout.jsp >
     <put name="title" value="Default title"/>
     <put name="header" value="/jsp/header.jsp"/>
     <put name="menu" value="/jsp/menu.jsp"/>
     <put name="content" value="/jsp/defaultContentPage.jsp"/>
     <put name="footer" value="/jsp/footer.jsp"/>
</definition>

In our .basic.layout definition I did a couple of things. One, I declared the jsp file (/jsp/basicLayout.jsp) that all the pages in my site will use for layout. Two, I put several attributes into the definition, that don't change from page to page. (Menu, header, footer). If you look back at the definition for ".view.penguins" you can see I "overrode" two of the attributes, title and content. This is the beauty of tiles, I can define my overall layout ONCE and just set the specifics of each page by overriding the tiles attributes I need.


The Layout Page

The /jsp/basicLayout.jsp pulls the attribute we defined in the definitions together to generate the final page. Let's take a look at that.

<html>
<header><title><tiles:getAsString name="title"/></title>
<body>
    <tiles:get name="header"/><br/>
    <tiles:get name="menu"/> <tiles:get name="content"/><br/>
    <tiles:get name="footer"/>
</body>
</html>

Here's where we bring everything together. The <tiles:getAsString/> tag spits out the title of the page (as a string rather than a jsp page). The <tiles:get> tags include their jsp bodies. You can see that while some parts (menu,footer) were defined in ".basic.layout", other parts (content, title) were defined in our specific view ".view.penguin".


The next step

Is this overkill for a one-page site? Yes. But for my second and subsequent pages I only need to add a very simple content jsp, a tiles definition and a struts mapping. And when I want to improve on my layout (maybe add some tables or some neat-o penguin pictures) its in one file, basicLayout.jsp.


Wrapping up Tiles 101

This example shows a "best practice" example of Struts/Tiles that uses the essential parts of tiles to create a site layout. Using tiles definitions I have dramatically reduced duplicate html code within my site as well as decoupled the views completely from the view layouts. And since Struts/Tiles integrate, I can easily figure what URL (/Home.do) maps to what action (LoadPenguinsAction) and goes to which view (.view.penguin).


Tiles 201 - Using Controllers

Controllers are a somewhat hidden feature that aren't typically discussed when the subject of Tiles comes up. Neither Struts in Action nor Programming with Struts discusses them. The only real documentation I've seen on it is by the creator of Tiles, Cedric Dumolin's web site. It's excellent, and covers Tiles in more depth than I'm going to here.


What's a Tiles Controller?

Start with the premise that a Tiles Controller is basically a mini-Action. It prepares data for viewing much like a Struts Action does. Tiles Controllers are not designed for controlling page flow or receiving input. That's what Actions do. (Note: Tiles can do some view selection logic, but that is beyond the scope of this discussion.) Each tile in an application can have it's own separate controllers that prepare data just for them. So for independent, one Action per page (or several linear chained Actions) you can have multiple independent controllers per page. (Portals anyone?)


What does one look like?

The org.apache.struts.tiles.Controller interface that a Tiles Controller implements has a single perform() method that is similar to Strut's Action classes execute() / perform() methods. It looks like this.

public void perform( ComponentContext context,
   HttpServletRequest request,
              HttpServletResponse response,
   ServletContext servletContext) throws ServletException,
         IOException;

The ComponentContext is a new scope, which I will call tiles scope, similar to the conventional servlet's request and session scopes. Tiles scope is where the put attributes in your definitions end up. Also there are no ActionMappings or ActionForms to be found.


So why bother?

When I first looked at TilesControllers I asked myself this very question. If I have Actions, why do I need TilesControllers? Let's extend the Penguin Store example to illustrate.


Return of the Penguin Store

Currently, my home page does one logical thing. It loads a list of penguins into the view. Now sales have been brisk and I want to add some more functionality to my site. I want to display a "Penguin of the Day" that's on sale up by the header and a list of my Penguin store's locations (100+ and growing). That is three logically different things my home page is going to do. So I just add some Penguin.findPenguinOfTheDay() and Store.findAll() calls to my LoadPenguinsAction, right? Wrong. Since every page on my site needs to display the PenguinOfTheDay and AllStores, I would need to add those behaviors to every Action on the site. Or chain Actions. Two equally bad options.

Ignore for the moment the obvious, that I could just put these penguins into the session or application scopes. Pretend it's penguinChosenAtRandom if you like. Anyway, on with the example!

The solution? - Use a Tiles Controller.

To implement the Penguin Of the Day using tiles, we need to do three things. Write a Controller, link our definition to it, and add the view. So let's write the controller.

class LoadPenguinOfTheDayController implements Controller{
    public void perform(... request, ...) // exceptions {
        Penguin penguin = Penguin.findPenguinOfTheDay();
        request.setAttribute("penguinOfTheDay", penguin);
    }
}

I put the penguins into a request-scoped attribute called "penguinOfTheDay ", rather than the tiles scope, mainly because it keeps the view code simpler. If I need the same tile twice on a page (i.e. with different penguins each time), I could put them into tiles scope by rewriting as...


public void perform(ComponentContext tilesContext, ...) {
        tilesContext.putAttribute("penguinOfTheDay", penguin);
}

Written this way, each Tile's view gets it's own tiles scoped penguin and they won't step on each other. The jsp view page can then use a <tiles:useAttribute name="penguinOfTheDay"/> tag to copy the Tiles scoped attribute into the page. This lets you use the <c:forEach> and <c:out> tags to display the list of penguins.


Linking to a Definition

Here's the original home page tiles definition in my tiles-def.xml file from Tiles 101.

<definition name=".basic.layout" path="/jsp/basicLayout.jsp >
     <put name="title" value="Default title"/>
     <put name="header" value="/jsp/header.jsp"/>
     <put name="menu" value="/jsp/menu.jsp"/>
     <put name="content" value="/jsp/defaultContentPage.jsp"/>
     <put name="footer" value="/jsp/footer.jsp"/>
</definition>

Since my header is the place I want my penguins to go, I'm going to make the header into a tile by creating a definition for it. So...


<put name="header" value="/jsp/header.jsp"/>

becomes...


<put name="header" value=" .penguin.header"/>

Then I create my new header tile definition that declares my penguin's controller as its controllerClass.


<definition  name=".penguin.header"
  path="/jsp/header.jsp"
  controllerClass ="penguinstore.LoadPenguinOfTheDayController "/>

This means that on every page my ".penguin.header" tile appears my LoadPenguinOfTheDayController.perform() method will be called and will load the penguinOfTheDay into the request to be displayed.


The View

Finally, I alter the header.jsp to look like this.

Welcome to the Penguin Store<br/>
On sale today - <c:out value="${penguinOfTheDay.name}"/> : <c:out value="${ penguinOfTheDay.price}

So now each page can display the same data with no duplication between actions.


Inserting Definitions directly into pages

Next, lets work on that load all Stores functionality. My definition looks like this.

<definition  name=".view.allStores"
  path="/jsp/viewAllStores.jsp"
  controllerClass ="penguinstore.LoadStoresController "/>

Tiles can also insert definitions directly into any jsp in the site using the tag. So I do this by adding the following tag to my menu.jsp page:


<tiles:insert definition=".view.allStores"/>

I could have attached the controllerClass directly to the menu tile's definition. In this case, I want to display the list of stores both in the menu and on the main home page. I can now use the <tiles:insert> like a tag library anywhere in my site to display a list of stores.


Implications

This example is pretty simple, but these snippets of code have some pretty profound implications for Struts.

  • Reduces duplication between Actions - Moving the "load the blue penguin into the request" type behavior from an Action into a Tile means that Actions are simpler and can stick to just data collection and page flow.
  • Reduces the need for Action chaining. If you need to do more than one logical thing per page, use multiple tiles (with controllers) rather than multiple actions.
  • Tiles can act as "quasi" tag libraries - I could have written a ".penguin.header" Tile as a tag library instead of a tile. But if a tile has significant display logic, do I want to bury that in Java code where my interface guys can't tweak it? Taglibs are a vehicle for reuse; invest some time now, get some reuse later. What are the odds I am going to reuse my ".penguin.header" tile in another project? You aren't going to need it. (YANGI).
  • Better separation of roles - My html interface guys can move or copy the ".view.allStores" tile to another page without having to involve a Java developer to change Action code.

Summary

Tiles Controllers should be considered a top tier Struts class, alongside Actions, ActionForms and ActionMappings. In this section, I have covered what Controllers are, what they can do for you and how to set a basic one up. Tiles controllers can act as mini-actions, handling just their view, oblivious to rest of the web app around them. They help organize a project better so Action's can focus on page flow rather than preparing views. Finally, they make very complex multipart layouts possible that would be torturously complex using a single Action.

Using both the layout and controller features of Tiles, you can go a long way in improving a Struts-based web app, by reducing html duplication and better organizing the control logic. Tiles should be considered an essential tool when developing with Struts.


References