Tiles & Page Layouts

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Apache Struts 7.4.0 (released 2026-10-02) with its struts2-tiles-plugin 7.4.0, running on Apache Tomcat 11.0.26 and Java 21. The plugin documentation, the plugin’s own struts-plugin.xml and the annotation and tag descriptors inside the plugin jar were checked on 2026-10-08, and every example was built into a WAR, deployed on Tomcat 11 and requested; the output shown is the output received. Almost every web application needs a page frame: a header, a menu, a footer and a slot for the content of the page being shown. Struts 1 shipped a solution for it, Tiles, and Struts 2 kept it as a plugin. The catch, and the reason this page exists, is that Apache Tiles itself was retired in 2018 and moved to the Attic in 2019, so a new Struts 7 application has to decide whether to use a retired framework, which Struts still bundles and still maintains inside its own plugin, or one of the alternatives. The running scenario is the Bookshelf, a catalogue of books; this page gives it a layout with Tiles, then builds the same frame with a JSP tag file. The tag-file technique is explained in full on the Tag Files & Layouts page; the tags used in the layouts (s:iterator, s:url, s:property) are the Struts tags of the page Tags Overview & Generic Tags.

Should a New Application Use Tiles?

Tiles is a composite-view framework. A page is not written once as a whole but assembled: a definition in an XML file names a layout template and gives values for its slots, and an action result renders that definition. The values can be paths of JSP fragments, plain strings, lists or other definitions. The same idea is called a layout or a template in other frameworks, and the tag-file page already has a comparison table of the ways to build a skeleton in JSP; this table is its Struts counterpart.

Table 1. Choosing a page-layout technique in a Struts 7.4 application
Technique Where the layout is declared Fit and cost

Tiles plugin (tiles result)

tiles.xml definitions, and the action only names a definition

Existing applications that already use it; definition inheritance and wildcards are convenient. Apache Tiles is retired (Attic, 2019), the plugin bundles its code, and one evaluator (OGNL:) is switched off in 7.4.0

JSP tag-file layout

A layout.tag file in WEB-INF/tags, and each page wraps its content in the tag

No dependency, no listener, no extra configuration; plain JSP and the Struts tags work inside it. The page owns its own content and calls the layout

insertDefinition or insertTemplate in a JSP

A Tiles tag inside an ordinary page

A way to use Tiles components in a page that was not rendered by the tiles result

SiteMesh 3 used directly

A servlet filter decorates every response with a decorator page

Layout is applied to finished HTML, so pages know nothing about it; the Struts SiteMesh plugin is not published for 7.4.0 (see the retired plugins on the page Other Plugins), so the filter is configured in the application itself

FreeMarker macros and includes

#include and #macro in .ftl templates

Natural when the views are FreeMarker; described on the page FreeMarker & Velocity Views

For a new application on JSP the plain answer is the tag-file layout: one file, no framework, and the Jakarta Pages specification defines it. For an application that already has tiles.xml files and a tiles result, nothing forces a migration. Struts 7.4.0 ships the plugin and keeps fixing it, as the next sections show, but the project’s own documentation of the 2.5 line says the older plugin generation was dropped, and the plugin’s dependencies (the Tiles code is bundled) will not receive upstream fixes.

How the Plugin Renders a Page

The plugin has four moving parts. A servlet context listener, org.apache.struts2.tiles.StrutsTilesListener, builds a Tiles container when the web application starts and reads every tiles*.xml file it finds. The package tiles-default, which extends struts-default, registers the result type tiles (org.apache.struts2.views.tiles.TilesResult). An action result of that type names a definition. And the JSP that the definition names as its template uses the Tiles tags to insert the attributes of the definition into its slots.

flowchart TD R[Request /books] --> F[StrutsPrepareAndExecuteFilter] F --> A[Action BookAction] A -->|result code success| T[TilesResult type tiles] T --> C[Tiles container built by StrutsTilesListener] C --> D[Definition bookshelf.books resolved in tiles.xml] D -->|extends| B[baseLayout merged attributes] B --> L[template layout.jsp] L --> H[insertAttribute header] L --> M[insertAttribute body] L --> FO[insertAttribute footer] D -. S2 expression .-> V[ValueStack of the request]

