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.
| Technique | Where the layout is declared | Fit and cost |
|---|---|---|
Tiles plugin ( |
|
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 ( |
JSP tag-file layout |
A |
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 |
|
A Tiles tag inside an ordinary page |
A way to use Tiles components in a page that was not rendered by the |
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 |
|
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.
Source: Apache Attic — Apache Tiles
Source: Apache Struts — Tiles Plugin
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.
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:
| Element or attribute | Meaning |
|---|---|
|
The name an action result (or a JSP tag) uses |
|
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 |
|
Inherits every attribute and the template of another definition and lets this one add or override attributes; chains of any depth are allowed |
|
Sets a named slot. |
|
The value is an expression with an evaluator prefix ( |
|
|
|
A list slot; a child definition can add items to the inherited list with its own |
|
A class run before the definition is rendered (the plugin supplies a Struts-aware preparer factory, |
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.
| Tag | Use |
|---|---|
|
Renders a named attribute of the current definition at this place; |
|
Writes an attribute as text, for a title or a CSS class |
|
Imports one attribute, or all of them when no name is given, into a scope ( |
|
Renders a named definition from inside a page, with optional overrides given by nested |
|
Renders a template file with attributes given inline, with no definition in |
|
The attributes of an |
|
Defines a definition in a page, stored in a scope, to be inserted by name later |
|
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.
Source: Apache Tiles — Wildcard support
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.
| Prefix | Evaluates against | State in 7.4.0 |
|---|---|---|
|
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 |
|
Treats the text after the prefix as a key of the Struts resource bundles and returns the localised text |
Works; saves one |
|
The Jakarta Expression Language; |
Works where the container provides EL (the plugin declares the EL API as provided) |
|
The Tiles request, with Tiles' own OGNL, outside the Struts controls |
Disabled by default; throws |
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.
Source: Struts Javadoc — TilesDefinition
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.
| Tiles | Tag-file layout |
|---|---|
|
|
|
An |
|
|
|
A layout tag file that calls another layout tag |
The |
A |
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
| Symptom | Cause | Fix |
|---|---|---|
|
The plugin’s digester brings an old |
Add |
First request fails with a |
The listener is not declared, so no container exists |
Add |
|
The definition is missing, or the file is not named |
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 ( |
|
A definition uses |
Use |
The title shows the bare key |
The |
Put it in a bundle of the action (class, package or global) |
The menu loses items in an |
A |
Use |
A |
The package does not extend |
Extend it, or declare the result type in the package |
Definitions are not found in a WAR deployed as |
The |
List the files in the |
What Changed Recently
| Version | Change |
|---|---|
Struts 2.0 (2008 book) |
Tiles 2, the version the book Struts 2 in Action describes |
Struts 2.3.28 |
Definitions matching |
Struts 2.5 |
The separate Tiles 3 plugin ( |
Struts 2.5.3 |
The |
Struts 2.6 |
The portlet Tiles plugin is marked deprecated |
Struts 6.3.0 |
The Tiles code is bundled in |
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 |
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.
Source: Apache Struts — Tiles Plugin
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.
| The book (Struts 1 era, or Struts 2.0) | Today |
|---|---|
Struts 1.x with Tiles 1: |
|
A Tiles 2 definition file and its DTD for the version of the time |
The Tiles 3.0 DTD ( |
"Definitions as forwards": a Struts 1 |
A result of type |
|
|
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 |
|
The plugin and Tiles as two jars with their own versions |
One jar, |
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.xmlhasput-attribute name="header" value=".header"andvalue=".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 theWILDCARD: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.0struts2-bomorstruts2-parentPOMs; with the BOM imported the version is the Struts version, and the page does not mention the extracommons-beanutilsdependency that 7.4.0 needed in the run (see above).
References
-
Apache Struts — Tiles Plugin: Overview, Usage, S2, I18N, Legacy OGNL expressions, Example, Settings, Installation
-
Apache Struts — Tiles 3 Plugin (dropped) and Portlet Tiles Plugin (deprecated)
-
Apache Struts examples: the
tilesexample -
Struts Javadoc: TilesDefinition
-
Apache Attic: Apache Tiles
-
Apache Tiles: Wildcard support, Nesting and extending definitions, insertAttribute, importAttribute, insertDefinition, insertTemplate
-
Jakarta Pages 4.0 — Chapter 8 Tag Files