Chapter 20. Layout Management with Tiles

This chapter introduces the Tiles framework and illustrates how it can provide structure and flexibility in presenting an interface to customers of your Web site. The chapter also demonstrates how to integrate Tiles with the Struts framework discussed in the last chapter.

This chapter covers the following topics:

  • An introduction to Tiles

  • Installing Tiles for use in a Web application

  • Tiles definitions

  • Tiles tags

  • Advanced Tiles usage with parameters and inheritance

  • The use of Tiles with Struts

Introduction to Tiles

Web developers have an awful habit of forgetting one of the most important concepts of software development: reuse. Why do something twice when you can do it once and use it everywhere? It has been said that some of the best software developers are essentially lazy souls who are always trying to ensure that they develop something only once, designing it flexibly enough so that it, whatever it is, can be used everywhere. Many of the techniques and tools explained in this book are created with this goal in mind. The Tiles framework is no different.

Tiles was first developed as a simple templating framework called Components by Cedric Dumoulin, and has since been renamed to Tiles to avoid the confusion that could result from using that word. Today, Tiles has been completely integrated into the Struts framework, although it can still be used independently.

What is the Tiles framework?

Pages that are displayed on Web sites often contain areas that are repeated in many places; menus, headers, and footers are typical examples. Web sites, therefore, are often designed in a layout or a number of layouts that don't change; only the content or what is actually displayed in those areas changes. Developers have traditionally tackled this challenge by using jsp:include invocations in an attempt to modularize these display elements. This approach, while achieving some degree of modularity, is limited because the included pages can become tightly bound to the pages in which they are used, with no way to define them, generally, across the application.

The Tiles framework enables a developer to use templates that contain common parts of an interface, without using includes. Tiles also organizes these templates into a series of reusable components that can be referenced from within the application.

For example, Figure 20-1 shows a very typical Web page layout. The separate areas have been shaded differently.

A simple Tiles template

Figure 20.1. A simple Tiles template

The page displayed here uses a template (or layout in Tiles terminology) that defines four separate areas, or regions. In the Tiles framework, these regions are referred to as tiles. A tile in this context is a simple JSP page that displays some content: the header, some menu items, or the footer of a page. The layout that has been developed to show this page is a JSP file as well. It makes use of the tiles that have been provided and places them in the correct positions.

Figure 20-2 illustrates this structure of layouts and tiles being used to display a page.

JSP page using a Tiles layout with certain Tiles pages

Figure 20.2. JSP page using a Tiles layout with certain Tiles pages

In this example, the index.jsp file defined which layout it will use and which tiles will be used in that layout. The tiles are passed to the layout as parameters. Note that the names defined in the layout page are consistent with the names of the tiles, such as header, footer, and body. When the tiles are passed to the layout, it uses these names to determine where each should be placed.

This instance of usage could also be defined as a definition and given a name that can be referenced in the application. Therefore, instead of the index.jsp file stipulating which tiles to use with the layout, and other pages having to do the same, it is defined centrally in an XML configuration file or another JSP file and called on by whatever pages require it. Each particular usage of this definition could then add special characteristics it requires. This second level of abstraction provided by tiles is enormously powerful.

Tiles terminology

This chapter uses terms that could become confusing if not understood in the correct context. This section defines the terms used in tiles to ensure that you understand the concepts presented in the rest of the chapter.

Layout

A layout is a JSP page that uses tiles to create an interface. An application can make use of many different layouts. A layout is simply a template for how a series of tiles can be placed together to form a view.

The term layout is used in this chapter instead of template, as it is consistent with Tiles terminology. In practice, the word template is equally correct.

Tile

A tile is an element or region of a layout; it represents a section of an interface or Web page that is likely to be reused in many layouts. Examples include menus, headers, and footers.

Definition

A definition is an instance of a usage of a layout that has been defined in the application. Separate definitions can take a single layout and apply different sets of tiles to be displayed. A definition can be given a name and reused throughout a site.

Installing Tiles

The Tiles framework is available as part of the Struts framework described in Chapter 19, "Struts Framework," or on its own from the Tiles Web site at http://www.lifl.fr/~dumoulin/tiles/index.html.