The diagram shows the two facts that matter in practice. The container is application-wide and built once, so a definition is shared by every request. But the template JSP runs inside the normal request, so the Struts tags work in it, and the S2: evaluator reads the value stack of that request.

The Bookshelf Used on This Page

All examples below are one WAR, g10tiles.war, deployed under the context /g10tiles on Tomcat 11.0.26. It is the Struts Bookshelf of the page Struts Getting Started with these changes: two dependencies were added, the plugin struts2-tiles-plugin (version managed by the Struts BOM) and commons-beanutils 1.11.0, the web descriptor declares the listener, the actions live in a package that extends tiles-default, and the views are Tiles templates under WEB-INF/tiles. The page BookAction offers a list of titles and a page title:

public class BookAction extends ActionSupport {
    public String getPageTitle() {
        return "Bookshelf - catalogue";
    }

    public List<String> getTitles() {
        return List.of("Dune", "Neuromancer");
    }
}

Installation and Configuration

Three things are needed, and the plugin documentation lists them in this order: the dependency, the listener and the result type.

The dependency, and the one the plugin forgot

The artifact is org.apache.struts:struts2-tiles-plugin; with the struts2-bom imported its version is the Struts version. The jar contains the Tiles classes (org.apache.tiles.api, org.apache.tiles.core, org.apache.tiles.request and the JSP tag handlers) next to the Struts integration classes, so no separate Tiles dependency exists; this is what "bundled" means in the release notes of Struts 6.3.0. The plugin jar’s struts-plugin.xml contributes the package tiles-default and the constant that disables the OGNL: evaluator, and the META-INF/tld/tiles-jsp.tld descriptor with the URI http://tiles.apache.org/tags-tiles.

<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-tiles-plugin</artifactId>
</dependency>
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.11.0</version>
</dependency>

The second dependency is a workaround. Without it the listener fails at start-up with NoClassDefFoundError: org/apache/commons/logging/LogFactory, because the plugin’s commons-digester3 pulls in a very old commons-beanutils that does not bring Commons Logging along; commons-beanutils 1.11.0 does (the failure and the fix are described on the page Results & Result Types).

The listener

The listener is declared in web.xml. The plugin documentation says the "standard" listener or the Struts replacement can be used and recommends the latter for tighter integration with Struts features such as FreeMarker. In the 7.4.0 jar only the Struts listener exists (the class org.apache.tiles.listener.TilesListener mentioned in the same paragraph is not in the jar; Tiles 3 has only the abstract AbstractTilesListener), so use the Struts one:

<listener>
    <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class>
</listener>

Without it a tiles result fails on the first request with a NullPointerException whose message says Cannot invoke "org.apache.tiles.request.ApplicationContext.getApplicationScope()" because "context" is null: there is no container to render the definition (verified by removing the listener from the Bookshelf).

The package and the result

A package gets the tiles result type by extending tiles-default (instead of struts-default, which tiles-default itself extends), or by registering the type in its own result-types element. The result body is the name of a definition:

<package name="default" extends="tiles-default" namespace="/">
    <action name="books" class="org.example.bookshelf.BookAction">
        <result type="tiles">bookshelf.books</result>
    </action>
</package>

The result is a subclass of ServletDispatcherResult, so the usual result features apply: parameters, a ${...} location evaluated against the value stack, and an action that returns different result codes can map each to its own definition. The other result types and the order in which a package contributes them are on the results page.

Definitions in tiles.xml

