|
|
|
|
Programming Jakarta StrutsBy Chuck CavanessNovember 2002 0-596-00328-5, Order Number 3285 464 pages, $39.95 US $61.95 CA |
Chapter 14
Using TilesUp to this point, not much has been said about how to organize and assemble the content and layout of JSP pages for a Struts application. In many ways, that is outside the scope of the topic of Struts. Many excellent books are available that provide strategies for organizing web content and the layout of pages.
In the Storefront application, we have used two different approaches to assembling web pages. The first approach, sometimes referred to as a straight JSP-based approach, is probably the most familiar to web designers. The JSP pages contain presentation logic along with HTML layout tags; there's no separation of the two. This approach is typically used for smaller, less complicated web applications.
The second approach uses the JSP
includedirective. It's used by developers for larger web applications, or after they realize how repetitive the first approach can be. If you have spent much time maintaining web applications, you know how frustrating it can be to update a site's look and feel. Using the JSPincludedirective allows for more reuse, which reduces the total development and maintenance costs.A third approach, which is introduced in this chapter, describes a far better way to reduce the amount of redundant code a web application contains and, at the same time, allows you to separate the content from the layout better than the first two approaches.
Understanding Templates
Traditional GUI toolkits such as VisualWorks Smalltalk or Java Swing all provide some type of layout manager that dictates how content should be displayed within the frame or window. With typical web sites, the layout can undergo many changes, both small and large, over its lifetime. Using layouts and layout managers can help to encapsulate the physical areas of the pages within an application so that they can be altered with minimal impact to the rest of the application. Unfortunately, the JSP technology does not natively provide any direct support for layouts or layout managers. This is why the template-based approach was invented. The concept of templates is not a new one--it has been around for many years, in one form or another.
To understand how templates can actually simplify a web site's layout, let's compare it with an approach that uses the JSP
includemechanism. The current index.jsp page of the Storefront application is shown in Example 14-1.Example 14-1: The index.jsp page for the Storefront application <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %><%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %><%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %><html:html><head><title><bean:message key="global.title"/></title><html:base/><script language=javascript src="include/scripts.js"></script><link rel="stylesheet" href="stylesheets/format_win_nav_main.css" type="text/css"></head><body topmargin="0" leftmargin="0" bgcolor="#FFFFFF"><!-- Header information --><%@ include file="include/head.inc"%><!-- Menu bar --><%@ include file="include/menubar.inc"%><!--- Include the special offer --><%@ include file="include/mainoffer.inc"%><!-- Featured items header row --><table width="645" cellpadding="0" cellspacing="0" border="0"><tr><td width="21"><html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/></td><td width="534"><html:img page="/images/week_picks.gif" altKey="label.featuredproducts"/></td><td width="1" bgcolor="#9E9EFF"><html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/></td><td width="1" bgcolor="#9E9EFF"><html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/></td><td width="90" bgcolor="#9E9EFF" align="right"><html:img height="1" alt="" page="/images/spacer.gif" width="90" border="0"/></td></tr><tr><td><html:img height="1" alt="" page="/images/spacer.gif" width="21" border="0"/></td><td colspan="4" bgcolor="#9E9EFF"><html:img height="1" alt="" page="/images/spacer.gif" width="1" border="0"/></td></tr></table><html:img height="10" alt="" page="/images/spacer.gif" width="1" border="0"/><br><!--- Include the featured items --><%@ include file="include/featureditems.inc"%><!--- Include the copyright statement --><%@ include file="include/copyright.inc"%></body></html:html>Although the main page uses the JSP
includedirective, the layout is mixed with content in the page. For example, notice that the page specifies explicitly that the head.inc include file comes first, then the menubar.inc file, the mainoffer.inc file, and so on, right down to the copyright.inc include at the bottom of the page. For every page that we want to have this particular layout, we need to add the same statements in the same order. If a customer wants the menu along the left side instead of across the top, every page will have to be changed.The Storefront application uses the JSP
includemechanism rather than a straight JSP approach. Although theincludemechanism is a step in the right direction because it does reduce redundancy (imagine if we included the copyright content in every page!), it's still less efficient than a template-based approach.
Static Versus Dynamic Content
<%@ include file="include/copyright.inc" %><jsp:include page="include/copyright.inc"/>
What Is a Template?
A template is a JSP page that uses a JSP custom tag library to describe the layout of a page. The template acts as a definition for what the pages of an application will look like, without specifying the content. The content is inserted into the template page at runtime. One or more pages may use the same template.
TIP: The purpose of a template is to get a consistent look and feel within an application without having to hardcode it for every page. It makes sense that most of the pages will use the same template; however, it's not uncommon to have a different look and feel for a few pages within an application and therefore to require more than one template.
Example 14-2 illustrates a template for the Storefront application.
Example 14-2: A basic template for the Storefront application <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%><%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%><%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles"%><html:html><head><title><bean:message key="global.title"/></title><html:base/></head><body topmargin="0" leftmargin="0" bgcolor="#FFFFFF"><!-- Header page information --><tiles:insert attribute="header"/><!-- Menu bar --><tiles:insert attribute="menubar"/><!-- Main body information --><tiles:insert attribute="body-content"/><!-- Copyright information --><tiles:insert attribute="copyright"/></body></html:html>Not many new concepts are introduced in the template file in Example 14-2. The first thing that you should notice is that we are using Struts custom tag libraries. The fact that we are using the Tiles tag library as well as the HTML and Bean libraries shouldn't be too shocking; the Tiles tag library is just like any other. We'll talk in detail about the Tiles tag library later in this chapter.
The rest of the page is a mixture of HTML layout tags. You should notice that there's no content included, only
inserttags where content will be inserted at runtime. You should already be familiar with the Struts tags shown here, so we won't say anything about them. Theinserttag performs a role similar to that of the JSPincludedirective. It's basically saying that somewhere there's a variable calledheader, for instance, and that the attribute value of "header" should be passed to theinserttag, and the content that is produced should be inserted right here. The same thing goes for themenubar,body-content, andcopyrightinserts. We'll explain shortly how the "real" content is substituted for these attributes at runtime.Notice that this layout is very similar to the one shown in Example 14-1. The only difference is that instead of explicitly including the
mainofferandfeatureditemincludes, as Example 14-1 does, the template file includes abody-contentsection. This allows us to reuse the template for any page that has this generic format. Once we figure out how to supply the page-specific body content, we can reuse this template over and over again. This one file can then control the layout of multiple pages. If we need to modify the layout of the site, this is the only file we need to change--that's the real power of using a template-based approach.The last piece of the puzzle is how the
header,menubar,body-content, andcopyrightsections are put together to form the rendered output. The important point to remember is that the JSP page shown in Example 14-2 is the template. You still need JSP pages that supply page-specific content used by the template. For example, if we rewrite the index.jsp page from Example 14-1 using the template from Example 14-2, it will look like the one in Example 14-3.Example 14-3: The index.jsp page for the Storefront application using a template <%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %><tiles:insert page="/layouts/storefrontDefaultLayout.jsp" flush="true"><tiles:put name="header" value="/common/header.jsp" /><tiles:put name="menubar" value="/common/menubar.jsp" /><tiles:put name="body-content" value="/index-body.jsp" /><tiles:put name="copyright" value="/common/copyright.jsp" /></tiles:insert>The first thing to notice in Example 14-3 is that the Tiles tag library is included at the top. Every page (or tile) that needs to use the Tiles tag library must include it with this line:
<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>Two tags from the Tiles library are used in Example 14-3:
insertandput. (The complete set of Tiles tags and their associated attributes are discussed later in this chapter.) You already saw theinserttag in Example 14-2, but it's performing a slightly different function in Example 14-3. Two attributes are being supplied to theinserttag:pageandflush. Thepageattribute informs the tag that this JSP page is using a particular template (or layout, in the Tiles world--layouts also are discussed later in this chapter). We are calling the template from Example 14-2 storefrontDefaultLayout.jsp. Theflushattribute informs the controller to flush the page output stream before inserting content into the result page.The
puttag in Example 14-3 answers a question that we asked in the previous section: how does the page-specific content get supplied to the template? As you can see, the attributes for theputtag in this example arenameandvalue. If you compare the values of the differentnameattributes, you'll see that they match up to the ones that the template file in Example 14-2 expects. When the index.jsp page from Example 14-3 is executed, the template file is processed and dynamically passed the header.jsp, menubar.jsp, index-tile.jsp, and copyright.jsp files from theputtags:<tiles:insert page="/layouts/storefrontDefaultLayout.jsp" flush="true"><tiles:put name="header" value="/common/header.jsp" /><tiles:put name="menubar" value="/common/menubar.jsp" /><tiles:put name="body-content" value="/index-body.jsp" /><tiles:put name="copyright" value="/common/copyright.jsp" /></tiles:insert>At runtime, the values of the
puttags are dynamically substituted into the template file and processed. The resulting output is what gets displayed to the client.To wrap up the discussion of templates, here is another page that uses the same template from Example 14-2 but supplies a different
body-content. Example 14-4 shows the itemdetail.jsp page.Example 14-4: The itemdetail.jsp page for the Storefront application <%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %><tiles:insert page="../layouts/storefrontDefaultLayout.jsp" flush="true"><tiles:put name="header" value="../common/header.jsp"/><tiles:put name="menubar" value="../common/menubar.jsp"/><tiles:put name="body-content" value="../catalog/itemdetail-body.jsp"/><tiles:put name="copyright" value="../common/copyright.jsp"/></tiles:insert>The only difference between the index.jsp page in Example 14-3 and the itemdetail.jsp page in Example 14-4 is the content supplied by the
body-contentattribute.TIP: If you are still not convinced of the value of using templates, notice that the index.jsp and itemdetail.jsp pages in Example 14-3 and Example 14-4 do not specify anything about how the content should be laid out. They both reference the storefrontDefaultLayout.jsp file, which has sole responsibility for displaying the content in a prescribed format. If we want to change the layout of the site, we have to modify only the storefrontDefaultLayout.jsp file.
Installing and Configuring Tiles
Before you can use the Tiles framework, you must ensure that it's installed and properly configured within your web container. The Tiles framework is not dependent on any specific container. You will need to obtain the required files and ensure that they are placed into their proper directories within the web application.
Downloading Tiles
The Tiles framework is included with the Struts distribution. It previously was included in the contrib folder, but it is now part of the core distribution. You also can find the latest source and binary distribution, as well as other useful information, at http://www.lifl.fr/~dumoulin/tiles/index.html.
Installing the Required JARs and Misc Files
The first step is to install the required files. For Tiles, the following files must be located in the WEB-INF/lib directory:
- tiles.jar
- commons-digester.jar
- commons-beanutils.jar
- commons-collections.jar
- commons-logging.jar
All of these binaries should already be present if you are using Struts. You will also need to install the Tiles TLD file, tiles.tld, in the WEB-INF directory for the application.
WARNING: Don't add the tiles.jar file to the classpath of your servlet container in an attempt to avoid placing it in the WEB-INF/lib directory of each individual web application. Doing so will cause
ClassNotFoundExceptions to be thrown.You should put the tiles-config.dtd file in the WEB-INF directory, too. This DTD is used to validate Tiles definition files, which we'll discuss later in this chapter.
Adding the Tiles Tag Library
As with any other JSP tag library, you must add the Tiles library to the web application deployment descriptor before you can use it. Add the following
taglibelement to the web.xml file:<taglib><taglib-uri>/WEB-INF/tiles.tld</taglib-uri><taglib-location>/WEB-INF/tiles.tld</taglib-location></taglib>There should already be
taglibelements present if you are using any of the standard Struts tag libraries. Each page that needs to use the Tiles tag library must include the following line at the top:<%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %>Configuring Tiles to Work with Struts
The Tiles framework can be used with or without Struts. Depending on how you use it, there are several options for configuring it for a web application. Because this book is about Struts, we'll focus on how to bind it to a Struts application.
TIP: With earlier versions of the Tiles framework, you had to configure a special
ActionServletcalledActionComponentServletin the web.xml file. It was also necessary to configure a specialRequestProcessorin the Strutscontrollerelement. This is no longer true--a Tiles plug-in is now available that will take care of all the initialization.The Tiles plug-in is really necessary only if you are planning on using Tiles definitions. It is possible to use the Tiles libraries with Struts without configuring the plug-in. However, it doesn't hurt to configure it, and it may save you time later if you decide to use definitions.
To add the Tiles plug-in to a Struts application, add the following
plug-inelement to the Struts configuration 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>Within the
plug-inelement, you can specify one or moreset-propertyelements to pass additional parameters to thePluginclass. Thedefinitions-configinitialization parameter specifies the XML file or files containing Tile definitions. If multiple filenames are used, they must be comma-separated.The
definitions-debugparameter specifies the debug level. The allowed values are:
0
- No debug information is written out.
1
- Partial debug information is provided.
2
- Full debug information is provided.
The default value is
0.The
definitions-parser-detailsparameter indicates the required level of debugging information while the definition files are being parsed. This value is passed to the Commons Digester. The allowed values are the same as those for thedefinitions-debugparameter. The default value is0.The
definitions-parser-validateparameter specifies whether the parser should validate the Tiles configuration file. The allowed values aretrueandfalse. The default istrue.There is an additional parameter, not shown, called
definitions-factory-class. You can create a custom definitions factory and supply the class name here. The default isorg.apache.struts.tiles.xmlDefinition.I18NfactorySet.Overview of Tiles
The Tiles framework provides a templating mechanism that allows you to separate the responsibilities of layout from those of content. As with the templates described earlier in this chapter, you have the ability to establish a layout and dynamically insert the contents of your pages into that layout at runtime. This is a powerful mechanism if you need to customize your site based on such things as internationalization, user preferences, or just the look-and-feel changes that occur in every web application sooner or later. The Tiles framework provides the following features:
- Template capabilities
- Dynamic page construction and loading
- Screen definitions
- Support for tile and layout reuse
- Support for internationalization
- Support for multiple channels
There has been a Template tag library within the Struts framework for quite some time. These tags allow you to use a very basic templating approach to assemble your JSP pages in a web application. Although these tags are helpful in separating the content for a web application from its prescribed layout, the Tiles framework goes much further and actually provides a superset of the Template tag library's behavior, as well as many other features.
TIP: The Tiles framework was previously called Components, but the name was changed because that term is so overused. The Tiles documentation and source code still make reference to the old name in some places.
Cedric Dumoulin created the Tiles framework to extend the concept of templates and provide developers with more flexibility and freedom when creating web applications built with JSP technology. The content for the web applications is still driven by JSP pages and JavaBeans. However, the layout is specified within a separate JSP page or, as we'll see later, in an XML file.
What Is a Tile?
A tile is an area or region within a web page. A page can consist of just one region or be broken up into several regions. Figure 14-1 illustrates an example from the Storefront application.
Figure 14-1. The regions of the Storefront application
![]()
A JSP page is typically made of several regions, or tiles. There's nothing too special about the page, other than the fact that it's designed to be used with the Tiles framework and makes use of the Tiles tag library.
The most important aspect of a tile is that it is reusable. This is true for layouts as well as body content. Unlike most JSP pages, tile components are reused within an application and possibly across different applications. Other than that, there's nothing that complicated about a tile. In fact, most of the examples we've seen so far can be classified as tiles, including Example 14-2 through Example 14-4.
Using a Layout Tile
In the Tiles world, a layout is what we have been referring to as a template. A layout serves the exact same purpose as a template--that is, to assemble a group of tiles to specify the format of a page. Example 14-2 is, in fact, a Tiles layout. The syntax between Tiles and a template library like the one included with Struts is almost identical.
TIP: The Tiles framework provides a superset of the functionality included with the standard Struts template tags defined by David Geary, but it takes the concept of templates even further by providing additional functionality.
Layouts also are considered tiles. JSP pages and even entire applications can reuse layouts, and it's common to build a library of layouts that are used in many different projects. The Tiles framework comes with several prebuilt layout tiles that you can reuse or modify as needed. The included layouts are:
- Classic layout
- Renders a header, left menu, body, and footer
- Columns layout
- Renders a list of tiles in multiple columns, each of which renders its tiles vertically stacked
- Center layout
- Renders a header, left tile, right tile, body, and footer
- Menu layout
- Renders a menu with links
- Tabs layout
- Renders several tiles in a tabs-like fashion
- Vertical box layout
- Renders a list of tiles in a vertical column
Because one of the main goals of Tiles is reusability, you can reuse these layouts within your application with little or no modifications. You also have the freedom to customize the layouts in any way you need.
Planning Your Layout
It's very important that you plan your layout requirements ahead of time. Trying to decide how your site is going to look after it's already built is definitely not the right approach. This decision is typically made by a human factors team, product management, or possibly the web developers themselves. In any case, you need to develop the layout (or layouts) well ahead of any actual development.
The default layout for the Storefront application was presented in Example 14-2. This layout will be used as an example throughout this chapter.
Passing Parameters to the Layout
The layout tile shown in Example 14-2 is generic. It doesn't know anything about the itemdetail.jsp content or any of the other pages, for that matter. This is intentional, as it allows us to reuse this layout for many pages. Instead of being hardcoded within the layout page, the content is supplied or "passed" as parameters to the layout page at runtime. Let's look at the signin.jsp tile for the Storefront application, shown in Example 14-5.
Example 14-5: The signin.jsp tile for the Storefront application <%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %><tiles:insert page="../layouts/storefrontDefaultLayout.jsp" flush="true"><tiles:put name="header" value="../common/header.jsp"/><tiles:put name="menubar" value="../common/menubar.jsp"/><tiles:put name="body-content" value="../security/signin-body.jsp"/><tiles:put name="copyright" value="../common/copyright.jsp"/></tiles:insert>The purpose of the
puttags in Example 14-5 is to supply the layout tile, which is specified in the enclosinginserttag, with content. The values of thenameattributes in Example 14-5 (as in the other tiles shown in Example 14-3 and Example 14-4) must match the ones that the layout tile is expecting.TIP: The
inserttag optionally can include anignoreattribute that will cause the tag to not write out anything when it can't find a value for an expected attribute. By default, a runtime exception is thrown when an attribute has not been supplied.The Tiles Tag Library
This section introduces the JSP custom tags used by the Tiles framework. Table 14-1 lists the tags available to the framework. The tags are very similar to the ones provided in any template-based framework, but Tiles contains more functionality.
The insert Tag
The
inserttag is responsible for inserting content into a page. In a layout tile, theinserttag prescribes where the content will go using attribute values. In a regular, non-layout tile, theinserttag is used to retrieve a layout and allow content to be passed to the layout usingputtags. Table 14-2 lists the attributes for theinserttag.Several examples of the
inserttag were shown earlier in this chapter.The definition Tag
The
definitiontag is used to create a tile (template) definition as a bean. The newly created bean will be saved under the specifiedid, in the requestedscope. Thedefinitiontag has the same syntax as theinserttag. The new definition can extend a definition described in the definition factory (XML file) and may overload any previously defined parameters. Table 14-3 lists the attributes supported by thedefinitiontag.The following fragment illustrates how to use the
definitiontag in a JSP page:<tiles:definitionid="storefront.default"page="/layouts/storefrontDefaultLayout.jsp"scope="request"><tiles:put name="header" value="/common/header.jsp" /><tiles:put name="menubar" value="/common/menubar.jsp" /><tiles:put name="copyright" value="/common/copyright.jsp" /></tiles:definition>A complete example is shown later, in "Declaring Definitions in a JSP Page" of this chapter.
The put Tag
The
puttag is used to pass attributes to a tile component. This tag can be used only inside theinsertordefinitiontags. The value (or content) of theputtag is specified using thevalueattribute or the tag body. It is also possible to specify the type of the value:
string- Content is literally translated.
pageortemplate- Content is included from specified URL. Name is used as URL.
definition- Content comes from specified definition (from factory). Name is used as definition name.
If the
typeattribute is used, it is taken into account by thegetorinserttags inside the inserted tile. If thetypeattribute is not specified, the content is untyped, unless it comes from a typed bean.TIP: Setting
direct="true"is equivalent to settingtype="string".
Table 14-4 lists the attributes for the
puttag.The putList Tag
The
putListtag creates a list that will be passed as an attribute to a tile. The list elements are added using theaddtag. This tag can be used only inside theinsertordefinitiontag. Table 14-5 lists the attribute for theputListtag.
Table 14-5: Attribute for the putList tag Attribute name
Description
nameThe name of the List. This attribute is required.
The add Tag
The
addtag adds an element to the surrounding list. This tag can be used only inside theputListtag. The value can come from a direct assignment (value="aValue") or from a bean. One ofvalueorbeanNamemust be specified. Table 14-6 lists the attributes for theaddtag.The get Tag
The
gettag retrieves content from the tile context and includes it in the page. Table 14-7 lists the attributes for thegettag.The getAsString Tag
The
getAsStringtag retrieves the value of the specified tile attribute property and renders it to the current JspWriter as aString. The usualtoString( )conversion is applied to the value. If the named value is not found, aJSPExceptionwill be thrown. Table 14-8 lists the attributes for thegetAsStringtag.The useAttribute Tag
The
useAttributetag declares a Java variable and an attribute in the specified scope, using the tile's attribute value. The variable and attribute will have the name specified byid, or the original name if not specified. Table 14-9 lists the attributes for theuseAttributetag.The importAttribute Tag
The
importAttributetag imports the attribute from the tile to the requested scope. Thenameandscopeattributes are optional. If not specified, all tile attributes are imported in page scope. Once imported, an attribute can be used like any other bean from the JSP context. Table 14-10 lists the attributes for theimportAttributetag.The initComponentDefinitions Tag
The
initComponentDefinitionstag initializes the definitions factory. Table 14-11 lists the attributes for the tag.Using Definitions
The tiles shown so far add value to an application because they organize the layout of a page in a single resource, the layout JSP page. This can save development time and, more importantly, the time it takes to change the layout for an application. However, there is a problem with the approach used in the Storefront application shown earlier. In each of the non-layout tiles, there is redundant code that specifies what content to use for the
header,menubar, andcopyrightcontent--the same attributes are being passed in every page. This may not always be the case, but in general, these values will be constant throughout an application. For instance, the same copyright is typically shown on every page.It's redundant to have to specify these in every tile. Ideally, we could declare these attributes in one place, and the tiles could include just the page-specific attributes where needed. Tiles definitions provide just such functionality. A definition allows you to statically specify the attributes that are used by a template, which in turn allows you to specify only the page-specific attributes in your tiles. Definitions enable you to:
- Screen definitions
- Centralize declaration of page description
- Avoid repetitive declaration of nearly identical pages (by using definition inheritance)
- Avoid creation of intermediate components used to pass parameters
- Specify the name of a definition as a
forwardin the Struts configuration file
- Specify the name of a definition as component parameters
- Overload definition attributes
- Use a different copy of a component, depending on the locale (I18N)
- Use a different copy of a component, depending on a key (this might be used to show different layouts depending on the client type)
Definitions can be declared in a JSP page or an XML file. Either way, you should strive to keep the definitions in a central place. If you are planning on using a JSP page to specify your definitions, put all the definitions for your application in a single page. Don't spread your definitions throughout your site, as that will only make maintenance more difficult.
Declaring Definitions in a JSP Page
As was previously mentioned, there are two locations in which you can specify definitions: a JSP page or an XML file. We'll discuss the JSP page approach first.
To use the JSP approach, create a JSP page and declare all of your definitions in that file. For the Storefront application, we've created a file called storefront-defs.jsp and put the default definition in it, as Example 14-6 shows.
Example 14-6: Declaring tiles definitions in a JSP page <%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %><tiles:definitionid="storefront.default"page="/layouts/storefrontDefaultLayout.jsp"scope="request"><tiles:put name="header" value="/common/header.jsp" /><tiles:put name="menubar" value="/common/menubar.jsp" /><tiles:put name="copyright" value="/common/copyright.jsp" /></tiles:definition>The definition in Example 14-6 uses the same layout tile used earlier. The common files that were spread through the various tiles are now located in the definition file. This makes changing their values much easier. For instance, if we wanted to specify a different copyright page, the only place to change it would be in the definition file; we would not have to modify every JSP page.
The
definitiontag syntax looks very similar to the syntax for theinserttags shown earlier. We just need to provide anidattribute and switch thepathattribute to thepageattribute. Also, the default scope for thedefinitiontag ispage. It was set to request scope here to give it a little broader scope.To take advantage of the definition, the tile components need to be able to access it. Because we have given the definition request scope and it will exist only for the lifetime of a request, we need a way to include the definition in the various JSP pages. Fortunately, we already know how to include a JSP page in another page using the JSP
includedirective. Example 14-7 shows what the signin.jsp page looks like using the JSP definition file.Example 14-7: The signin.jsp page using a tile definition <%@ taglib uri="/WEB-INF/tiles.tld" prefix="tiles" %><%@include file="../common/storefront-defs.jsp" %><tiles:insert beanName="storefront.default" beanScope="request"><tiles:put name="body-content" value="../security/signin-body.jsp"/></tiles:insert>With this approach, the tile components have to insert only the page-specific content. Compare Example 14-7 to Example 14-5. Notice that the signin.jsp file using the definition needs to provide only the page-specific content, the sign-body.jsp file.
Declaring Definitions in a Configuration File
You also have the option of declaring definitions in a centralized XML file. Whether you use the JSP or the XML alternative really depends on your requirements. With the XML approach, you won't need to use the
includedirective shown earlier.Creating a definition configuration file
To use the XML approach, create an XML file that follows the syntax of the tiles-config.dtd file. The definition XML file should be placed in the WEB-INF directory, as with the other application meta-information. The DTD should also be placed in the WEB-INF directory. Example 14-8 shows an example of a definition XML file.
Example 14-8: The Storefront definition XML file <!DOCTYPE tiles-definitions PUBLIC"-//Apache Software Foundation//DTD Tiles Configuration//EN""http://jakarta.apache.org/struts/dtds/tiles-config.dtd"><tiles-definitions><definition name="storefront.default" path="/layouts/storefrontDefaultLayout.jsp"><put name="header" value="/common/header.jsp" /><put name="menubar" value="/common/menubar.jsp" /><put name="copyright" value="/common/copyright.jsp" /></definition></tiles-definitions>There's not much difference between the definition format specified in the JSP file in Example 14-6 and the XML file in Example 14-8. The XML file uses a slightly different syntax, but it's still very similar.
WARNING: The two formats are just similar enough to cause problems. Notice that in the JSP definition, you use the
puttag:<tiles:put name="body-content" value="../security/signin-body.jsp"/>
but in the XML definition, you use the
putelement:<put name="header" value="/common/header.jsp" />Make sure that you don't get these two confused, as this can be a difficult bug to track down.
Each definition should have a unique name. JSP tags and pages use the name to retrieve the definition. It can't be used as a URL, however; it's only a logical name for the definition.
Extending Tile Definitions
One of the most powerful features of tile definitions is the ability to create new definitions by extending existing ones. All attributes and properties of the parent definition are inherited, and you can override any attribute or property. To extend a definition, add the
extendsattribute. Example 14-9 shows an example of a definition namedstorefront.customextending thestorefront.defaultdefinition.Example 14-9: Definitions can extend other definitions <tiles-definitions><definition name="storefront.default" path="/layouts/storefrontDefaultLayout.jsp"><put name="header" value="/common/header.jsp" /><put name="menubar" value="/common/menubar.jsp" /><put name="copyright" value="/common/copyright.jsp" /></definition></tiles-definitions><tiles-definitions><definition name="storefront.custom" extends="storefront.default"><put name="copyright" value="/common/new-copyright.jsp" /></definition></tiles-definitions>In Example 14-9, all of the attributes in the
storefront.defaultdefinition are inherited. However, thestorefront.customerdefinition overrides the value for thecopyrightattribute with an alternate copyright page. This is a very powerful feature. If you have multiple child definitions all extending a root definition, changing a value in the root definition changes it for all the children. Thus, you can change the layout in the root definition and have it changed for all child definitions.Using Definitions as Forwards in Struts
Tiles definitions can also be used as Struts forwards, instead of actual URLs. To use definitions in this manner, you first create the definitions:
<tiles-definitions><definition name="storefront.default" path="/layouts/storefrontDefaultLayout.jsp"><put name="header" value="/common/header.jsp" /><put name="menubar" value="/common/menubar.jsp" /><put name="copyright" value="/common/copyright.jsp" /></definition><definition name="storefront.superuser.main" extends="storefront.default"><put name="header" value="/common/super_header.jsp" /><put name="menubar" value="/common/super_menubar.jsp" /><put name="copyright" value="/common/copyright.jsp" /></definition></tiles-definitions>This fragment shows two definitions, the standard default definition and a second one that defines the layout for a "superuser." A superuser might be someone that frequents the site and places many orders, and such a user might be given more options on the menu bar to facilitate faster ordering.
In the Struts configuration file, we need to define the forwards that use these definitions:
<global-forwards><forward name="Super_Success" path="storefront.superuser.main" /></global-forwards>You can then use the
Super_Successforward to send the user to thestorefront.superuser.maindefinition just as you would for any other forward.Internationalization Support with Tiles
Although the Struts framework provides certain I18N capabilities that can be used with Tiles, Tiles also provides the ability to select a particular tile based on the user's locale. To support this feature in your application, you need to create a different Tiles definition file for each locale that you need to support. For example, if you need to support a set of definitions for the U.S. locale and a separate set for the German locale, you must create two separate definition files:
- tiles-tutorial-defs_en.xml
- tiles-tutorial-defs_de.xml
The suffix naming conventions follow the ones set by the
java.util.ResourceBundle, which is also used by the resource bundles for Struts. When a request for a definition is made, the correct definition is determined by the included locale.TIP: As with regular Java resource bundles, you should always provide a base definition that is used when no locale is provided or when an unsupported locale is used. No language or country suffix is appended to the name of the Tiles base definition file.
Once you have created the locale-specific definition files and placed them in the WEB-INF directory, the only other necessary step is to ensure that a
Localeis stored in the user'sHttpSession. The Tiles framework depends on the sameLocaleinstance that Struts uses to determine which definition file to use. You will need to ensure that the Struts framework is storing the user'sLocalein the session. This is accomplished by setting thelocaleattribute totruein thecontrollerelement. See for more information on thecontrollerelement.That's all there is to it. Note that you should still rely on the Struts resource bundle for locale-sensitive resources such as text, messages, and images. The I18N support in Tiles should be used for differences in layout based on the locale. Struts and Tiles work very well together to provide complete I18N support.
Back to: Programming Jakarta Struts
© 2001, O'Reilly & Associates, Inc.
webmaster@oreilly.com