This chapter uses Tiles on its own and then investigates its integration with the Struts framework. The following steps will prepare a Web application on your computer to use the Tiles framework without using Struts:

  1. Download the latest distribution from the Tiles Web site at www.lifl.fr/~dumoulin/tiles/index.html.

  2. Extract the file you downloaded into a directory on your computer; in a Windows system this might be C: iles.

  3. Copy the tiles.jar file from C: ileslib into the WEB-INF/lib directory of your Web application.

  4. Copy the struts-tiles.tld and tiles-config.dtd files from C: ileslib into the WEB-INF directory of your Web application.

  5. Tiles also requires some commons libraries from Jakarta (http://jakarta.apache.org/commons/). Download the following commons libraries and include the associated JAR files in the WEB-INF/lib directory:

    • Commons Digester: commons-digester.jar

    • Commons BeanUtils: commons-beanutils.jar

    • Commons Collections: commons-collections.jar

    • Commons Logging: commons-logging.jar

  6. Although this step isn't compulsory for Tiles, the examples in this chapter use the JSTL. Place the jstl.jar and standard.jar files from the JSTL you have used in previous chapters in the /WEB-INF/lib directory

  7. Make sure the WEB-INF/web.xml file in your Web application contains the following:

    <?xml version="1.0" encoding="ISO-8859-1" ?>
    
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
                 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
             version="2.4">
    
        <icon></icon>
        <display-name>Tiles Examples</display-name>
        <description>This is a Tiles Example</description>
    
      <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.apache.struts.tiles.TilesServlet</servlet-class>
        <init-param>
          <param-name>definitions-config</param-name>
          <param-value>/WEB-INF/tiles-defs.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>*.do</url-pattern>
      </servlet-mapping>
    
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
    </web-app>

    The servlet specified here is provided by the Tiles framework, and is required in order to initialize the Tiles factory with the specified configuration file (tiles-defs.xml). This configuration file will define the Tiles definitions available in the Web application. In addition to the definitions-config parameter, the following table describes the additional parameters that can be set.

    Attribute

    Description

    definitions-config

    Points to a Tiles configuration file or files. A comma should separate multiple values.

    definitions-debug

    Indicates the level of debugging that should be output from Tiles. Appropriate values here are 0, 1, or 2. A value of 2 will place Tiles in full debug mode.

    definitions-parser-details

    Similar to debug, but this parameter outputs detailed debugging information about parsing of the configuration file. Again, appropriate values are 0, 1, or 2.

    definitions-parser-validate

    Indicates that the tiles configuration file should be validated upon initialization. Values appropriate here are true and false.

    definitions-factory-class

    A developer can create a custom definition factory. In order for it to be initialized, this parameter would be used.

  8. Create a file called tiles-defs.xml in the WEB-INF directory of your Web application. Note that the location matches the value of the init-param in the servlet definition in the previous step. Place the following content in the tiles-defs.xml file. This content doesn't actually do anything yet, but it will ensure that the file can be initialized properly when the servlet container loads it:

    <!DOCTYPE tiles-definitions PUBLIC
           "-//Apache Software Foundation//DTD Tiles Configuration//EN"
           "http://jakarta.apache.org/struts/dtds/tiles-config.dtd">
    
    <tiles-definitions>
    </tiles-definitions>

Your application is now ready to use Tiles in the following Try It Out exercise. The examples in this chapter assume you are familiar with establishing an application in a servlet container such as Tomcat and applying changes as each example is discussed.

How It Works

Central to this example is the layout and the JSP page that uses it. The simple-insert-index.jsp page has used the tiles:insert tag to invoke a layout:

...
<tiles:insert page="/layout/simplelayout.jsp" flush="true">
...
</tiles:insert>

Nested within the insert tag are various put tags that define which tiles the layout will use. Each of these put tags defines a name for the tile with the name attribute and the file that is associated with that name in the value attribute:

...
<tiles:put name="menu" value="/tiles/simple-menu.jsp" />
...

We are effectively passing parameters into the layout page so that it knows which pages to use. These parameters are placed into the tile scope of the layout page, which you will learn more about later.

The layout page itself uses the tile values passed to it in order to display the correct tiles. Each placeholder for a tile uses the insert tag to display the page passed in the put tag from the invoking page:

...
<td><tiles:insert attribute="menu"/></td>
...

The attribute menu was passed to this layout page as an attribute that is now within its scope. The insert tag here inserts the content that the menu parameter represents: simple-menu.jsp.

From this simple example, you can see how the layout can be given various references for the tiles it expects to receive. These could even be determined dynamically, changing the behavior of the layout based on various conditions such as user preferences or internationalization settings.

Tile Scope

Throughout this book, you will have become familiar with the various scopes that are available from within JSP pages: application, session, request, and page. The Tiles framework introduces a new scope in order to avoid clashes with attributes that may appear in other scopes. This new scope is called tile scope. Internally, tile scope refers to a Map of objects placed within the existing page context. Some tags in the Tiles tag library allow you to copy values from the tile scope into the standard scopes you are already familiar with. From there, you can use the value just like other values in JSP pages.

As you will see in the following sections, when an attribute is passed to a layout tile or standard tile, it is placed within the tile scope of that tile only; it is not available to the other tiles that are being used. Developers must explicitly manage the scope and delegation of values from layout to tile and so on. This explicit declarative approach means that tiles will not become mistakenly reliant on an attribute unless it is deliberately made available. This enhances the decoupled nature of the components of the application.

Definitions

One limitation of the previous example is that the simple-insert-index.jsp page actually stipulates the tiles to be used, as well as the layout itself. What if this configuration were required from another page on the site? The same insert tag would be used, with the same parameters being passed. This is duplication, one of the many traps that Web applications fall into. Were a change to be made, such as the name of the simple-menu.jsp file, then each instance would have to be separately changed and tested. In a large system this is a serious problem, and in a smaller one it's just annoying.

Remember the comment that some of the best programmers are lazy? Making such a site-wide change could mean a lot of work. Thankfully, Tiles provides a way to define this instance of layout usage in a central place so that it can be used by the entire application and changed only in one place if the need arises.

This concept is the definition that was discussed earlier. Definitions can be stored centrally in two ways: within a JSP file that is included on pages within the site, or within an XML configuration file that resides in the /WEB-INF/ directory of your Web application. Once a definition is specified, JSP pages can use the layout by using the insert tag in a slightly different way from the simple-insert-index.jsp page in the preceding example. If the definition were defined in a Tiles configuration file, the content of that page would appear as follows:

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<tiles:insert definition="simple.layout" flush="true" />

Note that there is no reference to the specific tiles used within the JSP page anymore. The following Try It Out exercise illustrates the use of a definition, as specified within a JSP page.

Tiles Tags

In the preceding examples, you saw the use of some of the Tiles tags that are available. This section introduces these and the remaining tags, and explains the attributes that can be used with them.

insert

You have seen the insert tag used in at least two capacities so far: First it was used within the nontile JSP pages so that a layout could be inserted and various tiles passed to it as parameters with the put tag. Second, it was used in the layout JSP page to display values it had been passed. The following table explains the attributes available for this tag.

Attribute

Description

attribute

The value for this attribute is the name of an attribute, such as the name of a tile or other component. This attribute corresponds to the name attribute in the put tag when the parameter is specified.

beanName

The name of a bean to be used. The beanScope attribute is used to define the scope in which this bean should be retrieved. The bean specified here could be a definition that has been declared.

beanProperty

Used in conjunction with the beanName attribute. Use of this attribute causes the value to be retrieved from this property of the bean specified.

beanScope

The scope used to retrieve the value specified in beanName. This attribute is optional; page scope is the default value.

component

Same as the page and template attributes. It represents the location of a tile or layout.

controllerClass

The Struts action class used before this insert tag is invoked. Only one of controllerUrl or controllerClass can be used.

controllerUrl

Used with Struts to specify an action used before this insert tag is invoked.

definition

Used to insert a definition defined in the tiles-defs.xml configuration file. The value of this attribute would be a definition id.

flush

Used to flush the body of the page to the output stream after this tag is invoked.

ignore

Used in conjunction with the name attribute. If the bean specified in the name attribute does not exist and the ignore attribute is set to true, no error is thrown and no value is displayed. When set to false, a runtime error would be displayed. The default value of this optional attribute is false.

name

Used to identify a bean to be inserted by this tag.

page

See component.

role

Used to specify the role that a user must have in order for this tag to be executed by the server.

template

See component.

definition

This tag is used to establish a definition as described earlier. A corresponding definition element in a tiles-defs.xml file performs the same basic function. This tag effectively creates a bean that can be used with the insert tag. This tag also supports more advanced usage whereby a new definition can inherit and add to the characteristics of another definition defined in the tiles-defs.xml file. You will see an example of this behavior in subsequent sections. The following table describes the attributes of the definition tag.

Attribute

Description

extends

The value of this optional attribute is the id of another definition defined in the tiles-defs.xml file. This allows the new definition to assume all the characteristics of the other definition.

id

The identifying name of this definition. Used by the insert tag to insert this definition into a page. Required.

scope

As this tag effectively creates a bean to be used, this attribute specifies the scope into which this bean will be placed. The default value of this optional attribute is page.

template

The value of this optional attribute is a path to a tile or layout to be used with this definition.

page

See template.

role

This optional attribute is used to specify the role that a user must have in order for this tag to be executed by the server.

put

This tag is used to pass parameters to components. You saw this tag used earlier where paths to tiles (JSP pages) were passed within the confines of a definition tag and where the insert tag was used to insert a layout for display. The put tag can only be used within the definition or insert tags.

Attribute

Description

beanName

This optional attribute defines the name of a bean to be passed. The beanScope attribute is used to define the scope in which this bean should be retrieved.

beanProperty

This optional attribute is used in conjunction with the beanName attribute. Using this attribute causes the value to be retrieved from this property of the bean specified.

beanScope

The scope used to retrieve the value specified in beanName. This attribute is optional; page scope is the default value. Valid values here include application, session, page, component, template, and tile. The last three are all the same.

content

Same as value.

direct

See type. Manipulates the way in which the tag handles the type attribute when it is missing. If the type attribute is used, this attribute has no effect. The documentation states that this is included for JSP template compatibility.

type

This defines the content type of the value being put. Valid values here are as follows:

string: The value of the parameter is a text value and can be used directly (see direct).

page (template): The value of the parameter is content retrieved from another file. With type set as one of these values, the value attribute is treated as a path, rather than as a value itself, or a definition.

definition: Used where a definition is used as a parameter. The value here is the id or name of the definition being passed. The definition must have been defined in a definition factory, meaning it must have been added to the configuration XML file.

If the type attribute is not used and the direct attribute is set to true, the value is implicitly used as though string were set here. If direct is specified as false, the value is implicitly treated as though page or template were used here.

If the type attribute is not specified, but beanName is specified, then the object is treated as is, meaning as an object. See beanName, beanProperty, and beanScope.

This is an optional attribute.

name

The name of this parameter. The value of this required attribute is used to reference the value this tag represents.

value

An object that is the value being put. The value of this attribute is entered either directly as a string value, or as the name of a bean that is available where this tag is being used. Optional, but one of beanName, value, or content must be used.

role

The role of users for which this tag will be used. If the user is not in the specified role, this tag is ignored. This attribute is optional.

putList

The putList tag, like put, defines a parameter to be passed to another Tiles component; however, putList creates a list of other objects as the value. This tag can be used only inside the definition or insert tags.

Internally, putList represents the data created as a java.util.List object. Elements are added to the object created by putList with nested add tags. You can use the attributes shown in the following table with putList.

Attribute

Description

name

The name of this parameter. The value of this required attribute is used to reference the value this tag represents.

role

This optional attribute defines the role of users for which this tag will be used. If the user is not in the specified role, this tag is ignored.

add

The add tag is used to add items to a surrounding putList tag. It cannot be used within any other tag. Its behavior is identical to that of the put tag described earlier (internally, the AddTag extends the PutTag class), except the values are added to the list being defined. All the parameters described in put apply to this tag, with the exception of the name attribute.

get

The get tag is used to retrieve a value that has been placed within the tile scope, and either print out its value (in the case of a string) or include it where this tag is called (in the case of a tile). Internally, the GetTag class extends the InsertTag class. The main difference between the get tag and the insert tag, other than the former having significantly fewer attributes, is the default value for the ignore attribute. The following table shows the attributes available for working with the get tag.

Attribute

Description

name

The name of the attribute or value on which the tag will act. This is a required attribute.

ignore

If the value specified in name does not exist, this attribute determines whether an error will be displayed. Valid values here are true and false. The default value of this optional attribute is true.

flush

Used to flush the body of the page to the output stream after this tag is invoked. The default value of this optional attribute is true.

role

The role of users for whom this tag will be used. If the user is not in the specified role, this tag is ignored. This attribute is optional.

getAsString

The getAsString tag basically invokes a toString() method on the object it is passed, and renders it via the JspWriter. It accepts the attributes shown in the following table.

Attribute

Description

name

This required attribute defines the name of the attribute or value on which the tag will act.

ignore

If the value specified in name does not exist, this attribute specifies whether an error will be displayed. Valid values here are true and false. The default value of this optional attribute is false.

role

The role of users for whom this tag will be used. If the user is not in the specified role, this tag is ignored. This attribute is optional.

useAttribute

The useAttribute tag is used to copy a value from the tile scope into a specified scope and the page scope. If the scope attribute is not specified, the value is only exposed to the page scope. There the value can be used like any other variable in the page. useAttribute accepts the attributes described in the following table.

Attribute

Description

id

The variable name with which the value can be referenced in the page and specified scopes. The default value of this optional attribute is the name of the value, its existing name (specified in the name attribute).

name

This required attribute specifies the name of the value to be acted on by this tag.

scope

In addition to the page scope, the value will also be placed in the scope specified by the value of this attribute. Possible values are application, session, request, and page. Due to this tag's behavior, specifying page scope here would be redundant. This attribute is optional.

ignore

If the value specified in name does not exist, this attribute specifies whether an error will be displayed. Valid values for this optional attribute are true and false. The default value is false.

classname

The class of the value being acted on by this tag. It has no impact on the behavior of the tag.

importAttribute

This tag is used to import a single value or all values in the tile scope into a specified scope. If the name attribute is not used, all values in the tile scope are imported. This tag accepts the attributes shown in the following table.

Attribute

Description

name

This required attribute specifies the name of the value to be acted on by this tag.

scope

This optional attribute specifies the scope into which the value is imported. The default is page scope.

ignore

This optional attribute is relevant only when importing a single value. If the value specified in name does not exist, this attribute indicates whether an error will be displayed. Valid values here are true and false. The default value is false.

initComponentDefinitions

The initComponentDefinations tag is used to initialize the definition factory with the specified XML configuration file. The following table describes the attributes this tag accepts.

Attribute

Description

file

This required attribute specifies the file that contains the definition elements.

classname

This optional attribute specifies the classname of the factory to initialize.

Passing Parameters to Tiles

Earlier in this chapter, you saw how parameters were passed within the body of the insert tag and definition tags. The parameters passed were pointers to tiles to be used in the template or layout tiles. It is also possible to pass other values to tiles components using the put and putList tags.

A simple example of this is where a string is passed to a layout as part of a definition and then used within the layout tile or a content tile. The following code fragment shows a definition tag being used to pass various parameters to the layout tile, including a string that will form the title of a page:

<tiles:definition
           id="simple.include.layout.title"
           page="/layout/simplelayout-title.jsp"
           scope="request">
  <tiles:put name="title" value="This is my New Title" />
  <tiles:put name="header" value="/tiles/simple-header-title.jsp" />
  <tiles:put name="menu" value="/tiles/simple-menu.jsp" />
  <tiles:put name="body" value="/tiles/simple-body.jsp" />
  <tiles:put name="footer" value="/tiles/simple-footer.jsp" />
</tiles:definition>

The parameters passed to this layout are placed in the tile scope of the layout; they are now accessible from within the simplelayout-title.jsp page using the getAsString tag (this tag only retrieves values from the tile scope). You have seen in the previous examples how most of these parameters can then be used. In the case of the title parameter, it can be displayed in the simplelayout-title.jsp page with the following code:

<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>

<html>
  <head>
    <title>
      <tiles:getAsString name="title"/>
    </title>
  </head>
<body bgcolor="white">
...

This will display the value of the title parameter in the title of the Web page. What if you wanted to have the title parameter also within the scope of the header tile? The value would have to be specifically placed within the scope of that tile using the following addition to the insert tag of this tile in the simplelayout-title.jsp page:

...
  <td colspan="2" height="100" bgcolor="gray">
      <tiles:insert attribute="header">
          <tiles:put name="headerTitle" beanName="title"/>
      </tiles:insert>
  </td>
...

Now the value of the title parameter is within the tile scope of the header tile with an alias of headerTitle, and can be used in the same way. A String object suited our example here, but the same scope accessibility could be achieved with any Java object. What results from this of course is the capability to use the parameter within tiles that have been given access to it.

Advanced Tiles

The following sections build on the principles of tiles you have learned so far and introduce some advanced topics.

Definitions and inheritance

As discussed earlier, definitions are a powerful feature of the Tiles framework. Configuring various layout tiles, standard tiles, and other parameters under a common name enables them to be used everywhere in an application; reuse is a good thing.

Of course, there may be times when the definition used for 99 percent of a site doesn't suit the other 1 percent. For instance, in a members only area, a different menu tile might be presented, or the same tile with different parameters may be used. Your first approach might be to create another definition to suit this new situation, but then you would end up just replicating all the tiles and parameters that didn't change. All the brownie points you got for exercising reuse are gone.

Tiles provides an excellent solution to this and other scenarios in which certain characteristics of an existing definition need to be used in a new definition, a concept called inheritance. A cornerstone of object-oriented software development, inheritance means that one object inherits some or all of the characteristics of another object. The child entity may then override certain characteristics with its own version or even add new ones. Tiles uses the inheritance principle to allow definitions to inherit (or extend in Java speak) the characteristics of another definition. The application can then use the child definition in special situations. Note that you can implement as many generations of inheriting definitions as you want—that is, until it gets too complicated for you to understand. As always, features like this are often overused by eager developers who end up with something that is way too complicated for a human to understand and therefore becomes unmaintainable. Keep the structure of definitions simple and logical, even at the expense of some reuse if it keeps things clearer.

To illustrate, the following Try It Out exercise implements two definitions to be used in two different pages. The second page will use a definition that extends the first page by only changing some of its defined parameters.

How It Works

The result of the insert tags in the inh-son-index.jsp and inh-mother-index.jsp is to display the relevant definition elements, with the desired effect that the son definition (inh.son.layout) includes all the elements of the mother definition (inh.mother.layout), except those that were overridden—namely, the myMovies list and the menu tile.

Nesting tiles

In previous examples, we used a definition to render a layout with certain tile and data attributes. This has only really defined a very simple layout pattern of two levels: the layout and the tiles used by it. Some Web sites use more complex layouts and have features or regions of a page that can themselves use a separate layout. To accommodate this, Tiles enables developers to insert a definition within a layout. It doesn't take long to appreciate the limitless combinations of tiles, definitions, and layouts that this provides.

The following Try It Out exercise illustrates the use of a new definition from within a layout.

How It Works

This change we have made could be a little confusing. Let's step through the process again. Our goal was to replace the menus used by the mother and son definitions with a separate definition called menu, illustrating that a separate definition can be used from within a layout.

We first created a definition in the tiles-defs.xml file called menu. This new definition was given some data to display as menu items, defined within the definition entry of the tiles-defs.xml file. We then created a layout for the new definition to handle the layout for the menu itself. This new layout was provided with a new Tile called menuTile that displays the list of menu items. To verify that this worked, we displayed the menu on its own, as shown in Figure 20-5.

To make use of the definition properly, we then changed the inh-layout.jsp page to call on this new menu definition directly, meaning that both the mother and son definitions would now show the same menu. As a bit of cleanup, the menu put entry for the mother and son definitions in the tiles-defs.xml file was removed.

This is all well and good, but our layout JSP page will always display a menu in the left-hand region, and to change it we would have to modify the inh-layout.jsp file. Instead of inserting the definition directly in the layout file, the definition could be passed to the layout as just another attribute, configured as such in the configuration file. This is the goal of the next Try It Out exercise.

How It Works

In the preceding Try It Out example, we called the menu definition directly from the inh-layout.jsp file. The change in this section involved replacing this direct call with the use of a parameter that has been passed to the layout. This parameter just happened to be a definition, a definition to display the menu.

This is probably a good place to summarize the last few topics; we've made some real progress from some very humble beginnings.

We started with a simple mother/son definition inheritance in which a single layout was used but one definition was set up with certain parameters and another definition inherited some of these parameters, replacing the rest with its own version of things, such as the movie list and a menu.

We then decided that the menu itself was to be an important repeatable component and deserved its own definition along with some menu items. We used this new menu definition in the inheritance layout tile to show the menu in all circumstances. Now, with this latest change, the menu definition is passed as a parameter in the mother inheritance definition, which is therefore inherited by the son definition.

Tiles and Struts

Tiles has now become an integral part of the Struts framework described in Chapter 19. This section assumes you have read and understood the material presented in that chapter. If you haven't, it would be wise to do so now; you wouldn't want to miss out on this very powerful duo.

Configuring Tiles with Struts

In the first part of this chapter, we initialized Tiles in our application by registering the Tiles servlet. This initializes the definitions file, making the definitions available to the application. Within a Struts application, Tiles is configured slightly differently. The following steps will guide you through the addition of Tiles to an existing Struts application:

  1. Ensure that the following commons JAR files are within the /WEB-INF/lib directory:

    • Commons Digester: commons-digester.jar

    • Commons BeanUtils: commons-beanutils.jar

    • Commons Collections: commons-collections.jar

    • Commons Logging: commons-logging.jar

  2. Add the following plug-in entry to the struts-config.xml file:

    ...
    <plug-in className="org.apache.struts.tiles.TilesPlugin" >
      <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml" />
      <set-property property="definitions-debug" value="2" />
      <set-property property="definitions-parser-details" value="2" />
      <set-property property="definitions-parser-validate" value="true" />
    </plug-in>
    ...

    Note that the set-property elements within the plug-in set the same parameters used earlier to initialize the Tiles factory in the TilesServlet.

  3. Create a tiles-defs.xml file within the /WEB-INF/lib directory along the same lines as before. There are no changes to the format of this file.

  4. Ensure that the /WEB-INF directory contains the struts-tiles.tld file.

The Struts application should now be able to use Tiles as it has been used throughout this chapter.

Tiles definitions as action forwards

An action forward defined in the struts-config.xml file is typically given a name and a path that points to a JSP file or another action, as shown in the following example:

...
<action-mappings>
  <action path="/mypage"
          type="com.wrox.begjsp.ch20.struts.SomeAction"
          scope="request">
    <forward name="success" path="/somefile.jsp"/>
  </action>
</action-mappings>
...

The execute method of the action class then uses the name success to pass processing to the JSP file and, ultimately, to be displayed as a Web page. This is very powerful, as a layer of abstraction has been placed between the Controller portion of the application, the action, and the view portion, the JSP file.

When integrated with Tiles, a definition or tile name can be used as the value of the path attribute in the forward element. The preceding action mapping would appear as follows:

...
<action-mappings>
  <action path="/mypage"
          type="com.wrox.begjsp.ch20.struts.SomeAction"
          scope="request">
    <forward name="success" path="homepage"/>
  </action>
</action-mappings>
...

In the preceding code, homepage represents the name of a definition initialized by the Tiles factory (i.e., those definitions defined within the tiles configuration XML file, tiles-defs.xml).The following explanation will walk you through creating a very simple Struts action to display the inheritance example explained earlier.

The following listing shows a simple Struts action, much like the examples you have seen in the previous chapter. This action (HomePageAction) returns an ActionForward object, which has been given the name success:

package com.wrox.begjsp.ch20.struts;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class HomePageAction extends Action
{
public ActionForward execute(ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
        throws Exception
    {
        return mapping.findForward("success");
    }
}

Your first reaction might be, "Well, that's no different from all the other Struts examples!" I know, isn't it great! The action itself just knows it has to forward processing on to something called success; it is up to the view portion of the application, as well the application configuration, to determine exactly what that means.

The action mapping to invoke this action is configured in the struts-config.xml file with the following:

...
<action-mappings>
    <action path="/homepage"
            type="com.wrox.begjsp.ch20.struts.HomePageAction"
            validate="true"
            scope="request">
        <forward name="success" path="inh.mother.layout"/>
    </action>
</action-mappings>
...

Instead of a path to a JSP file, the path attribute of the forward element now refers to the definition described earlier (with my Mom's favorite movies). Now browsing to /homepage.do will present the same screen shown in Figure 20-6.

Passing values from Struts to Tiles

When an application uses Struts, typically the bulk of the data being used for content will come from a database or some other data source to which only the Model and Controller layers have access. While setting parameters inside configuration files is excellent for minor settings, they hardly suit the broad content demands of a sophisticated Web site. In Chapter 19, you saw examples of data being passed from the Controller layer to the View layer and being presented accordingly.

With Tiles, certain components (tiles, layouts, and so on) may be expecting elements of data, no matter what the source, to be in their tile scope. The following Try It Out exercise illustrates how data can be provided from a Struts action and used within the tiles of an application. To demonstrate the flexibility of tiles, the menu definition from a previous example will be used to display menu items provided by the action, rather than the definition, without changing any code.

How It Works

As you can see, the menu data has been provided by two methods that construct a list of SimpleMenuItem objects (these were discussed earlier). The movies are constructed by the getMovies() method. The result of these methods is added to the request using the request.setAttribute method and given the appropriate names that are expected in the foobar-layout.jsp file.

The result of this is to display a page not unlike others in this chapter, except this time the data has been served by a Struts action and we have managed to reuse the characteristics of the menu definition that was developed earlier. If you browsed to the action (/foobar.do), you would see a screen similar to the one shown in Figure 20-7.

FooBar example in a browser

Figure 20.7. FooBar example in a browser

You can see how relatively easy it is to replace the data provided by one source with another. In a (much) more sophisticated application, the movie and menu data might come from a database; even so, the view components would be unaffected.

Summary

Although the full set of features and capabilities of the Tiles framework, as well as its integration with Struts, is beyond the scope of this chapter, the chapter has provided you with enough information to realize their power both separately and together and to use them in some capacity in your next Web application.

Some of the important features of Tiles that are not explained here include its capability to develop powerful portals, channels, and tabs, and the capability to integrate the insert tag with Struts controller classes, among others. Internationalization is also notably missing, but is featured in one of the exercises for this chapter.

Despite these omissions, this chapter has covered a lot of ground, including the following:

  • A definition of Tiles

  • Installing Tiles for use in a Web application

  • Tiles definitions and tags

  • Advanced Tiles usage with parameters and inheritance

  • Using Tiles with Struts

The chapter began by introducing the need for a framework like Tiles. You learned the basic way in which Tiles is used and how its major concepts coordinate to add a level of abstraction between what is presented and how it is presented.

After installation of Tiles was covered, a simple example was introduced. This then led to configurations either using a JSP include file or via definitions in an XML configuration file. The intricacies of definitions were then explored, including inheritance and various ways in which parameters can be passed—as tiles, data, or even definitions themselves—to other components. The chapter then finished with two examples of Tiles integration with the Struts framework.

Exercises

  1. Create a simple definition inheritance example using three definitions. Ensure that at least one item is overridden by the child definitions.

  2. Using the Struts and Tiles resources on the Internet to help you, develop a one-page application using Struts that will display the result of a different definition based on different locale settings specified by the user.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.68.182