A definition has a name, usually a template, and a list of attributes. The plugin loads every file matching tiles*.xml from the classpath or from WEB-INF since Struts 2.3.28, so no DEFINITIONS_CONFIG parameter is needed; that parameter is still the way to name the files explicitly in a restricted environment, and a WAR that uses Tomcat’s ## version mechanism must list its definition files explicitly, because of a limitation of the URL class.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
        "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
        "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
    <definition name="baseLayout" template="/WEB-INF/tiles/layout.jsp">
        <put-attribute name="title" value="Bookshelf"/>
        <put-attribute name="header" value="/WEB-INF/tiles/header.jsp"/>
        <put-attribute name="footer" value="/WEB-INF/tiles/footer.jsp"/>
        <put-list-attribute name="menu">
            <add-attribute value="books"/>
            <add-attribute value="about"/>
        </put-list-attribute>
        <put-attribute name="body" value="/WEB-INF/tiles/empty.jsp"/>
    </definition>

    <definition name="bookshelf.books" extends="baseLayout">
        <put-attribute name="title" expression="S2:pageTitle"/>
        <put-attribute name="body" value="/WEB-INF/tiles/books-body.jsp"/>
    </definition>
</tiles-definitions>

The vocabulary, in the order the file uses it:

Table 2. Elements of a Tiles definition
Element or attribute Meaning

definition name

The name an action result (or a JSP tag) uses

template

The JSP (or FreeMarker, or Velocity template) that lays the page out; a definition that has no template of its own inherits it from the definition it extends

extends

Inherits every attribute and the template of another definition and lets this one add or override attributes; chains of any depth are allowed

put-attribute name value

Sets a named slot. value is a path, a string or a definition name, depending on type

put-attribute ... expression

The value is an expression with an evaluator prefix (S2:, I18N:, EL:, OGNL:), see below

put-attribute ... type

string (output as text), template (render as a template), definition (render another definition); without it Tiles guesses from the value

put-list-attribute with add-attribute

A list slot; a child definition can add items to the inherited list with its own put-list-attribute inherit="true" (verified: the menu then showed books, about and inherited)

preparer

A class run before the definition is rendered (the plugin supplies a Struts-aware preparer factory, StrutsPreparerFactory)

The type attribute was exercised in the Bookshelf by a definition that sets the header to a plain string, the footer to another definition and the body to a template; the page rendered Plain text header as bare text, the footer definition as its JSP and the body as the usual list:

<definition name="bookshelf.footerNote" template="/WEB-INF/tiles/footer.jsp"/>

<definition name="bookshelf.typed" extends="baseLayout">
    <put-attribute name="title" value="Typed attributes" type="string"/>
    <put-attribute name="header" value="Plain text header" type="string"/>
    <put-attribute name="footer" value="bookshelf.footerNote" type="definition"/>
    <put-attribute name="body" value="/WEB-INF/tiles/books-body.jsp" type="template"/>
</definition>

The Layout Template

The template named by baseLayout is an ordinary JSP that uses the Tiles tag library and, where it is useful, the Struts tags. tiles:insertAttribute writes the value of a named attribute, rendering it according to its type; tiles:getAsString writes it as text; tiles:importAttribute copies one or all of the attributes into a scope so that other tags and EL can read them (a list attribute imported this way is a list the Struts iterator can walk).

<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<tiles:importAttribute name="title" scope="request"/>
<!DOCTYPE html>
<html>
<head><title><tiles:getAsString name="title"/></title></head>
<body>
<tiles:insertAttribute name="header"/>
<nav>
<tiles:importAttribute name="menu"/>
<s:iterator value="#attr.menu"><a href="<s:url action='%{top}'/>"><s:property/></a> </s:iterator>
</nav>
<main><tiles:insertAttribute name="body"/></main>
<tiles:insertAttribute name="footer"/>
</body>
</html>

Requesting /g10tiles/books produced the assembled page (blank lines trimmed):

$ curl -s http://localhost:18080/g10tiles/books
<!DOCTYPE html>
<html>
<head><title>Bookshelf - catalogue</title></head>
<body>
<header><h1>Bookshelf (Struts + Tiles)</h1></header>
<nav>
<a href="/g10tiles/books.action;jsessionid=...">books</a> <a href="/g10tiles/about.action;jsessionid=...">about</a>
</nav>
<main>
<ul>
<li>Dune</li><li>Neuromancer</li>
</ul>
</main>
<footer><small>Bookshelf demo</small></footer>
</body>
</html>

The title came from the action (S2:pageTitle), the header and footer from baseLayout, the body from the child definition and the menu from the list attribute. The s:url output carries a ;jsessionid only on the first request of a session, as always with URL rewriting.

The Tiles tag library has eleven tags, listed here from the plugin’s TLD; the tutorial pages of Tiles document each of them.

Table 3. The tags of tiles-jsp.tld in the 7.4.0 plugin
Tag Use

insertAttribute

Renders a named attribute of the current definition at this place; ignore="true" renders nothing instead of failing when it is missing

getAsString

Writes an attribute as text, for a title or a CSS class

importAttribute

Imports one attribute, or all of them when no name is given, into a scope (page, request, session, application)

insertDefinition

Renders a named definition from inside a page, with optional overrides given by nested putAttribute tags

insertTemplate

Renders a template file with attributes given inline, with no definition in tiles.xml

putAttribute, putListAttribute, addAttribute, addListAttribute

The attributes of an insertDefinition or insertTemplate call (and of a definition tag)

definition

Defines a definition in a page, stored in a scope, to be inserted by name later

setCurrentContainer

Chooses a named Tiles container for the tags that follow

Tiles Tags in an Ordinary Page

The tiles result is optional. Any JSP, whether it was reached by a dispatcher result or by a forward, can insert a definition, and can override attributes while doing so. This is the way to use Tiles components in an application that renders its pages some other way:

<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles" %>
<tiles:insertDefinition name="bookshelf.books">
    <tiles:putAttribute name="title" value="Overridden in the JSP"/>
    <tiles:putListAttribute name="menu">
        <tiles:addAttribute value="books"/>
    </tiles:putListAttribute>
</tiles:insertDefinition>

<tiles:insertTemplate template="/WEB-INF/tiles/layout.jsp">
    <tiles:putAttribute name="title" value="Ad hoc"/>
    <tiles:putAttribute name="header" value="/WEB-INF/tiles/header.jsp"/>
    <tiles:putAttribute name="footer" value="/WEB-INF/tiles/footer.jsp"/>
    <tiles:putListAttribute name="menu"><tiles:addAttribute value="about"/></tiles:putListAttribute>
    <tiles:putAttribute name="body" value="/WEB-INF/tiles/about-body.jsp"/>
</tiles:insertTemplate>

The first call rendered the full Bookshelf page with the title Overridden in the JSP and a menu that had lost its about entry (the override replaced the inherited list, it did not add to it); the second rendered the layout with the title Ad hoc and a body that is not part of any definition. Both ran inside the action’s request, so the Struts tags in the layout saw the action as before.

Wildcard Definitions

A definition name can contain wildcards, so one definition serves many result names. In the plugin the name must carry the prefix WILDCARD:; the Tiles tutorial says the prefix is not needed when wildcards are the only pattern syntax in use, and that is not what the Struts container did. The container the plugin builds registers two pattern syntaxes, WILDCARD and REGEXP, and a name with no prefix is treated as a literal (verified: a definition named page-* was never matched). The matched parts are available as {1}, {2} in the attribute values:

<definition name="WILDCARD:bookshelf.*" extends="baseLayout">
    <put-attribute name="title" value="Bookshelf - {1}"/>
    <put-attribute name="body" value="/WEB-INF/tiles/{1}-body.jsp"/>
</definition>

With this definition the result <result type="tiles">bookshelf.about</result> rendered Bookshelf - about and the body /WEB-INF/tiles/about-body.jsp, with no bookshelf.about entry anywhere. The explicit definitions still win: a name that exists literally (bookshelf.books) is found first, and the wildcard is the fallback for the rest. A wildcard that is too greedy hides mistakes; the result bookshelf.nope did not report a missing definition, it matched and failed later with a Jasper error about the missing /WEB-INF/tiles/nope-body.jsp at line 14 of layout.jsp.

Expressions: S2, I18N, EL and the Disabled OGNL

An attribute can take its value from an expression instead of a literal. The prefix of the expression selects the evaluator. The plugin registers four of them.

Table 4. Expression evaluators of the Struts Tiles container in 7.4.0
Prefix Evaluates against State in 7.4.0

S2:

The Struts value stack of the current request, with the full OGNL controls (member access policy, allowlist, expression guard); available since Struts 2.5.3

Use this one

I18N:

Treats the text after the prefix as a key of the Struts resource bundles and returns the localised text

Works; saves one tiles_de.xml per language

EL:

The Jakarta Expression Language; EL:Sum ${1+2} gave the title Sum 3 on Tomcat 11

Works where the container provides EL (the plugin declares the EL API as provided)

OGNL:

The Tiles request, with Tiles' own OGNL, outside the Struts controls

Disabled by default; throws EvaluationException

S2: the value stack

S2:pageTitle in the bookshelf.books definition above is looked up on the value stack, where the action is the top object, and called getPageTitle(); that is where the title of the page in the output above came from. Anything an s:property could read can be read here, so a definition can take its title, a CSS class or a template name from the action. The details of the stack are on the ValueStack, ActionContext & OGNL page.

I18N: the resource bundles

I18N:page.title is the key page.title looked up in the resource bundles of the action, which is how localisation finds a message. In the Bookshelf a file org/example/bookshelf/BookAction.properties with the line page.title=Bookshelf (from bundle) made the title Bookshelf (from bundle); before the file existed the page showed the bare key page.title in the title, with no error, the usual behaviour of the Struts message lookup.

<definition name="bookshelf.i18n" extends="baseLayout">
    <put-attribute name="title" expression="I18N:page.title"/>
    <put-attribute name="body" value="/WEB-INF/tiles/books-body.jsp"/>
</definition>

OGNL: disabled in 7.4.0

The prefix OGNL: comes from Tiles itself. It evaluates against the Tiles request, not the value stack, and it bypasses the Struts OGNL protections that every other expression in Struts passes through. Struts 7.4.0 therefore disables it. A definition that still uses it does not render wrongly; it fails, naming the cause:

<definition name="bookshelf.legacy" extends="baseLayout">
    <put-attribute name="title" expression="OGNL:pageTitle"/>
    <put-attribute name="body" value="/WEB-INF/tiles/books-body.jsp"/>
</definition>
Caused by: org.apache.tiles.core.evaluator.EvaluationException: The Tiles OGNL evaluator is disabled.
Migrate the expression to S2:, or temporarily enable struts.tiles.ognl.legacy.enabled;
legacy Tiles OGNL support is deprecated for removal.

The page request ended in a 500 with the message ServletException including path '/WEB-INF/tiles/layout.jsp'; the disabling is visible as the root cause. The fix is to change the prefix to S2:. When that cannot be done at once, the constant struts.tiles.ognl.legacy.enabled (default false in the plugin’s struts-plugin.xml) can be set to true in the struts.xml of the web application that owns the container; the same definition then rendered the title Bookshelf - catalogue. The constant and the legacy evaluator are deprecated (removal is tracked as WW-5714) and are read once, on the first OGNL: evaluation:

<constant name="struts.tiles.ognl.legacy.enabled" value="true"/>

Definitions in the Action: Annotations

The plugin ships annotations that keep a one-off definition next to the action that uses it, instead of in tiles.xml. The annotation @TilesDefinition has the members name, extend, template, templateExpression, templateType, preparer, role, putAttributes and putListAttributes; the nested annotations are @TilesPutAttribute, @TilesPutListAttribute, @TilesAddAttribute, @TilesAddListAttribute and @TilesDefinitions for more than one. The action’s result is a tiles result with no body: the plugin builds a definition from the annotation and registers it for the action.

@TilesDefinition(extend = "baseLayout", putAttributes = {
        @TilesPutAttribute(name = "title", value = "Annotated"),
        @TilesPutAttribute(name = "body", value = "/WEB-INF/tiles/books-body.jsp")
})
public class AnnotatedAction extends ActionSupport {
    public List<String> getTitles() {
        return List.of("Hyperion");
    }
}
<action name="annotated" class="org.example.bookshelf.AnnotatedAction">
    <result type="tiles"/>
</action>

The request answered the layout with the title Annotated and the list Hyperion. The definition baseLayout it extends still lives in tiles.xml: the annotation only describes the part that differs. The plugin documentation shows the same construction with a @Result(name = "success", type = "tiles") of the Convention plugin.

The Alternative: a Tag-File Layout

The same Bookshelf frame without Tiles needs two files and no configuration. The layout is a tag file whose slots are attributes and fragments, and the page that uses it wraps its content in the tag. The tag-file mechanics (the tag and attribute directives, jsp:invoke, jsp:doBody) are on the Tag Files & Layouts page; the point here is that the Struts tags and the value stack work inside it.

<%@ tag description="Bookshelf page layout" pageEncoding="UTF-8" %>
<%@ attribute name="title" required="true" %>
<%@ attribute name="menu" fragment="true" %>
<!DOCTYPE html>
<html>
<head><title>${title}</title></head>
<body>
<header><h1>Bookshelf (tag file)</h1></header>
<nav><jsp:invoke fragment="menu"/></nav>
<main><jsp:doBody/></main>
<footer><small>Bookshelf demo</small></footer>
</body>
</html>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<t:layout title="Bookshelf - ${pageTitle}">
    <jsp:attribute name="menu"><a href="<s:url action='books'/>">books</a></jsp:attribute>
    <jsp:body>
        <ul><s:iterator value="titles"><li><s:property/></li></s:iterator></ul>
    </jsp:body>
</t:layout>

The action’s result is the plain dispatcher result for /WEB-INF/views/tagfile.jsp. The response was the framed page with the title Bookshelf - Bookshelf - catalogue; the doubled prefix is a slip of the example (the page adds one and the action’s pageTitle already has it), but it proves the interesting fact: the EL expression ${pageTitle} inside the page read the action’s property, because Struts wraps the request so that EL attribute lookups fall back to the value stack.

Table 5. Tiles concepts and their tag-file counterparts
Tiles Tag-file layout

definition with template

layout.tag

put-attribute

An attribute directive and an attribute of the call, or a jsp:attribute fragment

tiles:insertAttribute

${title}, jsp:invoke fragment="menu" and jsp:doBody

extends

A layout tag file that calls another layout tag

The tiles result

A dispatcher result to a page that is just the call of the layout

Wildcard definitions

No equivalent; one page per result, or the action returns the page name

The costs of the tag-file route are small: pages repeat the one-line wrapper, and the layout lives in the page rather than in a central registry. Its gains are that nothing is retired, the page is checked at translation time and a missing slot is a compile error instead of a blank place. SiteMesh 3, used directly as a servlet filter, is the other alternative when the layout must be applied to finished pages of any origin; the plugin for it is not published for 7.4.0, so the filter is a dependency of the application, not of Struts.

Pitfalls

Table 6. Things that go wrong with Tiles in a Struts 7.4 application
Symptom Cause Fix

NoClassDefFoundError ... LogFactory at start-up

The plugin’s digester brings an old commons-beanutils without Commons Logging

Add commons-beanutils 1.11.0

First request fails with a NullPointerException mentioning ApplicationContext and context is null

The listener is not declared, so no container exists

Add org.apache.struts2.tiles.StrutsTilesListener to web.xml

Cannot find definition named 'x'

The definition is missing, or the file is not named tiles*.xml, or a wildcard definition has no WILDCARD: prefix

Check the name, the file name and the prefix

A typo in a result name renders a broken page, not a clear error

A wildcard definition matched the typo and failed on a derived path

Make wildcard definitions narrower (WILDCARD:bookshelf.page.*), or list the pages explicitly

The Tiles OGNL evaluator is disabled

A definition uses OGNL: in 7.4.0

Use S2:; the constant is a temporary migration aid

The title shows the bare key page.title

The I18N: key is not in the bundle that the action resolves

Put it in a bundle of the action (class, package or global)

The menu loses items in an insertDefinition override

A putListAttribute replaces the inherited list unless it inherits

Use inherit="true" on the list when items should be added

A tiles result in a package gives an unknown result type

The package does not extend tiles-default

Extend it, or declare the result type in the package

Definitions are not found in a WAR deployed as app##1.war

The ## version separator breaks the URL-based file lookup

List the files in the DEFINITIONS_CONFIG parameter

What Changed Recently

Table 7. History of the Struts Tiles integration
Version Change

Struts 2.0 (2008 book)

Tiles 2, the version the book Struts 2 in Action describes

Struts 2.3.28

Definitions matching tiles*.xml are loaded automatically; the DEFINITIONS_CONFIG init-param becomes optional

Struts 2.5

The separate Tiles 3 plugin (struts2-tiles3-plugin) is dropped; the Tiles plugin is upgraded to Tiles 3

Struts 2.5.3

The S2: evaluator reads the value stack; the I18N: evaluator selects a text from the resource bundles

Struts 2.6

The portlet Tiles plugin is marked deprecated

Struts 6.3.0

The Tiles code is bundled in struts2-tiles-plugin; there is no separate Tiles dependency

Struts 7.0

The Jakarta namespace for servlets and JSP; the TLD and the listener work on Tomcat 10.1 and later

Struts 7.4.0

The OGNL: evaluator is disabled by default; struts.tiles.ognl.legacy.enabled=true restores it temporarily

Apache Tiles retired in December 2018, and the Attic move was completed in November 2019. The plugin documentation of the Tiles 3 generation still exists on the Struts site, with the notice that the plugin was dropped in 2.5 and the Tiles plugin should be used instead, and the portlet Tiles plugin has the notice that it is deprecated since 2.6.

Where the Books Are Outdated

The two Struts books of the section’s bibliography teach Tiles in the form of their day, and the vocabulary is the best map for a reader who inherits such an application.

Table 8. From the books to Struts 7.4
The book (Struts 1 era, or Struts 2.0) Today

Struts 1.x with Tiles 1: tiles:insert, tiles:put, tiles:useAttribute, a tiles-defs.xml file, a Tiles plug-in in struts-config.xml, definitions used as forwards

tiles:insertAttribute, tiles:importAttribute, tiles.xml, a listener in web.xml, and a tiles result in struts.xml

A Tiles 2 definition file and its DTD for the version of the time

The Tiles 3.0 DTD (tiles-config_3_0.dtd, bundled in the plugin jar); the XML shape of definition and put-attribute is unchanged

"Definitions as forwards": a Struts 1 forward whose path is a definition name

A result of type tiles whose body is a definition name, one per result code

org.apache.tiles.listener.TilesListener or a Tiles servlet

org.apache.struts2.tiles.StrutsTilesListener

The Composite View pattern as a reason to prefer Tiles over includes

Still the pattern, but a layout tag file implements it in plain JSP with no framework, and a retired framework is a cost

Expressions such as OGNL: in definitions

S2:; OGNL: is off by default

The plugin and Tiles as two jars with their own versions

One jar, struts2-tiles-plugin, versioned with Struts

Where the Official Documentation Is Inconsistent

  • The plugin page says the listener may be "the standard tiles listener org.apache.tiles.listener.TilesListener`"; that class is not in the 7.4.0 jar (Tiles 3 moved it to `org.apache.tiles.web.startup, where only the abstract base exists).

  • The plugin page’s sample tiles.xml has put-attribute name="header" value=".header" and value=".bodyp" in the layout definition; the values look like typos (the child definition overrides both with real paths).

  • The plugin page has a settings table and a section on the OGNL: prefix, but no word on wildcard definitions; the Tiles tutorial says the WILDCARD: prefix is optional, and in the Struts container it was required.

  • The plugin dependency in the Maven snippet uses a property named version.tiles, which is not defined in the 7.4.0 struts2-bom or struts2-parent POMs; with the BOM imported the version is the Struts version, and the page does not mention the extra commons-beanutils dependency that 7.4.0 needed in the run (see above).

References