You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2022/11/27 08:24:44 UTC

[struts] 01/23: WW-5233 Copies a based set of Tiles classes used by Struts

This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch WW-5233-tiles
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 1d58de276a47da4994c65ce13b64616d0b02817d
Author: Lukasz Lenart <lu...@apache.org>
AuthorDate: Sat Oct 1 16:48:18 2022 +0200

    WW-5233 Copies a based set of Tiles classes used by Struts
---
 apps/showcase/pom.xml                              |   6 -
 plugins/portlet-tiles/pom.xml                      |   9 -
 .../struts2/views/tiles/PortletTilesResult.java    |   6 +-
 plugins/tiles/pom.xml                              |  40 +-
 .../struts2/tiles/I18NAttributeEvaluator.java      |   2 +-
 .../struts2/tiles/StrutsAttributeEvaluator.java    |   4 +-
 .../tiles/StrutsFreeMarkerAttributeRenderer.java   |   6 +-
 .../struts2/tiles/StrutsPreparerFactory.java       |   4 +-
 .../tiles/StrutsTilesAnnotationProcessor.java      |  10 +-
 .../struts2/tiles/StrutsTilesContainerFactory.java |  44 +-
 .../struts2/tiles/StrutsTilesInitializer.java      |   6 +-
 .../apache/struts2/tiles/StrutsTilesListener.java  |   4 +-
 .../struts2/tiles/StrutsTilesLocaleResolver.java   |   4 +-
 .../apache/struts2/views/tiles/TilesResult.java    |  46 +-
 .../main/java/org/apache/tiles/api/Attribute.java  | 366 ++++++++++++++
 .../org/apache/tiles/api/AttributeContext.java     | 164 +++++++
 .../apache/tiles/api/BasicAttributeContext.java    | 434 ++++++++++++++++
 .../java/org/apache/tiles/api/CompareUtil.java     |  67 +++
 .../main/java/org/apache/tiles/api/Definition.java | 162 ++++++
 .../main/java/org/apache/tiles/api/Expression.java | 157 ++++++
 .../java/org/apache/tiles/api/ListAttribute.java   | 166 +++++++
 .../api/NoSuchContainerException.java}             |  34 +-
 .../java/org/apache/tiles/api/TilesContainer.java  | 139 ++++++
 .../apache/tiles/api/TilesContainerWrapper.java    | 109 +++++
 .../java/org/apache/tiles/api/TilesException.java  |  68 +++
 .../org/apache/tiles/api/access/TilesAccess.java   | 160 ++++++
 .../api/access/package-info.java}                  |  23 +-
 .../api/mgmt/MutableTilesContainer.java}           |  35 +-
 .../api/mgmt/package-info.java}                    |  24 +-
 .../java/org/apache/tiles/api/package-info.java    | 385 +++++++++++++++
 .../tiles/api/preparer/PreparerException.java      |  67 +++
 .../apache/tiles/api/preparer/ViewPreparer.java    |  58 +++
 .../api/preparer/package-info.java}                |  26 +-
 .../autotag/core/runtime/AbstractModelBody.java    |  87 ++++
 .../tiles/autotag/core/runtime/AutotagRuntime.java |  51 ++
 .../tiles/autotag/core/runtime/ModelBody.java      |  62 +++
 .../autotag/core/runtime/annotation/Parameter.java |  56 +++
 .../core/runtime/annotation/package-info.java}     |  26 +-
 .../autotag/core/runtime/package-info.java}        |  26 +-
 .../autotag/core/runtime/util/NullWriter.java}     |  38 +-
 .../autotag/core/runtime/util/package-info.java}   |  26 +-
 .../apache/tiles/autotag/model/TemplateClass.java  | 195 ++++++++
 .../apache/tiles/autotag/model/TemplateMethod.java | 131 +++++
 .../tiles/autotag/model/TemplateParameter.java     | 186 +++++++
 .../apache/tiles/autotag/model/TemplateSuite.java  | 129 +++++
 .../autotag/model/package-info.java}               |  26 +-
 .../tiles/core/definition/DefinitionsFactory.java  |  79 +++
 .../definition/DefinitionsFactoryException.java    |  74 +++
 .../tiles/core/definition/DefinitionsReader.java   |  54 ++
 .../definition/NoSuchDefinitionException.java}     |  33 +-
 .../core/definition/RefreshMonitor.java}           |  34 +-
 .../UnresolvingLocaleDefinitionsFactory.java       |  90 ++++
 .../definition/dao/BaseLocaleUrlDefinitionDAO.java | 164 +++++++
 .../dao/CachingLocaleUrlDefinitionDAO.java         | 275 +++++++++++
 .../tiles/core/definition/dao/DefinitionDAO.java   |  57 +++
 .../dao/ResolvingLocaleUrlDefinitionDAO.java       | 174 +++++++
 .../core/definition/dao/package-info.java}         |  27 +-
 .../digester/DigesterDefinitionsReader.java        | 468 ++++++++++++++++++
 .../DigesterDefinitionsReaderException.java}       |  36 +-
 .../core/definition/digester/package-info.java}    |  26 +-
 .../core/definition/package-info.java}             |  27 +-
 .../pattern/AbstractPatternDefinitionResolver.java | 108 ++++
 .../pattern/BasicPatternDefinitionResolver.java    |  77 +++
 .../pattern/DefinitionPatternMatcher.java}         |  38 +-
 .../pattern/DefinitionPatternMatcherFactory.java   |  45 ++
 .../pattern/PatternDefinitionResolver.java         |  66 +++
 .../pattern/PatternDefinitionResolverAware.java}   |  34 +-
 .../definition/pattern/PatternRecognizer.java}     |  33 +-
 .../tiles/core/definition/pattern/PatternUtil.java | 242 +++++++++
 .../pattern/PrefixedPatternDefinitionResolver.java | 106 ++++
 .../core/definition/pattern/package-info.java}     |  26 +-
 .../regexp/RegexpDefinitionPatternMatcher.java     |  74 +++
 .../RegexpDefinitionPatternMatcherFactory.java}    |  32 +-
 .../definition/pattern/regexp/package-info.java}   |  26 +-
 .../wildcard/WildcardDefinitionPatternMatcher.java |  80 +++
 .../WildcardDefinitionPatternMatcherFactory.java   |  56 +++
 .../definition/pattern/wildcard/package-info.java} |  26 +-
 .../core/evaluator/AbstractAttributeEvaluator.java |  52 ++
 .../tiles/core/evaluator/AttributeEvaluator.java   |  52 ++
 .../core/evaluator/AttributeEvaluatorFactory.java  |  50 ++
 .../evaluator/AttributeEvaluatorFactoryAware.java} |  33 +-
 .../evaluator/BasicAttributeEvaluatorFactory.java  |  90 ++++
 .../core/evaluator/EvaluationException.java}       |  42 +-
 .../evaluator/impl/DirectAttributeEvaluator.java}  |  30 +-
 .../core/evaluator/impl/package-info.java}         |  26 +-
 .../core/evaluator/package-info.java}              |  26 +-
 .../factory/AbstractTilesContainerFactory.java}    |  37 +-
 .../core/factory/BasicTilesContainerFactory.java   | 408 +++++++++++++++
 .../factory/TilesContainerFactoryException.java}   |  35 +-
 .../core/factory/package-info.java}                |  26 +-
 .../tiles/core/impl/BasicTilesContainer.java       | 400 +++++++++++++++
 .../core/impl/InvalidTemplateException.java}       |  42 +-
 .../core/impl/mgmt/CachingTilesContainer.java      | 224 +++++++++
 .../core/impl/mgmt/package-info.java}              |  26 +-
 .../core/impl/package-info.java}                   |  26 +-
 .../core/locale/LocaleResolver.java}               |  36 +-
 .../core/locale/impl/DefaultLocaleResolver.java    |  57 +++
 .../core/locale/impl/package-info.java}            |  26 +-
 .../core/locale/package-info.java}                 |  27 +-
 .../core/prepare/factory/BasicPreparerFactory.java |  86 ++++
 .../prepare/factory/NoSuchPreparerException.java}  |  33 +-
 .../core/prepare/factory/PreparerFactory.java      |  51 ++
 .../core/prepare/factory/package-info.java}        |  28 +-
 .../tiles/core/renderer/DefinitionRenderer.java    |  69 +++
 .../core/renderer/package-info.java}               |  26 +-
 .../core/startup/AbstractTilesInitializer.java     | 112 +++++
 .../tiles/core/startup/TilesInitializer.java       |  47 ++
 .../core/startup/package-info.java}                |  28 +-
 .../apache/tiles/core/util/CombinedBeanInfo.java   |  97 ++++
 .../org/apache/tiles/core/util/WildcardHelper.java | 545 +++++++++++++++++++++
 .../core/util/package-info.java}                   |  26 +-
 .../org/apache/tiles/el/ELAttributeEvaluator.java  |  94 ++++
 .../java/org/apache/tiles/el/ELContextImpl.java    | 146 ++++++
 .../el/ExpressionFactoryFactory.java}              |  33 +-
 .../tiles/el/JspExpressionFactoryFactory.java      |  65 +++
 .../java/org/apache/tiles/el/ScopeELResolver.java  | 145 ++++++
 .../tiles/el/TilesContextBeanELResolver.java       | 174 +++++++
 .../apache/tiles/el/TilesContextELResolver.java    | 156 ++++++
 .../el/package-info.java}                          |  26 +-
 .../freemarker/package-info.java}                  |  26 +-
 .../freemarker/template/AddAttributeFMModel.java   |  86 ++++
 .../template/AddListAttributeFMModel.java          |  81 +++
 .../freemarker/template/DefinitionFMModel.java     |  84 ++++
 .../freemarker/template/GetAsStringFMModel.java    |  91 ++++
 .../template/ImportAttributeFMModel.java           |  81 +++
 .../template/InsertAttributeFMModel.java           | 105 ++++
 .../template/InsertDefinitionFMModel.java          | 110 +++++
 .../freemarker/template/InsertTemplateFMModel.java | 109 +++++
 .../freemarker/template/PutAttributeFMModel.java   | 109 +++++
 .../template/PutListAttributeFMModel.java          |  84 ++++
 .../template/SetCurrentContainerFMModel.java       |  71 +++
 .../template/TilesFMModelRepository.java           |  59 +++
 .../tiles/ognl/AnyScopePropertyAccessor.java       |  91 ++++
 .../tiles/ognl/DelegatePropertyAccessor.java       |  86 ++++
 .../ognl/NestedObjectDelegatePropertyAccessor.java |  95 ++++
 .../ognl/NestedObjectExtractor.java}               |  35 +-
 .../apache/tiles/ognl/OGNLAttributeEvaluator.java  |  47 ++
 .../ognl/PropertyAccessorDelegateFactory.java}     |  39 +-
 .../apache/tiles/ognl/ScopePropertyAccessor.java   |  70 +++
 ...esApplicationContextNestedObjectExtractor.java} |  29 +-
 ...ilesContextPropertyAccessorDelegateFactory.java | 105 ++++
 .../ognl/package-info.java}                        |  26 +-
 .../tiles/request/AbstractClientRequest.java       |  93 ++++
 .../org/apache/tiles/request/AbstractRequest.java  |  56 +++
 .../apache/tiles/request/AbstractViewRequest.java  |  62 +++
 .../apache/tiles/request/ApplicationAccess.java    |  49 ++
 .../apache/tiles/request/ApplicationContext.java   |  81 +++
 .../request/ApplicationContextAware.java}          |  32 +-
 .../apache/tiles/request/ApplicationResource.java  |  82 ++++
 .../org/apache/tiles/request/DispatchRequest.java  |  53 ++
 .../tiles/request/DispatchRequestWrapper.java      | 141 ++++++
 .../request/NotAvailableFeatureException.java}     |  32 +-
 .../java/org/apache/tiles/request/Request.java     | 163 ++++++
 .../request/RequestException.java}                 |  41 +-
 .../request/RequestWrapper.java}                   |  31 +-
 .../request/attribute/Addable.java}                |  33 +-
 .../request/attribute/AttributeExtractor.java}     |  27 +-
 .../attribute/EnumeratedValuesExtractor.java}      |  33 +-
 .../request/attribute/HasAddableKeys.java}         |  27 +-
 .../request/attribute/HasKeys.java}                |  38 +-
 .../request/attribute/HasRemovableKeys.java}       |  32 +-
 .../request/attribute/package-info.java}           |  30 +-
 .../tiles/request/collection/CollectionUtil.java   |  68 +++
 .../tiles/request/collection/HeaderValuesMap.java  | 518 ++++++++++++++++++++
 .../apache/tiles/request/collection/KeySet.java    | 164 +++++++
 .../apache/tiles/request/collection/MapEntry.java  | 126 +++++
 .../request/collection/MapEntryArrayValues.java    |  97 ++++
 .../request/collection/ReadOnlyEnumerationMap.java | 421 ++++++++++++++++
 .../tiles/request/collection/RemovableKeySet.java  |  93 ++++
 .../apache/tiles/request/collection/ScopeMap.java  | 171 +++++++
 .../request/collection/package-info.java}          |  36 +-
 .../request/freemarker/EnvironmentScopeMap.java    |  60 +++
 .../request/freemarker/FreemarkerRequest.java      | 145 ++++++
 .../freemarker/FreemarkerRequestException.java}    |  33 +-
 .../request/freemarker/FreemarkerRequestUtil.java  |  84 ++++
 .../NotAvailableFreemarkerServletException.java}   |  33 +-
 .../autotag/FreemarkerAutotagException.java}       |  34 +-
 .../autotag/FreemarkerAutotagRuntime.java          |  70 +++
 .../freemarker/autotag/FreemarkerModelBody.java    |  64 +++
 .../request/freemarker/autotag/FreemarkerUtil.java |  62 +++
 .../request/freemarker/autotag/package-info.java}  |  26 +-
 .../extractor/EnvironmentScopeExtractor.java       |  89 ++++
 .../freemarker/extractor/package-info.java}        |  26 +-
 .../request/freemarker/package-info.java}          |  26 +-
 .../render/AttributeValueFreemarkerServlet.java    |  53 ++
 .../request/freemarker/render/package-info.java}   |  26 +-
 .../freemarker/servlet/SharedVariableFactory.java} |  32 +-
 .../SharedVariableLoaderFreemarkerServlet.java     | 202 ++++++++
 .../servlet/WebappClassTemplateLoader.java         |  85 ++++
 .../request/freemarker/servlet/package-info.java}  |  26 +-
 .../tiles/request/jsp/JspPrintWriterAdapter.java   | 436 +++++++++++++++++
 .../org/apache/tiles/request/jsp/JspRequest.java   | 201 ++++++++
 .../java/org/apache/tiles/request/jsp/JspUtil.java |  54 ++
 .../request/jsp/autotag/JspAutotagRuntime.java     |  71 +++
 .../tiles/request/jsp/autotag/JspModelBody.java    |  65 +++
 .../request/jsp/autotag/package-info.java}         |  26 +-
 .../request/jsp/extractor/ScopeExtractor.java      |  73 +++
 .../jsp/extractor/SessionScopeExtractor.java       |  78 +++
 .../request/jsp/extractor/package-info.java}       |  26 +-
 .../request/jsp/package-info.java}                 |  26 +-
 .../apache/tiles/request/locale/LocaleUtil.java    |  60 +++
 .../locale/PostfixedApplicationResource.java       | 237 +++++++++
 .../request/locale/URLApplicationResource.java     | 201 ++++++++
 .../reflect/CannotInstantiateObjectException.java} |  34 +-
 .../apache/tiles/request/reflect/ClassUtil.java    | 125 +++++
 .../request/reflect/package-info.java}             |  26 +-
 .../tiles/request/render/BasicRendererFactory.java |  81 +++
 .../request/render/CannotRenderException.java}     |  40 +-
 .../request/render/ChainedDelegateRenderer.java    |  82 ++++
 .../tiles/request/render/DispatchRenderer.java     |  65 +++
 .../request/render/NoSuchRendererException.java}   |  33 +-
 .../request/render/RenderException.java}           |  40 +-
 .../org/apache/tiles/request/render/Renderer.java  |  50 ++
 .../request/render/RendererFactory.java}           |  39 +-
 .../request/render/StringRenderer.java}            |  37 +-
 .../servlet/NotAServletEnvironmentException.java}  |  34 +-
 .../request/servlet/ServletApplicationContext.java | 129 +++++
 .../tiles/request/servlet/ServletRequest.java      | 345 +++++++++++++
 .../apache/tiles/request/servlet/ServletUtil.java  | 119 +++++
 .../extractor/ApplicationScopeExtractor.java       |  66 +++
 .../request/servlet/extractor/HeaderExtractor.java |  74 +++
 .../servlet/extractor/InitParameterExtractor.java  |  57 +++
 .../servlet/extractor/ParameterExtractor.java      |  56 +++
 .../servlet/extractor/RequestScopeExtractor.java   |  66 +++
 .../servlet/extractor/SessionScopeExtractor.java   |  79 +++
 .../request/servlet/extractor/package-info.java}   |  26 +-
 .../request/servlet/package-info.java}             |  26 +-
 .../apache/tiles/template/AddAttributeModel.java   | 124 +++++
 .../tiles/template/AddListAttributeModel.java      |  62 +++
 .../apache/tiles/template/AttributeResolver.java   |  60 +++
 .../apache/tiles/template/ComposeStackUtil.java    |  85 ++++
 .../tiles/template/DefaultAttributeResolver.java   |  87 ++++
 .../org/apache/tiles/template/DefinitionModel.java | 141 ++++++
 .../apache/tiles/template/GetAsStringModel.java    | 217 ++++++++
 .../tiles/template/ImportAttributeModel.java       | 196 ++++++++
 .../tiles/template/InsertAttributeModel.java       | 208 ++++++++
 .../tiles/template/InsertDefinitionModel.java      | 151 ++++++
 .../apache/tiles/template/InsertTemplateModel.java | 143 ++++++
 .../template/NoSuchAttributeException.java}        |  34 +-
 .../apache/tiles/template/PutAttributeModel.java   | 167 +++++++
 .../tiles/template/PutListAttributeModel.java      |  90 ++++
 .../template/SetCurrentContainerModel.java}        |  35 +-
 .../template/package-info.java}                    |  26 +-
 .../tiles/web/jsp/taglib/UseAttributeTag.java      | 221 +++++++++
 .../web/jsp/taglib/package-info.java}              |  27 +-
 .../tiles/web/startup/AbstractTilesListener.java   |  71 +++
 .../web/startup/package-info.java}                 |  26 +-
 .../web/util/AttributeContextMutator.java}         |  36 +-
 .../tiles/web/util/TilesDispatchServlet.java       | 150 ++++++
 .../web/util/package-info.java}                    |  26 +-
 .../resources/META-INF/tld/tiles-extras-jsp.tld    | 105 ++++
 .../tiles/StrutsTilesAnnotationProcessorTest.java  |   6 +-
 pom.xml                                            |  62 +--
 253 files changed, 19997 insertions(+), 1886 deletions(-)

diff --git a/apps/showcase/pom.xml b/apps/showcase/pom.xml
index add8be9a5..994ae103c 100644
--- a/apps/showcase/pom.xml
+++ b/apps/showcase/pom.xml
@@ -68,12 +68,6 @@
             <artifactId>struts2-json-plugin</artifactId>
         </dependency>
 
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-jsp</artifactId>
-            <scope>runtime</scope>
-        </dependency>
-
         <dependency>
             <groupId>org.apache.struts</groupId>
             <artifactId>struts2-convention-plugin</artifactId>
diff --git a/plugins/portlet-tiles/pom.xml b/plugins/portlet-tiles/pom.xml
index 4c26ddc55..3ce787962 100644
--- a/plugins/portlet-tiles/pom.xml
+++ b/plugins/portlet-tiles/pom.xml
@@ -40,10 +40,6 @@
              <groupId>org.apache.struts</groupId>
              <artifactId>struts2-portlet-plugin</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-request-portlet</artifactId>
-        </dependency>
         <dependency>
             <groupId>javax.servlet.jsp</groupId>
             <artifactId>jsp-api</artifactId>
@@ -54,11 +50,6 @@
             <artifactId>portlet-api</artifactId>
             <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-jsp</artifactId>
-            <scope>runtime</scope>
-        </dependency>
     </dependencies>
     <properties>
     	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
diff --git a/plugins/portlet-tiles/src/main/java/org/apache/struts2/views/tiles/PortletTilesResult.java b/plugins/portlet-tiles/src/main/java/org/apache/struts2/views/tiles/PortletTilesResult.java
index 82249ccc9..ef1f82f76 100644
--- a/plugins/portlet-tiles/src/main/java/org/apache/struts2/views/tiles/PortletTilesResult.java
+++ b/plugins/portlet-tiles/src/main/java/org/apache/struts2/views/tiles/PortletTilesResult.java
@@ -22,9 +22,9 @@ import com.opensymphony.xwork2.ActionInvocation;
 import org.apache.struts2.portlet.PortletConstants;
 import org.apache.struts2.portlet.context.PortletActionContext;
 import org.apache.struts2.result.ServletDispatcherResult;
-import org.apache.tiles.TilesContainer;
-import org.apache.tiles.TilesException;
-import org.apache.tiles.access.TilesAccess;
+import org.apache.tiles.api.TilesContainer;
+import org.apache.tiles.api.TilesException;
+import org.apache.tiles.api.access.TilesAccess;
 import org.apache.tiles.request.ApplicationContext;
 import org.apache.tiles.request.Request;
 import org.apache.tiles.request.portlet.RenderPortletRequest;
diff --git a/plugins/tiles/pom.xml b/plugins/tiles/pom.xml
index 79daada9f..807bd1f3d 100644
--- a/plugins/tiles/pom.xml
+++ b/plugins/tiles/pom.xml
@@ -33,44 +33,8 @@
 
     <dependencies>
         <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-core</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-servlet</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-request-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-request-jsp</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-request-servlet</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-jsp</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-freemarker</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-ognl</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.tiles</groupId>
-            <artifactId>tiles-el</artifactId>
+            <groupId>commons-digester</groupId>
+            <artifactId>commons-digester</artifactId>
         </dependency>
         <dependency>
             <groupId>org.glassfish</groupId>
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/I18NAttributeEvaluator.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/I18NAttributeEvaluator.java
index 04673f294..e566f5747 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/I18NAttributeEvaluator.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/I18NAttributeEvaluator.java
@@ -25,7 +25,7 @@ import com.opensymphony.xwork2.config.ConfigurationException;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.struts2.ServletActionContext;
-import org.apache.tiles.evaluator.AbstractAttributeEvaluator;
+import org.apache.tiles.core.evaluator.AbstractAttributeEvaluator;
 import org.apache.tiles.request.Request;
 import org.apache.tiles.request.servlet.ServletUtil;
 
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsAttributeEvaluator.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsAttributeEvaluator.java
index b0cdb95ba..7b3e7624f 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsAttributeEvaluator.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsAttributeEvaluator.java
@@ -25,8 +25,8 @@ import ognl.OgnlException;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.struts2.ServletActionContext;
-import org.apache.tiles.evaluator.AbstractAttributeEvaluator;
-import org.apache.tiles.evaluator.EvaluationException;
+import org.apache.tiles.core.evaluator.AbstractAttributeEvaluator;
+import org.apache.tiles.core.evaluator.EvaluationException;
 import org.apache.tiles.request.Request;
 import org.apache.tiles.request.servlet.ServletUtil;
 
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsFreeMarkerAttributeRenderer.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsFreeMarkerAttributeRenderer.java
index cb6e0d403..e39fed4c6 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsFreeMarkerAttributeRenderer.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsFreeMarkerAttributeRenderer.java
@@ -33,9 +33,9 @@ import org.apache.struts2.views.freemarker.FreemarkerManager;
 import org.apache.struts2.views.freemarker.FreemarkerResult;
 import org.apache.struts2.views.freemarker.StrutsBeanWrapper;
 import org.apache.tiles.freemarker.template.TilesFMModelRepository;
-import org.apache.tiles.impl.InvalidTemplateException;
 import org.apache.tiles.request.Request;
 import org.apache.tiles.request.render.Renderer;
+import org.apache.tiles.core.impl.InvalidTemplateException;
 import org.apache.tiles.request.servlet.ServletUtil;
 
 import javax.servlet.ServletContext;
@@ -44,7 +44,7 @@ import java.io.IOException;
 
 public class StrutsFreeMarkerAttributeRenderer implements Renderer {
 
-    private static Logger LOG = LogManager.getLogger(StrutsFreeMarkerAttributeRenderer.class);
+    private static final Logger LOG = LogManager.getLogger(StrutsFreeMarkerAttributeRenderer.class);
 
     @Override
     public void render(String path, Request request) throws IOException {
@@ -75,7 +75,7 @@ public class StrutsFreeMarkerAttributeRenderer implements Renderer {
     }
 
     /**
-     * Depending how Tiles definition was defined, request can an instance of JspRequest (for JSPs)
+     * Depending on how Tiles definition was defined, request can an instance of JspRequest (for JSPs)
      * or a ServletRequest (FreeMarker)
      */
     protected ActionContext readActionContext(Request request) {
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsPreparerFactory.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsPreparerFactory.java
index e279adcb4..8630a061f 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsPreparerFactory.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsPreparerFactory.java
@@ -23,8 +23,8 @@ import com.opensymphony.xwork2.ObjectFactory;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.logging.log4j.message.ParameterizedMessage;
-import org.apache.tiles.preparer.ViewPreparer;
-import org.apache.tiles.preparer.factory.BasicPreparerFactory;
+import org.apache.tiles.api.preparer.ViewPreparer;
+import org.apache.tiles.core.prepare.factory.BasicPreparerFactory;
 
 /**
  * This is a basic ViewPreparer factory that uses {@link ObjectFactory} to create the ViewPreparer
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesAnnotationProcessor.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesAnnotationProcessor.java
index e36b75d16..8842578af 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesAnnotationProcessor.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesAnnotationProcessor.java
@@ -25,10 +25,10 @@ import org.apache.struts2.tiles.annotation.TilesDefinition;
 import org.apache.struts2.tiles.annotation.TilesDefinitions;
 import org.apache.struts2.tiles.annotation.TilesPutAttribute;
 import org.apache.struts2.tiles.annotation.TilesPutListAttribute;
-import org.apache.tiles.Attribute;
-import org.apache.tiles.Definition;
-import org.apache.tiles.Expression;
-import org.apache.tiles.ListAttribute;
+import org.apache.tiles.api.Attribute;
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.api.Expression;
+import org.apache.tiles.api.ListAttribute;
 
 /**
  * Processes tiles annotations to create {@link Definition}s and
@@ -123,7 +123,7 @@ public class StrutsTilesAnnotationProcessor {
         String templateType = getValueOrNull(tilesDef.templateType());
         if (templateType != null) {
             attribute.setRenderer(templateType);
-        } else if (getValueOrNull(tilesDef.extend()) != null && templateType == null) {
+        } else if (getValueOrNull(tilesDef.extend()) != null) {
             attribute.setRenderer(null);
         }
         return attribute;
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java
index 41b93a38f..205476467 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java
@@ -24,25 +24,31 @@ import ognl.OgnlRuntime;
 import ognl.PropertyAccessor;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.tiles.TilesContainer;
-import org.apache.tiles.definition.DefinitionsFactory;
-import org.apache.tiles.definition.pattern.DefinitionPatternMatcherFactory;
-import org.apache.tiles.definition.pattern.PatternDefinitionResolver;
-import org.apache.tiles.definition.pattern.PrefixedPatternDefinitionResolver;
-import org.apache.tiles.definition.pattern.regexp.RegexpDefinitionPatternMatcherFactory;
-import org.apache.tiles.definition.pattern.wildcard.WildcardDefinitionPatternMatcherFactory;
+import org.apache.tiles.api.TilesContainer;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.ApplicationResource;
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.render.BasicRendererFactory;
+import org.apache.tiles.request.render.ChainedDelegateRenderer;
+import org.apache.tiles.core.definition.DefinitionsFactory;
+import org.apache.tiles.core.definition.pattern.DefinitionPatternMatcherFactory;
+import org.apache.tiles.core.definition.pattern.PatternDefinitionResolver;
+import org.apache.tiles.core.definition.pattern.PrefixedPatternDefinitionResolver;
+import org.apache.tiles.core.definition.pattern.regexp.RegexpDefinitionPatternMatcherFactory;
+import org.apache.tiles.core.definition.pattern.wildcard.WildcardDefinitionPatternMatcherFactory;
+import org.apache.tiles.core.evaluator.AttributeEvaluatorFactory;
+import org.apache.tiles.core.evaluator.BasicAttributeEvaluatorFactory;
+import org.apache.tiles.core.evaluator.impl.DirectAttributeEvaluator;
+import org.apache.tiles.core.factory.BasicTilesContainerFactory;
+import org.apache.tiles.core.factory.TilesContainerFactoryException;
+import org.apache.tiles.core.impl.mgmt.CachingTilesContainer;
+import org.apache.tiles.core.locale.LocaleResolver;
+import org.apache.tiles.core.prepare.factory.PreparerFactory;
 import org.apache.tiles.el.ELAttributeEvaluator;
 import org.apache.tiles.el.JspExpressionFactoryFactory;
 import org.apache.tiles.el.ScopeELResolver;
 import org.apache.tiles.el.TilesContextBeanELResolver;
 import org.apache.tiles.el.TilesContextELResolver;
-import org.apache.tiles.evaluator.AttributeEvaluatorFactory;
-import org.apache.tiles.evaluator.BasicAttributeEvaluatorFactory;
-import org.apache.tiles.evaluator.impl.DirectAttributeEvaluator;
-import org.apache.tiles.factory.BasicTilesContainerFactory;
-import org.apache.tiles.factory.TilesContainerFactoryException;
-import org.apache.tiles.impl.mgmt.CachingTilesContainer;
-import org.apache.tiles.locale.LocaleResolver;
 import org.apache.tiles.ognl.AnyScopePropertyAccessor;
 import org.apache.tiles.ognl.DelegatePropertyAccessor;
 import org.apache.tiles.ognl.NestedObjectDelegatePropertyAccessor;
@@ -51,12 +57,6 @@ import org.apache.tiles.ognl.PropertyAccessorDelegateFactory;
 import org.apache.tiles.ognl.ScopePropertyAccessor;
 import org.apache.tiles.ognl.TilesApplicationContextNestedObjectExtractor;
 import org.apache.tiles.ognl.TilesContextPropertyAccessorDelegateFactory;
-import org.apache.tiles.preparer.factory.PreparerFactory;
-import org.apache.tiles.request.ApplicationContext;
-import org.apache.tiles.request.ApplicationResource;
-import org.apache.tiles.request.Request;
-import org.apache.tiles.request.render.BasicRendererFactory;
-import org.apache.tiles.request.render.ChainedDelegateRenderer;
 import org.apache.tiles.request.render.Renderer;
 
 import javax.el.ArrayELResolver;
@@ -81,7 +81,7 @@ import java.util.Set;
  * - S2 ro access Struts' ValueStack
  * - OGNL
  * - EL
- *
+ * <p>
  * If you need additional features create your own listener and factory,
  * you can base on code from Tiles' CompleteAutoloadTilesContainerFactory
  */
@@ -270,4 +270,4 @@ public class StrutsTilesContainerFactory extends BasicTilesContainerFactory {
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesInitializer.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesInitializer.java
index 5170003a4..cb471a1ab 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesInitializer.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesInitializer.java
@@ -20,11 +20,11 @@ package org.apache.struts2.tiles;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.tiles.definition.DefinitionsFactory;
-import org.apache.tiles.factory.AbstractTilesContainerFactory;
+import org.apache.tiles.core.definition.DefinitionsFactory;
+import org.apache.tiles.core.factory.AbstractTilesContainerFactory;
+import org.apache.tiles.core.startup.AbstractTilesInitializer;
 import org.apache.tiles.request.ApplicationContext;
 import org.apache.tiles.request.servlet.ServletApplicationContext;
-import org.apache.tiles.startup.AbstractTilesInitializer;
 
 import javax.servlet.ServletContext;
 
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
index 38e506552..5ebe36237 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
@@ -20,7 +20,7 @@ package org.apache.struts2.tiles;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
+import org.apache.tiles.core.startup.TilesInitializer;
 import org.apache.tiles.web.startup.AbstractTilesListener;
 
 /**
@@ -37,4 +37,4 @@ public class StrutsTilesListener extends AbstractTilesListener {
         LOG.info("Starting Struts Tiles 3 integration ...");
         return new StrutsTilesInitializer();
     }
-}
\ No newline at end of file
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesLocaleResolver.java b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesLocaleResolver.java
index 3e6524fa4..0d3d3ec33 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesLocaleResolver.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesLocaleResolver.java
@@ -24,7 +24,7 @@ import com.opensymphony.xwork2.config.ConfigurationException;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.struts2.ServletActionContext;
-import org.apache.tiles.locale.LocaleResolver;
+import org.apache.tiles.core.locale.LocaleResolver;
 import org.apache.tiles.request.Request;
 import org.apache.tiles.request.servlet.ServletUtil;
 
@@ -33,7 +33,7 @@ import java.util.Locale;
 
 public class StrutsTilesLocaleResolver implements LocaleResolver {
 
-    private static Logger LOG = LogManager.getLogger(StrutsTilesLocaleResolver.class);
+    private static final Logger LOG = LogManager.getLogger(StrutsTilesLocaleResolver.class);
 
     @Override
     public Locale resolveLocale(Request request) {
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/views/tiles/TilesResult.java b/plugins/tiles/src/main/java/org/apache/struts2/views/tiles/TilesResult.java
index 4c801f4aa..fefad2482 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/views/tiles/TilesResult.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/views/tiles/TilesResult.java
@@ -29,58 +29,41 @@ import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.result.ServletDispatcherResult;
 import org.apache.struts2.tiles.StrutsTilesAnnotationProcessor;
 import org.apache.struts2.tiles.annotation.TilesDefinition;
-import org.apache.tiles.Definition;
-import org.apache.tiles.TilesContainer;
-import org.apache.tiles.TilesException;
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.api.TilesContainer;
+import org.apache.tiles.api.TilesException;
 
 import com.opensymphony.xwork2.ActionInvocation;
-import org.apache.tiles.access.TilesAccess;
-import org.apache.tiles.mgmt.MutableTilesContainer;
+import org.apache.tiles.api.access.TilesAccess;
+import org.apache.tiles.api.mgmt.MutableTilesContainer;
 import org.apache.tiles.request.ApplicationContext;
 import org.apache.tiles.request.Request;
 import org.apache.tiles.request.servlet.ServletRequest;
 import org.apache.tiles.request.servlet.ServletUtil;
 
 /**
- * <!-- START SNIPPET: description -->
- * Renders a view using struts-tiles.
- * <!-- END SNIPPET: description -->
- *
- * <!-- START SNIPPET: webxml -->
- * In your web.xml file, you need to add a TilesListener.
- *
+ * Renders a view using struts-tiles. In your web.xml file, you need to add a TilesListener.
+ * <p>
  * &lt;listener&gt;
  *      &lt;listener-class&gt;org.apache.struts2.tiles.StrutsTilesListener&lt;/listener-class&gt;
  * &lt;/listener&gt;
- * <!-- END SNIPPET: webxml -->
- *
- * <!-- START SNIPPET: strutsxml -->
+ * <p>
  * In struts.xml, use type="tiles" on your &lt;result&gt;.
- *
+ * <p>
  * &lt;action name="editUser" class="userAction" method="edit"&gt;
  *      &lt;result name="success" type="tiles"&gt;userForm&lt;/result&gt;
  *      &lt;result name="input" type="tiles"&gt;userList&lt;/result&gt;
  * &lt;/action&gt;
- * <!-- END SNIPPET: strutsxml -->
- *
- *
- * <!-- START SNIPPET: packageconfig -->
- *
+ * <p>
  * Making this result type the default for the current package.
- *
+ * <p>
  * &lt;result-types&gt;
  *      &lt;result-type name="tiles"
  * class="org.apache.struts2.views.tiles.TilesResult" default="true" /&gt;
  * &lt;/result-types&gt;
- * <!-- END SNIPPET: packageconfig -->
- *
- *
- * <!-- START SNIPPET: tilesconfig -->
- * You have to configure tiles itself. Therefore you can add <code>tiles.xml</code> either 
+ * <p>
+ * You have to configure tiles itself. Therefore you can add <code>tiles.xml</code> either
  * to resources or WEB-INF. You may also use annotations like {@link TilesDefinition}.
- *
- * <!-- END SNIPPET: tilesconfig -->
- *
  */
 public class TilesResult extends ServletDispatcherResult {
 
@@ -114,8 +97,7 @@ public class TilesResult extends ServletDispatcherResult {
         if (StringUtils.isEmpty(location)) {
             LOG.trace("location not set -> action must have one @TilesDefinition");
             tilesDefinition = annotationProcessor.findAnnotation(action, null);
-            String tileName = StringUtils.isNotEmpty(tilesDefinition.name()) ? tilesDefinition.name() : actionName;
-            location = tileName;
+            location = StringUtils.isNotEmpty(tilesDefinition.name()) ? tilesDefinition.name() : actionName;
             LOG.debug("using new location name '{}' and @TilesDefinition '{}'", location, tilesDefinition);
         }
         setLocation(location);
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/Attribute.java b/plugins/tiles/src/main/java/org/apache/tiles/api/Attribute.java
new file mode 100644
index 000000000..60a09df9f
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/Attribute.java
@@ -0,0 +1,366 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.api;
+
+import org.apache.tiles.request.Request;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import static org.apache.tiles.api.CompareUtil.nullSafeEquals;
+import static org.apache.tiles.api.CompareUtil.nullSafeHashCode;
+
+/**
+ * Common implementation of attribute definition.
+ */
+public class Attribute implements Serializable, Cloneable {
+
+    /**
+     * The name of the template renderer.
+     */
+    private static final String TEMPLATE_RENDERER = "template";
+
+    /**
+     * The roles that can render this attribute.
+     * @since 2.0.6
+     */
+    protected Set<String> roles = null;
+
+    /**
+     * The value of the attribute.
+     */
+    protected Object value = null;
+
+    /**
+     * The expression to evaluate. Ignored if {@link #value} is not
+     * <code>null</code>.
+     *
+     * @since 2.2.0
+     */
+    protected Expression expressionObject = null;
+
+    /**
+     * The renderer name of the attribute. Default names are <code>string</code>,
+     * <code>template</code>, <code>definition</code>, <code>object</code>.
+     */
+    private String renderer = null;
+
+    /**
+     * Constructor.
+     *
+     */
+    public Attribute() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param value Object to store.
+     */
+    public Attribute(Object value) {
+        this.value = value;
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param attribute The attribute to copy from.
+     */
+    public Attribute(Attribute attribute) {
+        this.roles = attribute.roles;
+        this.value = attribute.getValue();
+        if (attribute.expressionObject != null) {
+            this.expressionObject = new Expression(attribute.expressionObject);
+        } else {
+            this.expressionObject = null;
+        }
+        this.renderer = attribute.renderer;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param value Object to store.
+     * @param role  Asociated role.
+     */
+    public Attribute(Object value, String role) {
+        this.value = value;
+        setRole(role);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param value Object to store. If specified, the <code>expression</code>
+     * parameter will be ignored.
+     * @param expression The expression to be evaluated. Ignored if the
+     * <code>value</code> is not null.
+     * @param role Associated role.
+     * @param rendererName The renderer name.
+     * @since 2.2.0
+     */
+    public Attribute(Object value, Expression expression, String role, String rendererName) {
+        this.value = value;
+        this.expressionObject = expression;
+        this.renderer = rendererName;
+        setRole(role);
+    }
+
+    /**
+     * Creates a template attribute, starting from the name of the template.
+     *
+     * @param template The template that will be rendered.
+     * @return The template attribute.
+     * @since 2.1.2
+     */
+    public static Attribute createTemplateAttribute(String template) {
+        Attribute attribute = new Attribute();
+        attribute.setValue(template);
+        attribute.setRenderer(TEMPLATE_RENDERER);
+        return attribute;
+    }
+
+    /**
+     * Creates a template attribute, starting from the name of the template.
+     *
+     * @param template The template that will be rendered.
+     * @param templateExpression The template expression that will be evaluated
+     * to a template.
+     * @param templateType The type, or renderer, of the template. If null, the
+     * default <code>template</code> will be used.
+     * @param role The comma-separated roles for which the template is
+     * authorized to be rendered.
+     * @return The template attribute.
+     * @since 2.2.2
+     */
+    public static Attribute createTemplateAttribute(String template,
+            String templateExpression, String templateType, String role) {
+        Attribute templateAttribute = createTemplateAttribute(template);
+        templateAttribute.setRole(role);
+        if (templateType != null) {
+            templateAttribute.setRenderer(templateType);
+        }
+        templateAttribute
+                .setExpressionObject(Expression
+                        .createExpressionFromDescribedExpression(templateExpression));
+        return templateAttribute;
+    }
+
+    /**
+     * Get role.
+     * @return the name of the required role(s)
+     */
+    public String getRole() {
+        String retValue = null;
+
+        if (roles != null && !roles.isEmpty()) {
+            StringBuilder builder = new StringBuilder();
+            Iterator<String> roleIt = roles.iterator();
+            if (roleIt.hasNext()) {
+                builder.append(roleIt.next());
+                while (roleIt.hasNext()) {
+                    builder.append(",");
+                    builder.append(roleIt.next());
+                }
+                retValue = builder.toString();
+            }
+        }
+
+        return retValue;
+    }
+
+    /**
+     * Returns the roles that can render this attribute.
+     *
+     * @return The enabled roles.
+     * @since 2.0.6
+     */
+    public Set<String> getRoles() {
+        return roles;
+    }
+
+    /**
+     * Set role.
+     *
+     * @param role Associated role.
+     */
+    public void setRole(String role) {
+        if (role != null && role.trim().length() > 0) {
+            String[] rolesStrings = role.split("\\s*,\\s*");
+            roles = new HashSet<>();
+            Collections.addAll(roles, rolesStrings);
+        } else {
+            roles = null;
+        }
+    }
+
+    /**
+     * Sets the roles that can render this attribute.
+     *
+     * @param roles The enabled roles.
+     * @since 2.0.6
+     */
+    public void setRoles(Set<String> roles) {
+        this.roles = roles;
+    }
+
+    /**
+     * Get value.
+     * @return the value
+     */
+    public Object getValue() {
+        return value;
+    }
+
+    /**
+     * Set value.
+     *
+     * @param value New value.
+     */
+    public void setValue(Object value) {
+        this.value = value;
+    }
+
+    /**
+     * Returns The expression to evaluate. Ignored if {@link #value} is not
+     * <code>null</code>.
+     *
+     * @return The expression to be evaluated.
+     * @since 2.2.0
+     */
+    public Expression getExpressionObject() {
+        return expressionObject;
+    }
+
+    /**
+     * Sets The expression to evaluate. Ignored if {@link #value} is not
+     * <code>null</code>.
+     *
+     * @param expressionObject The expression to be evaluated.
+     * @since 2.2.0
+     */
+    public void setExpressionObject(Expression expressionObject) {
+        this.expressionObject = expressionObject;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        if (value != null) {
+            return value.toString();
+        }
+        return null;
+    }
+
+    /**
+     * Returns the renderer name to use.
+     *
+     * @return The renderer name.
+     * @since 2.1.0
+     */
+    public String getRenderer() {
+        return renderer;
+    }
+
+    /**
+     * Sets the renderer name to use.
+     *
+     * @param rendererName The renderer.
+     * @since 2.1.0
+     */
+    public void setRenderer(String rendererName) {
+        this.renderer = rendererName;
+    }
+
+    /**
+     * Inherits an attribute, i.e. overwrites null properties with the ones
+     * provided by the attribute.
+     *
+     * @param attribute The attribute to inherit.
+     * @since 2.1.2
+     */
+    public void inherit(Attribute attribute) {
+        if (value == null) {
+            value = attribute.getValue();
+        }
+        Expression targetExpressionObject = attribute.getExpressionObject();
+        if (targetExpressionObject != null
+                && (expressionObject == null || expressionObject
+                        .getExpression() == null)) {
+            expressionObject = new Expression(targetExpressionObject);
+        }
+        if (roles == null || roles.isEmpty()) {
+            roles = attribute.getRoles();
+        }
+        if (renderer == null) {
+            renderer = attribute.getRenderer();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object obj) {
+        Attribute attribute = (Attribute) obj;
+        return nullSafeEquals(value, attribute.value)
+                && nullSafeEquals(renderer, attribute.renderer)
+                && nullSafeEquals(roles, attribute.roles)
+                && nullSafeEquals(expressionObject, attribute.expressionObject);
+    }
+
+    /**
+     * Checks if the current user can use this attribute.
+     *
+     * @param request The request context.
+     * @return <code>true</code> if the current user can see this attribute.
+     * @since 3.0.0
+     */
+    public boolean isPermitted(Request request) {
+        if (roles == null || roles.isEmpty()) {
+            return true;
+        }
+
+        boolean retValue = false;
+
+        for (Iterator<String> roleIt = roles.iterator(); roleIt.hasNext()
+                && !retValue;) {
+            retValue = request.isUserInRole(roleIt.next());
+        }
+
+        return retValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return nullSafeHashCode(value) + nullSafeHashCode(renderer)
+                + nullSafeHashCode(roles) + nullSafeHashCode(expressionObject);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Attribute clone() {
+        return new Attribute(this);
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/AttributeContext.java b/plugins/tiles/src/main/java/org/apache/tiles/api/AttributeContext.java
new file mode 100644
index 000000000..23e4fc124
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/AttributeContext.java
@@ -0,0 +1,164 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.api;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Encapsulation of the current state of execution.
+ *
+ * @since Tiles 2.0
+ * @version $Rev$ $Date$
+ */
+public interface AttributeContext {
+
+    /**
+     * Returns the attribute that will be used to render a template.
+     *
+     * @return The template attribute.
+     * @since 2.1.2
+     */
+    Attribute getTemplateAttribute();
+
+    /**
+     * Sets the template attribute, that will be used to render the template
+     * page.
+     *
+     * @param templateAttribute The template attribute.
+     * @since 2.1.2
+     */
+    void setTemplateAttribute(Attribute templateAttribute);
+
+    /**
+     * Get associated preparer instance.
+     *
+     * @return The preparer name.
+     * @since 2.1.0
+     */
+    String getPreparer();
+
+    /**
+     * Set associated preparer instance.
+     *
+     * @param url The preparer name.
+     * @since 2.1.0
+     */
+    void setPreparer(String url);
+
+    /**
+     * Add all attributes to the context.
+     *
+     * @param newAttributes the attributes to be added.
+     */
+    void addAll(Map<String, Attribute> newAttributes);
+
+    /**
+     * Copies the cascaded attributes to this attribute context.
+     *
+     * @param parent The parent context to be used.
+     * @since 2.1.0
+     */
+    void inheritCascadedAttributes(AttributeContext parent);
+
+    /**
+     * Copies all missing attributes from the <code>parent</code> attribute
+     * context to this one.
+     *
+     * @param parent The attribute context to copy attributes from.
+     * @since 2.1.0
+     */
+    void inherit(AttributeContext parent);
+
+    /**
+     * Retrieve the named attribute, either cascaded or not.
+     *
+     * @param name key name for the attribute.
+     * @return Attribute associated with the given name.
+     */
+    Attribute getAttribute(String name);
+
+    /**
+     * Retrieve the attribute that has been defined in this context (i.e. not
+     * cascaded).
+     *
+     * @param name key name for the attribute.
+     * @return Attribute The local attribute associated with the given name, if
+     * present, or <code>null</code> otherwise.
+     * @since 2.1.0
+     */
+    Attribute getLocalAttribute(String name);
+
+    /**
+     * Retrieve the attribute that has been cascaded at upper levels.
+     *
+     * @param name key name for the attribute.
+     * @return Attribute The cascaded attribute associated with the given name,
+     * if present, or <code>null</code> otherwise.
+     * @since 2.1.0
+     */
+    Attribute getCascadedAttribute(String name);
+
+    /**
+     * Returns the names of the local attributes, i.e. the one that have not
+     * been cascaded.
+     *
+     * @return The local attribute names.
+     * @since 2.1.0
+     */
+    Set<String> getLocalAttributeNames();
+
+    /**
+     * Returns the names of the cascaded attributes.
+     *
+     * @return The cascaded attribute names.
+     * @since 2.1.0
+     */
+    Set<String> getCascadedAttributeNames();
+
+    /**
+     * Add the specified attribute. The attribute value will be available only
+     * in the current context, i.e. it is like calling
+     * {@link AttributeContext#putAttribute(String, Attribute, boolean)} with
+     * <code>cascade = false</code>.
+     *
+     * @param name name of the attribute
+     * @param value value of the attribute
+     */
+    void putAttribute(String name, Attribute value);
+
+    /**
+     * Add the specified attribute.
+     *
+     * @param name name of the attribute
+     * @param value value of the attribute
+     * @param cascade If <code>true</code>, the attribute value will be
+     * available in all nested contexts. If <code>false</code>, it will be
+     * available only in the current context.
+     * @since 2.1.0
+     */
+    void putAttribute(String name, Attribute value, boolean cascade);
+
+    /**
+     * Clear the attributes.
+     */
+    void clear();
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/BasicAttributeContext.java b/plugins/tiles/src/main/java/org/apache/tiles/api/BasicAttributeContext.java
new file mode 100644
index 000000000..17d650754
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/BasicAttributeContext.java
@@ -0,0 +1,434 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.api;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.tiles.api.CompareUtil.nullSafeEquals;
+import static org.apache.tiles.api.CompareUtil.nullSafeHashCode;
+
+/**
+ * Basic implementation for <code>AttributeContext</code>.
+ *
+ * @since 2.1.0
+ */
+public class BasicAttributeContext implements AttributeContext, Serializable {
+
+    /**
+     * The template attribute, to render a template.
+     *
+     * @since 2.1.2
+     */
+    protected Attribute templateAttribute;
+
+    /**
+     * Associated ViewPreparer URL or classname, if defined.
+     *
+     * @since 2.1.0
+     */
+    protected String preparer = null;
+
+    /**
+     * Template attributes.
+     * @since 2.1.0
+     */
+    protected Map<String, Attribute> attributes = null;
+
+    /**
+     * Cascaded template attributes.
+     * @since 2.1.0
+     */
+    protected Map<String, Attribute> cascadedAttributes = null;
+
+    /**
+     * Constructor.
+     *
+     * @since 2.1.0
+     */
+    public BasicAttributeContext() {
+    }
+
+    /**
+     * Constructor.
+     * Create a context and set specified attributes.
+     *
+     * @param attributes Attributes to initialize context.
+     * @since 2.1.0
+     */
+    public BasicAttributeContext(Map<String, Attribute> attributes) {
+        if (attributes != null) {
+            this.attributes = deepCopyAttributeMap(attributes);
+        }
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param context The constructor to copy.
+     * @since 2.1.0
+     */
+    public BasicAttributeContext(AttributeContext context) {
+        if (context instanceof BasicAttributeContext) {
+            copyBasicAttributeContext((BasicAttributeContext) context);
+        } else {
+            Attribute parentTemplateAttribute = context.getTemplateAttribute();
+            if (parentTemplateAttribute != null) {
+                this.templateAttribute = new Attribute(parentTemplateAttribute);
+            }
+            this.preparer = context.getPreparer();
+            this.attributes = new HashMap<>();
+            Set<String> parentAttributeNames = context.getLocalAttributeNames();
+            if (parentAttributeNames != null) {
+                for (String name : parentAttributeNames) {
+                    attributes.put(name, new Attribute(context.getLocalAttribute(name)));
+                }
+            }
+            inheritCascadedAttributes(context);
+        }
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param context The constructor to copy.
+     * @since 2.1.0
+     */
+    public BasicAttributeContext(BasicAttributeContext context) {
+        copyBasicAttributeContext(context);
+    }
+
+    /** {@inheritDoc} */
+    public Attribute getTemplateAttribute() {
+        return templateAttribute;
+    }
+
+    /** {@inheritDoc} */
+    public void setTemplateAttribute(Attribute templateAttribute) {
+        this.templateAttribute = templateAttribute;
+    }
+
+    /** {@inheritDoc} */
+    public String getPreparer() {
+        return preparer;
+    }
+
+    /** {@inheritDoc} */
+    public void setPreparer(String url) {
+        this.preparer = url;
+    }
+
+    /** {@inheritDoc} */
+    public void inheritCascadedAttributes(AttributeContext context) {
+        if (context instanceof BasicAttributeContext) {
+            copyCascadedAttributes((BasicAttributeContext) context);
+        } else {
+            this.cascadedAttributes = new HashMap<>();
+            Set<String> parentAttributeNames = context.getCascadedAttributeNames();
+            if (parentAttributeNames != null) {
+                for (String name : parentAttributeNames) {
+                    cascadedAttributes.put(name, new Attribute(context
+                            .getCascadedAttribute(name)));
+                }
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void inherit(AttributeContext parent) {
+        if (parent instanceof BasicAttributeContext) {
+            inherit((BasicAttributeContext) parent);
+        } else {
+            // Inheriting template, roles and preparer.
+            Attribute parentTemplateAttribute = parent.getTemplateAttribute();
+            inheritParentTemplateAttribute(parentTemplateAttribute);
+            if (preparer == null) {
+                preparer = parent.getPreparer();
+            }
+
+            // Inheriting attributes.
+            Set<String> names = parent.getCascadedAttributeNames();
+            if (names != null && !names.isEmpty()) {
+                for (String name : names) {
+                    Attribute attribute = parent.getCascadedAttribute(name);
+                    Attribute destAttribute = getCascadedAttribute(name);
+                    if (destAttribute == null) {
+                        putAttribute(name, attribute, true);
+                    } else if (attribute instanceof ListAttribute
+                            && destAttribute instanceof ListAttribute
+                            && ((ListAttribute) destAttribute).isInherit()) {
+                        ((ListAttribute) destAttribute).inherit((ListAttribute) attribute);
+                    }
+                }
+            }
+            names = parent.getLocalAttributeNames();
+            if (names != null && !names.isEmpty()) {
+                for (String name : names) {
+                    Attribute attribute = parent.getLocalAttribute(name);
+                    Attribute destAttribute = getLocalAttribute(name);
+                    if (destAttribute == null) {
+                        putAttribute(name, attribute, false);
+                    } else if (attribute instanceof ListAttribute
+                            && destAttribute instanceof ListAttribute
+                            && ((ListAttribute) destAttribute).isInherit()) {
+                        ((ListAttribute) destAttribute).inherit((ListAttribute) attribute);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Inherits the attribute context, inheriting, i.e. copying if not present,
+     * the attributes.
+     *
+     * @param parent The attribute context to inherit.
+     * @since 2.1.0
+     */
+    public void inherit(BasicAttributeContext parent) {
+        // Set template, roles and preparer if not set.
+        inheritParentTemplateAttribute(parent.getTemplateAttribute());
+        if (preparer == null) {
+            preparer = parent.preparer;
+        }
+
+        // Sets attributes.
+        cascadedAttributes = addMissingAttributes(parent.cascadedAttributes,
+                cascadedAttributes);
+        attributes = addMissingAttributes(parent.attributes, attributes);
+    }
+
+    /**
+     * Add all attributes to this context.
+     * Copies all of the mappings from the specified attribute map to this context.
+     * New attribute mappings will replace any mappings that this context had for any of the keys
+     * currently in the specified attribute map.
+     *
+     * @param newAttributes Attributes to add.
+     * @since 2.1.0
+     */
+    public void addAll(Map<String, Attribute> newAttributes) {
+        if (newAttributes == null) {
+            return;
+        }
+
+        if (attributes == null) {
+            attributes = new HashMap<>(newAttributes);
+            return;
+        }
+
+        attributes.putAll(newAttributes);
+    }
+
+    /** {@inheritDoc} */
+    public Attribute getAttribute(String name) {
+        Attribute retValue = null;
+        if (attributes != null) {
+            retValue = attributes.get(name);
+        }
+
+        if (retValue == null && cascadedAttributes != null) {
+            retValue = cascadedAttributes.get(name);
+        }
+
+        return retValue;
+    }
+
+    /** {@inheritDoc} */
+    public Attribute getLocalAttribute(String name) {
+        if (attributes == null) {
+            return null;
+        }
+
+        return attributes.get(name);
+    }
+
+    /** {@inheritDoc} */
+    public Attribute getCascadedAttribute(String name) {
+        if (cascadedAttributes == null) {
+            return null;
+        }
+
+        return cascadedAttributes.get(name);
+    }
+
+    /** {@inheritDoc} */
+    public Set<String> getLocalAttributeNames() {
+        if (attributes != null && !attributes.isEmpty()) {
+            return attributes.keySet();
+        }
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public Set<String> getCascadedAttributeNames() {
+        if (cascadedAttributes != null && !cascadedAttributes.isEmpty()) {
+            return cascadedAttributes.keySet();
+        }
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public void putAttribute(String name, Attribute value) {
+        if (attributes == null) {
+            attributes = new HashMap<>();
+        }
+
+        attributes.put(name, value);
+    }
+
+    /** {@inheritDoc} */
+    public void putAttribute(String name, Attribute value, boolean cascade) {
+        Map<String, Attribute> mapToUse;
+        if (cascade) {
+            if (cascadedAttributes == null) {
+                cascadedAttributes = new HashMap<>();
+            }
+            mapToUse = cascadedAttributes;
+        } else {
+            if (attributes == null) {
+                attributes = new HashMap<>();
+            }
+            mapToUse = attributes;
+        }
+        mapToUse.put(name, value);
+    }
+
+    /** {@inheritDoc} */
+    public void clear() {
+        templateAttribute = null;
+        preparer = null;
+        attributes.clear();
+        cascadedAttributes.clear();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object obj) {
+        BasicAttributeContext bac = (BasicAttributeContext) obj;
+        return nullSafeEquals(templateAttribute, bac.templateAttribute)
+                && nullSafeEquals(preparer, bac.preparer)
+                && nullSafeEquals(attributes, bac.attributes)
+                && nullSafeEquals(cascadedAttributes, bac.cascadedAttributes);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return nullSafeHashCode(templateAttribute) + nullSafeHashCode(preparer)
+                + nullSafeHashCode(attributes)
+                + nullSafeHashCode(cascadedAttributes);
+    }
+
+    /**
+     * Inherits the parent template attribute.
+     *
+     * @param parentTemplateAttribute The parent template attribute.
+     */
+    private void inheritParentTemplateAttribute(
+            Attribute parentTemplateAttribute) {
+        if (parentTemplateAttribute != null) {
+            if (templateAttribute == null) {
+                templateAttribute = new Attribute(parentTemplateAttribute);
+            } else {
+                templateAttribute.inherit(parentTemplateAttribute);
+            }
+        }
+    }
+
+    /**
+     * Copies a BasicAttributeContext in an easier way.
+     *
+     * @param context The context to copy.
+     */
+    private void copyBasicAttributeContext(BasicAttributeContext context) {
+        Attribute parentTemplateAttribute = context.getTemplateAttribute();
+        if (parentTemplateAttribute != null) {
+            this.templateAttribute = new Attribute(parentTemplateAttribute);
+        }
+        preparer = context.preparer;
+        if (context.attributes != null && !context.attributes.isEmpty()) {
+            attributes = deepCopyAttributeMap(context.attributes);
+        }
+        copyCascadedAttributes(context);
+    }
+
+    /**
+     * Copies the cascaded attributes to the current context.
+     *
+     * @param context The context to copy from.
+     */
+    private void copyCascadedAttributes(BasicAttributeContext context) {
+        if (context.cascadedAttributes != null
+                && !context.cascadedAttributes.isEmpty()) {
+            cascadedAttributes = deepCopyAttributeMap(context.cascadedAttributes);
+        }
+    }
+
+    /**
+     * Adds missing attributes to the destination map.
+     *
+     * @param source The source attribute map.
+     * @param destination The destination attribute map.
+     * @return The destination attribute map if not null, a new one otherwise.
+     */
+    private Map<String, Attribute> addMissingAttributes(Map<String, Attribute> source, Map<String, Attribute> destination) {
+        if (source != null && !source.isEmpty()) {
+            if (destination == null) {
+                destination = new HashMap<>();
+            }
+            for (Map.Entry<String, Attribute> entry : source.entrySet()) {
+                String key = entry.getKey();
+                Attribute destAttribute = destination.get(key);
+                if (destAttribute == null) {
+                    destination.put(key, entry.getValue());
+                } else if (destAttribute instanceof ListAttribute
+                        && entry.getValue() instanceof ListAttribute
+                        && ((ListAttribute) destAttribute).isInherit()) {
+                    ((ListAttribute) destAttribute)
+                            .inherit((ListAttribute) entry.getValue());
+                }
+            }
+        }
+
+        return destination;
+    }
+
+    /**
+     * Deep copies the attribute map, by creating clones (using copy
+     * constructors) of the attributes.
+     *
+     * @param attributes The attribute map to copy.
+     * @return The copied map.
+     */
+    private Map<String, Attribute> deepCopyAttributeMap(Map<String, Attribute> attributes) {
+        Map<String, Attribute> retValue = new HashMap<>(attributes.size());
+        for (Map.Entry<String, Attribute> entry : attributes.entrySet()) {
+            Attribute toCopy = entry.getValue();
+            if (toCopy != null) {
+                retValue.put(entry.getKey(), toCopy.clone());
+            }
+        }
+        return retValue;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/CompareUtil.java b/plugins/tiles/src/main/java/org/apache/tiles/api/CompareUtil.java
new file mode 100644
index 000000000..cf9b643a6
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/CompareUtil.java
@@ -0,0 +1,67 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.api;
+
+/**
+ * Utilities to work with comparator between objects.
+ *
+ * @version $Rev$ $Date$
+ * @since 2.2.0
+ */
+public final class CompareUtil {
+
+    /**
+     * Private constructor to avoid instantiation.
+     */
+    private CompareUtil() { }
+
+    /**
+     * Checks if two objects (eventually null) are the same. They are considered the same
+     * even if they are both null.
+     *
+     * @param obj1 The first object to check.
+     * @param obj2 The second object to check.
+     * @return <code>true</code> if the objects are the same.
+     * @since 2.2.0
+     */
+    public static boolean nullSafeEquals(Object obj1, Object obj2) {
+        if (obj1 != null) {
+            return obj1.equals(obj2);
+        }
+        return obj2 == null;
+    }
+
+    /**
+     * Returns <code>0</code> if the object is null, the hash code of the object
+     * otherwise.
+     *
+     * @param obj The object from which the hash code must be calculated.
+     * @return The hash code.
+     * @since 2.2.0
+     */
+    public static int nullSafeHashCode(Object obj) {
+        if (obj != null) {
+            return obj.hashCode();
+        }
+        return 0;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/Definition.java b/plugins/tiles/src/main/java/org/apache/tiles/api/Definition.java
new file mode 100644
index 000000000..0843f1aca
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/Definition.java
@@ -0,0 +1,162 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.api;
+
+import java.util.Map;
+
+import static org.apache.tiles.api.CompareUtil.nullSafeEquals;
+import static org.apache.tiles.api.CompareUtil.nullSafeHashCode;
+
+/**
+ * A definition, i.e. a template with (completely or not) filled attributes.
+ * Attributes of a template can be defined with the help of this class.<br>
+ * It can be used as a data transfer object used for registering new
+ * definitions with the Container.
+ *
+ * @since Tiles 2.0
+ */
+public class Definition extends BasicAttributeContext {
+    /**
+     * Extends attribute value.
+     */
+    protected String inherit;
+    /**
+     * Definition name.
+     */
+    protected String name = null;
+
+    /**
+     * Constructor.
+     */
+    public Definition() {
+    }
+
+    /**
+     * Copy Constructor.
+     * Create a new definition initialized with parent definition.
+     * Do a shallow copy : attributes are shared between copies, but not the Map
+     * containing attributes.
+     *
+     * @param definition The definition to copy.
+     */
+    public Definition(Definition definition) {
+        super(definition);
+        this.name = definition.name;
+        this.inherit = definition.inherit;
+    }
+
+    /**
+     * Constructor.
+     * @param name The name of the definition.
+     * @param templateAttribute The template attribute of the definition.
+     * @param attributes The attribute map of the definition.
+     *
+     * @since 2.1.2
+     */
+    public Definition(String name, Attribute templateAttribute,
+                               Map<String, Attribute> attributes) {
+        super(attributes);
+        this.name = name;
+        this.templateAttribute = templateAttribute;
+    }
+
+    /**
+     * Access method for the name property.
+     *
+     * @return the current value of the name property
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets the value of the name property.
+     *
+     * @param aName the new value of the name property
+     */
+    public void setName(String aName) {
+        name = aName;
+    }
+
+    /**
+     * Set extends.
+     *
+     * @param name Name of the extended definition.
+     */
+    public void setExtends(String name) {
+        inherit = name;
+    }
+
+    /**
+     * Get extends.
+     *
+     * @return Name of the extended definition.
+     */
+    public String getExtends() {
+        return inherit;
+    }
+
+
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object obj) {
+        Definition def = (Definition) obj;
+        return nullSafeEquals(name, def.name)
+                && nullSafeEquals(inherit, def.inherit) && super.equals(def);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return nullSafeHashCode(name) + nullSafeHashCode(inherit)
+                + super.hashCode();
+    }
+
+    /**
+     * Get extends flag.
+     *
+     * @return <code>true</code> if this definition extends another.
+     */
+    public boolean isExtending() {
+        return inherit != null;
+    }
+
+    /**
+     * Returns a description of the attributes.
+     *
+     * @return A string representation of the content of this definition.
+     */
+    @Override
+    public String toString() {
+        return "{name="
+            + name
+            + ", template="
+            + (templateAttribute != null ? templateAttribute.getValue() : "<null>")
+            + ", role="
+            + (templateAttribute != null ? templateAttribute.getRoles() : "<null>")
+            + ", preparerInstance="
+            + preparer
+            + ", attributes="
+            + attributes
+            + "}";
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/Expression.java b/plugins/tiles/src/main/java/org/apache/tiles/api/Expression.java
new file mode 100644
index 000000000..439a2c2e8
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/Expression.java
@@ -0,0 +1,157 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.api;
+
+import static org.apache.tiles.api.CompareUtil.nullSafeEquals;
+import static org.apache.tiles.api.CompareUtil.nullSafeHashCode;
+
+/**
+ * It is an expression, along with the expression language (e.g. EL, MVEL, OGNL)
+ * it is expressed with.
+ *
+ * @since 2.2.0
+ */
+public class Expression {
+
+    /**
+     * The expression itself.
+     */
+    private final String expression;
+
+    /**
+     * The language of the expression.
+     */
+    private final String language;
+
+    /**
+     * Constructor.
+     *
+     * @param expression The expression itself.
+     * @param language The language of the expression.
+     * @since 2.2.0
+     */
+    public Expression(String expression, String language) {
+        this.expression = expression;
+        this.language = language;
+    }
+
+    /**
+     * Constructor, using the default (i.e. <code>null</code>) language.
+     *
+     * @param expression The expression itself.
+     * @since 2.2.0
+     */
+    public Expression(String expression) {
+        this(expression, null);
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param toCopy The expression to copy.
+     * @since 2.2.0
+     */
+    public Expression(Expression toCopy) {
+        this.expression = toCopy.expression;
+        this.language = toCopy.language;
+    }
+
+    /**
+     * Creates an Expression object from a string in the form
+     * <code>LANGUAGE:EXPRESSION</code>.
+     *
+     * @param describedExpression The expression in the form
+     * <code>LANGUAGE:EXPRESSION</code>. The LANGUAGE part should be expressed
+     * only with letters and numbers.
+     * @return The created object, or <code>null</code> if the expression is null.
+     * @since 2.2.0
+     */
+    public static Expression createExpressionFromDescribedExpression(String describedExpression) {
+        if (describedExpression != null) {
+            String language = null;
+            String expression = describedExpression;
+            if (describedExpression.matches("[a-zA-Z0-9]+:.+")) {
+                language = describedExpression.substring(0, describedExpression.indexOf(':'));
+                expression = describedExpression.substring(describedExpression.indexOf(':') + 1);
+            }
+            return new Expression(expression, language);
+        }
+
+        return null;
+    }
+
+    /**
+     * Creates an Expression object from the expression and its language.
+     *
+     * @param expression The expression itself.
+     * @param language The language of the expression.
+     * @return The created object, or <code>null</code> if the expression is null.
+     * @since 2.2.0
+     */
+    public static Expression createExpression(String expression, String language) {
+        if (expression != null) {
+            return new Expression(expression, language);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the expression string.
+     *
+     * @return The expression itself.
+     * @since 2.2.0
+     */
+    public String getExpression() {
+        return expression;
+    }
+
+    /**
+     * Returns the language in which the expression is expressed.
+     *
+     * @return The expression language.
+     * @since 2.2.0
+     */
+    public String getLanguage() {
+        return language;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object obj) {
+        Expression exp = (Expression) obj;
+        return nullSafeEquals(expression, exp.expression)
+                && nullSafeEquals(language, exp.language);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return nullSafeHashCode(expression) + nullSafeHashCode(language);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return (language == null ? "DEFAULT" : language) + ":" + expression;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/ListAttribute.java b/plugins/tiles/src/main/java/org/apache/tiles/api/ListAttribute.java
new file mode 100644
index 000000000..d618b66ec
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/ListAttribute.java
@@ -0,0 +1,166 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.api;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An attribute as a <code>List</code>.
+ * This attribute associates a name with a list. The list can be found by the
+ * property name.
+ * Elements in list are retrieved using List methods.
+ * This class is used to read configuration files.
+ *
+ * @since 2.1.0
+ */
+public class ListAttribute extends Attribute {
+
+    /**
+     * If true, the attribute will put the elements of the attribute with the
+     * same name of the parent definition before the ones specified here. By
+     * default, it is 'false'.
+     */
+    private boolean inherit = false;
+
+    /**
+     * Constructor.
+     *
+     * @since 2.1.0
+     */
+    public ListAttribute() {
+        setValue(new ArrayList<Object>());
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param toCopy The list attribute to copy.
+     * @since 2.1.3
+     */
+    public ListAttribute(ListAttribute toCopy) {
+        super(toCopy);
+        List<Attribute> attributesToCopy = toCopy.getValue();
+        if (attributesToCopy != null) {
+            List<Attribute> attributes = new ArrayList<>(attributesToCopy.size());
+            for (Attribute attribute : attributesToCopy) {
+                if (attribute != null) {
+                    attributes.add(attribute.clone());
+                } else {
+                    attributes.add(null);
+                }
+            }
+            setValue(attributes);
+        }
+        this.inherit = toCopy.inherit;
+    }
+
+    /**
+     * Sets the list of the attributes that are elements of this attribute.
+     *
+     * @param attributes The attributes.
+     * @since 3.0.0
+     */
+    public void setValue(List<Attribute> attributes) {
+        super.setValue(attributes);
+    }
+
+    /**
+     * Returns the list of the attributes that are elements of this attribute.
+     *
+     * @return The attributes.
+     * @since 3.0.0
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public List<Attribute> getValue() {
+        return (List<Attribute>) super.getValue();
+    }
+
+    /**
+     * Add an element in list.
+     * We use a property to avoid rewriting a new class.
+     *
+     * @param element XmlAttribute to add.
+     * @since 2.1.0
+     */
+    public void add(Attribute element) {
+        getValue().add(element);
+    }
+
+    /**
+     * If true, the attribute will put the elements of the attribute with the
+     * same name of the parent definition before the ones specified here. By
+     * default, it is 'false'
+     *
+     * @param inherit The "inherit" value.
+     * @since 2.1.0
+     */
+    public void setInherit(boolean inherit) {
+        this.inherit = inherit;
+    }
+
+    /**
+     * If true, the attribute will put the elements of the attribute with the
+     * same name of the parent definition before the ones specified here. By
+     * default, it is 'false'
+     *
+     * @return inherit The "inherit" value.
+     * @since 2.1.0
+     */
+    public boolean isInherit() {
+        return inherit;
+    }
+
+    /**
+     * Inherits elements present in a "parent" list attribute. The elements will
+     * be put before the ones already present.
+     *
+     * @param parent The parent list attribute.
+     * @since 2.1.0
+     */
+    @SuppressWarnings("unchecked")
+    public void inherit(ListAttribute parent) {
+        List<Attribute> tempList = new ArrayList<>();
+        tempList.addAll((List<Attribute>) parent.value);
+        tempList.addAll((List<Attribute>) value);
+        setValue(tempList);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object obj) {
+        ListAttribute attribute = (ListAttribute) obj;
+        return super.equals(attribute) && this.inherit == attribute.inherit;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return super.hashCode() + Boolean.valueOf(inherit).hashCode();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ListAttribute clone() {
+        return new ListAttribute(this);
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/api/NoSuchContainerException.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/api/NoSuchContainerException.java
index 38e506552..858db324d 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/NoSuchContainerException.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+package org.apache.tiles.api;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * Indicates that a keyed container has not been found.
  *
- * @since Struts 2.0.2
+ * @since 2.1.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
+public class NoSuchContainerException extends TilesException {
 
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @since 2.1.0
+     */
+    public NoSuchContainerException(String message) {
+        super(message);
     }
-}
\ No newline at end of file
+
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/TilesContainer.java b/plugins/tiles/src/main/java/org/apache/tiles/api/TilesContainer.java
new file mode 100644
index 000000000..20199772d
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/TilesContainer.java
@@ -0,0 +1,139 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.api;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.Request;
+
+import java.io.IOException;
+
+/**
+ * An encapsulation of the Tiles framework.  This interface is
+ * used to expose tiles features to frameworks which leverage
+ * it as a plugin.  It can alternately be used by web applications
+ * which would like a programmatic interface.
+ *
+ * @since 2.0
+ */
+public interface TilesContainer {
+
+    /**
+     * Retrieve the container's context.
+     *
+     * @return current application context
+     */
+    ApplicationContext getApplicationContext();
+
+    /**
+     * Retrieve the attribute context of the current request.
+     * @param request The request.
+     * @return map of the attributes in the current attribute context.
+     */
+    AttributeContext getAttributeContext(Request request);
+
+    /**
+     * Starts a new context, where attribute values are stored independently of others.<br>
+     * When the use of the contexts is finished, call{@link TilesContainer#endContext(Request)}
+     *
+     * @param request The request.
+     * @return The newly created context.
+     */
+    AttributeContext startContext(Request request);
+
+    /**
+     * Ends a context, where attribute values are stored independently of others.<br>
+     * It must be called after a {@link TilesContainer#startContext(Request)} call.
+     *
+     * @param request The request.
+     */
+    void endContext(Request request);
+
+    /**
+     * Renders the current context, as it is.
+     * @param request The request.
+     *
+     * @since 2.1.0
+     */
+    void renderContext(Request request);
+
+    /**
+     * Executes a preparer.
+     *
+     * @param preparer The name of the preparer to execute.
+     * @param request The request.
+     */
+    void prepare(String preparer, Request request);
+
+    /**
+     * Render the given tiles request.
+     *
+     * @param definition the current definition.
+     * @param request The request.
+     */
+    void render(String definition, Request request);
+
+    /**
+     * Renders the specified definition.
+     * @param definition The definition to render.
+     * @param request The request context.
+     */
+    void render(Definition definition, Request request);
+
+    /**
+     * Render the given Attribute.
+     *
+     * @param attribute The attribute to render.
+     * @param request The request.
+     * @throws IOException If something goes wrong during writing to the output.
+     * @since 2.1.2
+     */
+    void render(Attribute attribute, Request request)
+        throws IOException;
+
+    /**
+     * Evaluates the given attribute.
+     *
+     * @param attribute The attribute to evaluate.
+     * @param request The request.
+     * @return The evaluated object.
+     * @since 2.1.0
+     */
+    Object evaluate(Attribute attribute, Request request);
+
+    /**
+     * Returns a definition specifying its name.
+     *
+     * @param definitionName The name of the definition to find.
+     * @param request The request context.
+     * @return The definition, if found.
+     */
+    Definition getDefinition(String definitionName,
+                             Request request);
+
+    /**
+     * Determine whether the definition exists.
+     *
+     * @param definition the name of the definition.
+     * @param request The request.
+     * @return true if the definition is found.
+     */
+    boolean isValidDefinition(String definition, Request request);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/TilesContainerWrapper.java b/plugins/tiles/src/main/java/org/apache/tiles/api/TilesContainerWrapper.java
new file mode 100644
index 000000000..28687c194
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/TilesContainerWrapper.java
@@ -0,0 +1,109 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.api;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.Request;
+
+import java.io.IOException;
+
+/**
+ * Wraps a Tiles container to allow easy decoration.
+ */
+public class TilesContainerWrapper implements TilesContainer {
+
+    /**
+     * The container to wrap.
+     */
+    protected TilesContainer container;
+
+    /**
+     * Constructor.
+     *
+     * @param container The container to wrap.
+     */
+    public TilesContainerWrapper(TilesContainer container) {
+        this.container = container;
+        if (container == null) {
+            throw new NullPointerException("The wrapped container must be not null");
+        }
+    }
+
+    @Override
+    public void endContext(Request request) {
+        container.endContext(request);
+    }
+
+    @Override
+    public Object evaluate(Attribute attribute, Request request) {
+        return container.evaluate(attribute, request);
+    }
+
+    @Override
+    public ApplicationContext getApplicationContext() {
+        return container.getApplicationContext();
+    }
+
+    @Override
+    public AttributeContext getAttributeContext(Request request) {
+        return container.getAttributeContext(request);
+    }
+
+    @Override
+    public Definition getDefinition(String definitionName, Request request) {
+        return container.getDefinition(definitionName, request);
+    }
+
+    @Override
+    public boolean isValidDefinition(String definition, Request request) {
+        return container.isValidDefinition(definition, request);
+    }
+
+    @Override
+    public void prepare(String preparer, Request request) {
+        container.prepare(preparer, request);
+    }
+
+    @Override
+    public void render(String definition, Request request) {
+        container.render(definition, request);
+    }
+
+    @Override
+    public void render(Definition definition, Request request) {
+        container.render(definition, request);
+    }
+
+    @Override
+    public void render(Attribute attribute, Request request) throws IOException {
+        container.render(attribute, request);
+    }
+
+    @Override
+    public void renderContext(Request request) {
+        container.renderContext(request);
+    }
+
+    @Override
+    public AttributeContext startContext(Request request) {
+        return container.startContext(request);
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/TilesException.java b/plugins/tiles/src/main/java/org/apache/tiles/api/TilesException.java
new file mode 100644
index 000000000..77088c3d5
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/TilesException.java
@@ -0,0 +1,68 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.api;
+
+/**
+ * Root class for all Tiles-exceptions.
+ */
+public class TilesException extends RuntimeException {
+
+    /**
+     * Constructor.
+     */
+    public TilesException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The error or warning message.
+     */
+    public TilesException(String message) {
+        super(message);
+    }
+
+    /**
+     * Create a new <code>TilesException</code> wrapping an existing exception.
+     * <p/>
+     * <p>The existing exception will be embedded in the new
+     * one, and its message will become the default message for
+     * the TilesException.</p>
+     *
+     * @param e The cause to be wrapped.
+     */
+    public TilesException(Throwable e) {
+        super(e);
+    }
+
+    /**
+     * Create a new <code>TilesException</code> from an existing exception.
+     * <p/>
+     * <p>The existing exception will be embedded in the new
+     * one, but the new exception will have its own message.</p>
+     *
+     * @param message The detail message.
+     * @param e       The cause to be wrapped.
+     */
+    public TilesException(String message, Throwable e) {
+        super(message, e);
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/access/TilesAccess.java b/plugins/tiles/src/main/java/org/apache/tiles/api/access/TilesAccess.java
new file mode 100644
index 000000000..cdfaa753e
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/access/TilesAccess.java
@@ -0,0 +1,160 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.api.access;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.tiles.api.NoSuchContainerException;
+import org.apache.tiles.api.TilesContainer;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.Request;
+
+import java.util.Map;
+
+/**
+ * Provides static access to the Tiles container.
+ */
+public final class TilesAccess {
+
+    private static final Logger LOG = LogManager.getLogger(TilesAccess.class);
+
+    /**
+     * Name of the attribute used to store the current used container.
+     */
+    public static final String CURRENT_CONTAINER_ATTRIBUTE_NAME =
+        "org.apache.tiles.servlet.context.ServletTilesRequestContext.CURRENT_CONTAINER_KEY";
+
+    /**
+     * Constructor, private to avoid instantiation.
+     */
+    private TilesAccess() {
+    }
+
+    /**
+     * The name of the attribute to use when getting and setting the container
+     * object in a context.
+     */
+    public static final String CONTAINER_ATTRIBUTE =
+        "org.apache.tiles.CONTAINER";
+
+    /**
+     * Configures the container to be used in the application.
+     *
+     * @param context   The Tiles application context object to use.
+     * @param container The container object to set.
+     * @param key       The key under which the container will be stored.
+     * @since 2.1.2
+     */
+    public static void setContainer(ApplicationContext context, TilesContainer container, String key) {
+        if (key == null) {
+            key = CONTAINER_ATTRIBUTE;
+        }
+
+        if (container == null) {
+            LOG.info("Removing TilesContext for context: {}", context.getClass().getName());
+            context.getApplicationScope().remove(key);
+        } else {
+            LOG.info("Publishing TilesContext for context: {}", context.getClass().getName());
+            context.getApplicationScope().put(key, container);
+        }
+    }
+
+    /**
+     * Returns default the container to be used in the application.
+     *
+     * @param context The Tiles application context object to use.
+     * @return The default container object.
+     * @since 3.0.0
+     */
+    public static TilesContainer getContainer(ApplicationContext context) {
+        return getContainer(context, CONTAINER_ATTRIBUTE);
+    }
+
+    /**
+     * Returns the container to be used in the application registered under a specific key.
+     *
+     * @param context The Tiles application context object to use.
+     * @param key     The key under which the container will be stored.
+     * @return The container object.
+     * @since 3.0.0
+     */
+    public static TilesContainer getContainer(ApplicationContext context,
+                                              String key) {
+        if (key == null) {
+            key = CONTAINER_ATTRIBUTE;
+        }
+
+        return (TilesContainer) context.getApplicationScope().get(key);
+    }
+
+    /**
+     * Sets the current container to use in web pages.
+     *
+     * @param request The request to use.
+     * @param key     The key under which the container is stored.
+     * @since 2.1.0
+     */
+    public static void setCurrentContainer(Request request,
+                                           String key) {
+        ApplicationContext applicationContext = request.getApplicationContext();
+        TilesContainer container = getContainer(applicationContext, key);
+        if (container != null) {
+            request.getContext("request").put(CURRENT_CONTAINER_ATTRIBUTE_NAME, container);
+        } else {
+            throw new NoSuchContainerException("The container with the key '"
+                + key + "' cannot be found");
+        }
+    }
+
+    /**
+     * Sets the current container to use in web pages.
+     *
+     * @param request   The request to use.
+     * @param container The container to use as the current container.
+     * @since 2.1.0
+     */
+    public static void setCurrentContainer(Request request, TilesContainer container) {
+        if (container != null) {
+            request.getContext("request").put(CURRENT_CONTAINER_ATTRIBUTE_NAME, container);
+        } else {
+            throw new NullPointerException("The container cannot be null");
+        }
+    }
+
+    /**
+     * Returns the current container that has been set, or the default one.
+     *
+     * @param request The request to use.
+     * @return The current Tiles container to use in web pages.
+     * @since 2.1.0
+     */
+    public static TilesContainer getCurrentContainer(Request request) {
+        ApplicationContext context = request.getApplicationContext();
+        Map<String, Object> requestScope = request.getContext("request");
+        TilesContainer container = (TilesContainer) requestScope.get(CURRENT_CONTAINER_ATTRIBUTE_NAME);
+        if (container == null) {
+            container = getContainer(context);
+            requestScope.put(CURRENT_CONTAINER_ATTRIBUTE_NAME, container);
+        }
+
+        return container;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/api/access/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/api/access/package-info.java
index 38e506552..f6090a574 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/access/package-info.java
@@ -7,7 +7,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Tiles access package. Utility classes to access Tiles funcionality from an application.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+package org.apache.tiles.api.access;
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/api/mgmt/MutableTilesContainer.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/api/mgmt/MutableTilesContainer.java
index 38e506552..6e8765cc2 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/mgmt/MutableTilesContainer.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,24 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
+package org.apache.tiles.api.mgmt;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.api.TilesContainer;
+import org.apache.tiles.request.Request;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * Defines a mutable version of the TilesContainer.
  *
- * @since Struts 2.0.2
+ * @since Tiles 2.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public interface MutableTilesContainer extends TilesContainer {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+    /**
+     * Register a new definition with the container.
+     *
+     * @param definition The definition to register.
+     * @param request TODO
+     */
+    void register(Definition definition, Request request);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/api/mgmt/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/api/mgmt/package-info.java
index 38e506552..8a22e1cb5 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/mgmt/package-info.java
@@ -7,7 +7,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Classes and interfaces to be used when it is needed to create Tiles definitions
+ * during the execution of the application.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+package org.apache.tiles.api.mgmt;
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/package-info.java b/plugins/tiles/src/main/java/org/apache/tiles/api/package-info.java
new file mode 100644
index 000000000..f19af6def
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/package-info.java
@@ -0,0 +1,385 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * The Tiles taglib and framework allows building web pages by assembling reusable
+ pieces of pages, called Tiles. A Tiles is usually a simple JSP page.
+
+ <div class="section">
+ <h2>Introduction</h2>
+
+ <p>The Tiles framework allows building pages by assembling reusable Tiles.
+ As an example, the page in the next figure can be build by assembling a
+ header, a footer, a menu and a body.</p>
+
+ <p><img src="doc-files/image001.gif" height="169" width="145" alt="doc-files/image001"></p>
+
+ <p>Each Tiles (header, menu, body, ...) is a JSP page and can itself be build
+ by assembling other Tiles.</p>
+
+ <p>Using Tiles can be compared as using Java methods: You need to define the Tiles (the method body), and then you
+ can &quot;call&quot; this body anywhere you want, passing it some parameters. In Tiles, parameters are called
+ &quot;attributes&quot; in order to avoid confusion with the request parameters.</p>
+
+ <p>The Tiles body can be a simple JSP page, a Struts action or any URI pointing
+ to a resource inside the current web site.</p>
+
+ <p>Inserting the body, or calling it, is done with the tag &lt;tiles:insert
+ ...&gt; anywhere in a JSP page. Insertion can also be done by specifying
+ a <em>definition name </em>as the path of a Struts forward or as input,
+ forward or include attributes of a Struts action.</p>
+
+ <p>Tiles bodies are used to create layouts, reusable parts, ... Tiles insertions
+ are used to insert Tiles. The same Tiles can be reused several times in
+ the same site, or even in the same page.</p>
+
+ <p>Insertion of a Tiles body can be associated to a logical name in what Tiles calls a &quot;definition&quot;. A
+ definition contains a logical name, a page used as body and some attribute values. The definition declaration
+ doesn't insert the associated Tiles body. It just associates it with the name. A definition name can be used
+ anywhere insertion of a Tiles body can occur. The associated Tiles body is then inserted with associated
+ attributes.</p>
+
+ <p>The definition declarations can be done in JSP pages or in one or more
+ centralized files. A definition can extend another one, overload some attributes,
+ add new attributes ... This allows the declaration of a &quot;master&quot; definition
+ declaring the common layout, header, menu and footer. All other definitions
+ extend this master layout thereby making it possible to change the entire
+ site look &amp; feel simply by changing the master definition. </p>
+ </div>
+ <div class="section">
+ <h2>Simple Examples</h2>
+
+ <div class="subsection1">
+ <h3>Insert a JSP page</h3>
+ <pre>&lt;tiles:insert <strong>page</strong>=&quot;/layouts/commonLayout.jsp&quot; flush=&quot;true&quot; /&gt;
+ </pre>
+ <p>This example inserts the specified page in place of the tag. The page attribute is any valid URL pointing to
+ a resource inside the current site.</p>
+ </div>
+ <div class="subsection1">
+ <a name="doc.InsertPageWithAttributes"></a>
+
+ <h3>Insert a Tiles passing some attributes</h3>
+ <pre>
+ &lt;tiles:insert page=&quot;/layouts/classicLayout.jsp&quot; flush=&amp;quot;true&quot;&gt;
+ &lt;tiles:put name=&quot;title&quot;  value=&quot;Page Title&quot; /&gt;
+ &lt;tiles:put name=&quot;header&quot; value=&quot;/common/header.jsp&quot; /&gt;
+ &lt;tiles:put name=&quot;footer&quot; value=&quot;/common/footer.jsp&quot; /&gt;
+ &lt;tiles:put name=&quot;menu&quot;   value=&quot;/common/menu.jsp&quot; /&gt;
+ &lt;tiles:put name=&quot;body&quot;   value=&quot;/tiles/mainBody.jsp&quot; /&gt;
+ &lt;/tiles:insert&gt;
+ </pre>
+ <p>This example inserts the specified page, passing it the attributes. Attributes
+ are stored in a Tiles context which is passed to the inserted pag and
+ can then be accesssed by their names.</p>
+ </div>
+ <div class="subsection1">
+ <h3>Retrieve an attribute value as String</h3>
+ <pre>
+ &lt;tiles:getAsString name=&quot;title&quot; /&gt;
+ </pre>
+ <p>This example retrieves the value of the attribute &quot;title&quot; and prints it as a String in the current
+ output stream. The method toString() is applied on the attribute value, allowing to pass any kind of object
+ as value.</p>
+ </div>
+ <div class="subsection1">
+ <h3>Insert Tiles referenced by an attribute</h3>
+ <pre>
+ &lt;tiles:insert attribute='menu' /&gt;
+ </pre>
+ <p>This inserts the Tiles referenced by the attribute &quot;menu&quot; value. The
+ specified attribute value is first retrieved from current Tiles's context,
+ and then the value is used as a page target to insert.</p>
+ </div>
+ <div class="subsection1">
+ <h3>Classic Layout </h3>
+
+ <p>This example is a layout assembling a page in the classic header-footer-menu-body
+ fashion.</p>
+ <pre>
+ &lt;%@ taglib uri=&quot;http://tiles.apache.org/tags-tiles&quot; prefix=&quot;tiles&quot; %&gt;
+ &lt;HTML&gt;
+ &lt;HEAD&gt;
+ &lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;%=request.getContextPath()%&gt;/layouts/stylesheet.css&quot;
+ type=&quot;text/css&quot;/&gt;
+ &lt;title&gt;&lt;tiles:getAsString name=&quot;title&quot;/&gt;&lt;/title&gt;
+ &lt;/HEAD&gt;
+ &lt;body&gt;
+ &lt;table border=&quot;0&quot; width=&quot;100%&quot; cellspacing=&quot;5&quot;&gt;
+ &lt;tr&gt;
+ &lt;td colspan=&quot;2&quot;&gt;&lt;tiles:insert attribute=&quot;header&quot; /&gt;&lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;tr&gt;
+ &lt;td width=&quot;140&quot; valign=&quot;top&quot;&gt;
+ &lt;tiles:insert attribute='menu' /&gt;
+ &lt;/td&gt;
+ &lt;td valign=&quot;top&quot;  align=&quot;left&quot;&gt;
+ &lt;tiles:insert attribute='body' /&gt;
+ &lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;tr&gt;
+ &lt;td colspan=&quot;2&quot;&gt;
+ &lt;tiles:insert attribute=&quot;footer&quot; /&gt;
+ &lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;/table&gt;
+ &lt;/body&gt;
+ &lt;/html&gt;
+ </pre>
+ <p>The layout is declared in a JSP page (ex: /layouts/classicLayout.jsp).
+ It can be used in conjunction with the tag described in &quot;<a href="#doc.InsertPageWithAttributes">Insert
+ a page passing some attributes</a>&quot;. </p>
+ </div>
+ </div>
+ <div class="section">
+ <h2>Definitions</h2>
+
+ <p>A definition associates a logical name with the URL of a Tiles to be inserted
+ and some attribute values. A definition doesn't insert the Tiles. This is
+ done later using the definition name. A definition name can be inserted
+ as often as you want in your site, making it easy to reuse a Tiles. </p>
+
+ <p>A definition can extend another definition and overload some attributes
+ or add new ones. This makes easy factorization of definitions differing
+ by some attributes. For example, you can define a master definition declaring
+ the main header, menu, footer, and a default title. Then let each of your
+ page definitions extend this master definition and overload the title and
+ the body.</p>
+
+ <p>Definitions can be declared in a JSP page, or in one or more centralized
+ files. To enable the definitions from centralized files, you need to initialize
+ the &quot;definitions factory&amp;&amp;quot; which will parse the definitions from the files
+ and provide them to the Tiles framework.</p>
+
+ <div class="subsection1">
+ <h3>Enabling Definition Factory</h3>
+
+ <p>To enable Tiles definitions described in one or more files, you need to write these files and to initialize the
+ definition factory. </p>
+
+ <p>Initialization is different depending on the Struts version you use,
+ or if you do not use Struts at all.</p>
+
+ <div class="subsection2">
+ <h4>Struts1.1</h4>
+
+ <p>Use the Tiles plug-in to enable Tiles definitions. This plug-in creates
+ the definition factory and passese it a configuration object populated
+ with parameters. Parameters can be specified in the web.xml file or
+ as plug-in parameters. The plug-in first reads parameters from web.xml,
+ and then overloads them with the ones found in the plug-in. All parameters
+ are optional and can be omitted. The plug-in should be declared in each
+ struts-config file:</p>
+ <pre>
+ &lt;plug-in className=&amp;&amp;quot;org.apache.struts.tiles.TilesPlugin&amp;&amp;quot; &gt;
+ &lt;set-property property=&amp;&amp;quot;definitions-config&amp;&amp;quot;
+ value=&amp;&amp;quot;/WEB-INF/tiles-defs.xml,
+ /WEB-INF/tiles-tests-defs.xml,/WEB-INF/tiles-tutorial-defs.xml,
+ /WEB-INF/tiles-examples-defs.xml&amp;&amp;quot; /&gt;
+ &lt;set-property property=&amp;&amp;quot;moduleAware&amp;&amp;quot; value=&amp;&amp;quot;true&amp;&amp;quot; /&gt;
+ &lt;set-property
+ property=&amp;&amp;quot;org.apache.tiles.definition.digester.DigesterDefinitionsReader.PARSER_VALIDATE&amp;&amp;quot;
+ value=&amp;&amp;quot;true&amp;&amp;quot; /&gt;
+ &lt;/plug-in&gt;
+ </pre>
+ <ul>
+ <li>definitions-config: (optional)
+ <ul>
+ <li>Specify configuration file names. There can be several comma separated file names (default: ?? )
+ </li>
+ </ul>
+ </li>
+ <li>org.apache.tiles.definition.digester.DigesterDefinitionsReader.PARSER_VALIDATE: (optional)
+ <ul>
+ <li>Specify if XML parser should validate the Tiles configuration
+ file
+ <ul>
+ <li>true : validate. DTD should be specified in file header (default)</li>
+ <li>false : no validation</li>
+
+ </ul>
+ </li>
+ </ul>
+ </li>
+
+ <li>moduleAware: (optional)
+ <ul>
+ <li>Specify if the Tiles definition factory is module aware. If true (default),
+ there will be one factory for each Struts module.
+ If false, there will be one common factory for all module. In this later case,
+ it is still needed to declare one plugin per module. The factory will be
+ initialized with parameters found in the first initialized plugin (generally the
+ one associated with the default module).
+ <ul>
+ <li>true : Tiles framework is module aware</li>
+ <li>false :Tiles framework has one single factoy shared among modules (default)</li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+
+ <li>tilesUtilImplClassname: (optional - for advanced user)
+ <ul>
+ <li>Specify The classname of the TilesUtil implementation to use. The specified class should
+ be a subclass of TilesUtilStrutsImpl. This option disable the moduleAware option.
+ <br>Specifying &amp;&amp;&quot;TilesUtilStrutsImpl&amp;&amp;&quot; is equivalent to moduleAware =
+ false.
+ <br>Specifying &amp;&amp;&quot;TilesUtilStrutsModuleImpl&amp;&amp;&quot; is equivalent to moduleAware
+ = true.
+ This option is taken into account only once, when it is first encountered. To avoid problems,
+ it is advice to specify the same values in all TilesPlugin declaration.
+ </li>
+ </ul>
+ </li>
+
+ </ul>
+ <p>The TilesPlugin class creates one definition factory for each struts module.
+ </p>
+
+ <p>
+ If the flag moduleAware is false, only one shared factory is created for all modules.
+ In this later case, the factory is initialized with parameters found in the first plugin.
+ The plugins should be declared in all modules, and the moduleAware flag should be
+ the same for the entire application.</p>
+
+ <p>
+ Paths found in Tiles definitions are relative to the main context.</p>
+
+ <p>You don't need to specify a TilesRequestProcessor, this is automatically
+ done by the plug-in. If, however, you want to specify your own RequestProcessor,
+ it should extend the TilesRequestProcessor. The plug-in checks this
+ constraint.</p>
+ </div>
+ <div class="subsection2">
+ <h4>Struts1.0.x</h4>
+
+ <p>You need to use a special servlet extending the Struts servlet. This is specified in the web.xml file of your
+ application:</p>
+ <pre>
+ &lt;servlet&gt;
+ &lt;servlet-name&gt;action&lt;/servlet-name&gt;
+ &lt;servlet-class&gt;org.apache.tiles.web.startup.TilesServlet&lt;/servlet-class&gt;
+ &lt;!-- Tiles Servlet parameter
+ Specify configuration file names. There can be several comma
+ separated file names
+ --&gt;
+ &lt;init-param&gt;
+ &lt;param-name&gt;definitions-config&lt;/param-name&gt;
+ &lt;param-value&gt;/WEB-INF/tiles-defs.xml&lt;/param-value&gt;
+ &lt;/init-param&gt;
+ &lt;!-- Tiles Servlet parameter
+ Specify if XML parser should validate the Tiles configuration file(s).
+ true : validate. DTD should be specified in file header.
+ false : no validation
+ --&gt;
+ &lt;init-param&gt;
+ &lt;param-name&gt;org.apache.tiles.definition.digester.DigesterDefinitionsReader.PARSER_VALIDATE&lt;/param-name&gt;
+ &lt;param-value&gt;true&lt;/param-value&gt;
+ &lt;/init-param&gt;
+ ...
+ &lt;/servlet&gt;
+ </pre>
+ </div>
+ <div class="subsection2">
+ <h4>Without Struts</h4>
+
+ <p>Tiles can be used without Struts. To initialize the definition factory, you can use the provided servlet. Declare
+ it in the web.xml file of your application:</p>
+ <pre>
+ &lt;servlet&gt;
+ &lt;servlet-name&gt;action&lt;/servlet-name&gt;
+ &lt;servlet-class&gt;org.apache.struts.tiles.TilesServlet&lt;/servlet-class&gt;
+
+
+ &lt;init-param&gt;
+ &lt;param-name&gt;definitions-config&lt;/param-name&gt;
+ &lt;param-value&gt;/WEB-INF/tiles-defs.xml&lt;/param-value&gt;
+ &lt;/init-param&gt;
+ &lt;init-param&gt;
+ &lt;param-name&gt;org.apache.tiles.definition.digester.DigesterDefinitionsReader.PARSER_VALIDATE&lt;/param-name&gt;
+ &lt;param-value&gt;true&lt;/param-value&gt;
+ &lt;/init-param&gt;
+ ...
+ </pre>
+ <p>The parameters are the same as for Struts1.1 or 1.0.</p>
+ </div>
+ </div>
+ <div class="subsection1">
+ <h3>Definition File Syntax</h3>
+
+ <p>The definition file syntax can be found in the
+ <a href="http://tiles.apache.org/dtds/tiles-config_2_0.dtd">tiles-config_2_0.dtd file</a>.
+ </p>
+
+ <p>Following is a simple example:</p>
+ <pre>
+ &lt;!DOCTYPE tiles-definitions PUBLIC
+ &amp;&amp;quot;-//Apache Software Foundation//DTD Tiles Configuration//EN&amp;&amp;quot;
+ &amp;&amp;quot;http://tiles.apache.org/dtds/tiles-config_2_0.dtd&amp;&amp;quot;&gt;
+
+ &lt;!-- Definitions for Tiles documentation   --&gt;
+ &lt;tiles-definitions&gt;
+
+ &lt;!-- ========================================================== --&gt;
+ &lt;!-- Master definition                                          --&gt;
+ &lt;!-- ========================================================== --&gt;
+ &lt;!-- Main page layout used as a root for other page definitions --&gt;
+
+ &lt;definition name=&amp;&amp;quot;site.mainLayout&amp;&amp;quot;
+   template=&amp;&amp;quot;/layouts/classicLayout.jsp&amp;&amp;quot;&gt;
+ &lt;put name=&amp;&amp;quot;title&amp;&amp;quot;  value=&amp;&amp;quot;Tiles Blank Site&amp;&amp;quot; /&gt;
+ &lt;put name=&amp;&amp;quot;header&amp;&amp;quot; value=&amp;&amp;quot;/tiles/common/header.jsp&amp;&amp;quot; /&gt;
+ &lt;put name=&amp;&amp;quot;menu&amp;&amp;quot;   value=&amp;&amp;quot;site.menu.bar&amp;&amp;quot; /&gt;
+ &lt;put name=&amp;&amp;quot;footer&amp;&amp;quot; value=&amp;&amp;quot;/tiles/common/footer.jsp&amp;&amp;quot; /&gt;
+ &lt;put name=&amp;&amp;quot;body&amp;&amp;quot;   value=&amp;&amp;quot;/tiles/body.jsp&amp;&amp;quot; /&gt;
+ &lt;/definition&gt;
+
+ &lt;!-- ========================================================== --&gt;
+ &lt;!-- Index page definition                                      --&gt;
+ &lt;!-- ========================================================== --&gt;
+ &lt;!-- This definition inherits from the main definition.
+ It overloads the page title and the body used.
+ Use the same mechanism to define new pages sharing common
+ properties (here header, menu, footer, layout)
+ --&gt;
+
+ &lt;definition name=&amp;&amp;quot;site.index.page&amp;&amp;quot;
+   extends=&amp;&amp;quot;site.mainLayout&amp;&amp;quot; &gt;
+ &lt;put name=&amp;&amp;quot;title&amp;&amp;quot;  value=&amp;&amp;quot;Tiles Blank Site Index&amp;&amp;quot; /&gt;
+ &lt;put name=&amp;&amp;quot;body&amp;&amp;quot;   value=&amp;&amp;quot;/tiles/body.jsp&amp;&amp;quot; /&gt;
+ &lt;/definition&gt;
+
+ &lt;/tiles-definition&gt;
+ </pre>
+ </div>
+ <div class="subsection1">
+ <h3>Debugging</h3>
+
+ <p>To debug a page made of Tiles, you can use following advices:</p>
+ <ul>
+ <li>Check each Tiles separatly. Try to access nested Tiles directly to test
+ if thes work properly.
+ </li>
+ <li>Enable Tiles logging. See the commons-logging package help.</li>
+ </ul>
+ </div>
+ </div>
+
+ */
+package org.apache.tiles.api;
+
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/preparer/PreparerException.java b/plugins/tiles/src/main/java/org/apache/tiles/api/preparer/PreparerException.java
new file mode 100644
index 000000000..87ba1f1f3
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/preparer/PreparerException.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.api.preparer;
+
+import org.apache.tiles.api.TilesException;
+
+/**
+ * <p>
+ * Thrown when an exception occurs while processing
+ * a prepare request.
+ * </p>
+ *
+ * @since Tiles 2.0
+ */
+public class PreparerException extends TilesException {
+
+    /**
+     * Constructor.
+     */
+    public PreparerException() {
+        super();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param e The cause exception.
+     */
+    public PreparerException(Throwable e) {
+        super(e);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message to include.
+     */
+    public PreparerException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message to include.
+     * @param e The cause exception.
+     */
+    public PreparerException(String message, Throwable e) {
+        super(message, e);
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/preparer/ViewPreparer.java b/plugins/tiles/src/main/java/org/apache/tiles/api/preparer/ViewPreparer.java
new file mode 100644
index 000000000..14bd08308
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/preparer/ViewPreparer.java
@@ -0,0 +1,58 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.api.preparer;
+
+import org.apache.tiles.api.AttributeContext;
+import org.apache.tiles.request.Request;
+
+/**
+ * <p>
+ * Executed prior to rendering a view.
+ * </p>
+ *
+ * <p>
+ * A view preparer is typically used to provide last minute
+ * translations of the data within the attribute context.
+ * A preparer is not intended to replace the controller within an
+ * MVC architecture.
+ * </p>
+ *
+ * See
+ * <ul>
+ * <li>&lt;insert&gt;</li>
+ * <li>&lt;definition&gt;</li>
+ * </ul>>
+ *
+ * @version $Rev$ $Date$
+ */
+public interface ViewPreparer {
+
+    /**
+     * Method associated to a tile and called immediately before the tile
+     * is included.
+     *
+     * @param tilesContext     Current tiles application context.
+     * @param attributeContext Current tile context.
+     * @throws PreparerException If something goes wrong during execution.
+     */
+    void execute(Request tilesContext,
+        AttributeContext attributeContext);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/api/preparer/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/api/preparer/package-info.java
index 38e506552..a8fcab1fd 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/preparer/package-info.java
@@ -7,7 +7,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * "View preparers" are objects that allows the "preparation" of a Tiles artifact
+ * (definition, template or attribute) before it is rendered.<br>
+ * It is useful, for example, when a view item should be built and stored in a
+ * particular context (e.g. a menu) and then rendered.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+package org.apache.tiles.api.preparer;
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/AbstractModelBody.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/AbstractModelBody.java
new file mode 100644
index 000000000..3e3f1ede7
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/AbstractModelBody.java
@@ -0,0 +1,87 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.autotag.core.runtime;
+
+import org.apache.tiles.autotag.core.runtime.util.NullWriter;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.regex.Pattern;
+
+/**
+ * Base class for the abstraction of the body.
+ *
+ * @version $Rev$ $Date$
+ */
+public abstract class AbstractModelBody implements ModelBody {
+
+    // precompiled the pattern to avoid compiling on every method call
+    private static final Pattern PATTERN = Pattern.compile("^\\s*|\\s*$");
+
+    /**
+     * The default writer to use.
+     */
+    private Writer defaultWriter;
+
+    /**
+     * Constructor.
+     *
+     * @param defaultWriter The default writer to use.
+     */
+    public AbstractModelBody(Writer defaultWriter) {
+        this.defaultWriter = defaultWriter;
+    }
+
+    @Override
+    public void evaluate() throws IOException {
+        evaluate(defaultWriter);
+    }
+
+    @Override
+    public String evaluateAsString() throws IOException {
+        StringWriter writer = new StringWriter();
+        try {
+            evaluate(writer);
+        } finally {
+            writer.close();
+        }
+        String body = writer.toString();
+        if (body != null) {
+            body = PATTERN.matcher(body).replaceAll("");
+            if (body.length() <= 0) {
+                body = null;
+            }
+        }
+        return body;
+    }
+
+    @Override
+    public void evaluateWithoutWriting() throws IOException {
+        NullWriter writer = new NullWriter();
+        try {
+            evaluate(writer);
+        } finally {
+            writer.close();
+        }
+    }
+
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/AutotagRuntime.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/AutotagRuntime.java
new file mode 100644
index 000000000..4d5c818d7
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/AutotagRuntime.java
@@ -0,0 +1,51 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.autotag.core.runtime;
+
+/**
+ * Builder interface for creating requests.
+ * The implementations are expected to provide a default constructor,
+ * and to implement another interface that can be used to provide the
+ * parameters needed to build the actual request object.
+ */
+public interface AutotagRuntime<R> {
+    /**
+     * Creates a new Request instance.
+     *
+     * @return The Request.
+     */
+    R createRequest();
+
+    /**
+     * Creates a new ModelBody instance to match the request.
+     *
+     * @return The ModelBody.
+     */
+    ModelBody createModelBody();
+
+    /**
+     * Extracts a parameter from the tag.
+     * @param name The name of the parameter.
+     * @param defaultValue The default value if none is specified.
+     * @return The value of the parameter.
+     */
+    <T> T getParameter(String name, Class<T> type, T defaultValue);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/ModelBody.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/ModelBody.java
new file mode 100644
index 000000000..81178eedd
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/ModelBody.java
@@ -0,0 +1,62 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.autotag.core.runtime;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Abstracts a tag/directive body.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface ModelBody {
+
+    /**
+     * Evaluates a body and returns it as a string.
+     *
+     * @return The body, as a string.
+     * @throws IOException If something goes wrong.
+     */
+    String evaluateAsString() throws IOException;
+
+    /**
+     * Evaluates a body, but discards result.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    void evaluateWithoutWriting() throws IOException;
+
+    /**
+     * Evaluates the body and writes in the default writer.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    void evaluate() throws IOException;
+
+    /**
+     * Evaluates the body and writes the result in the writer.
+     *
+     * @param writer The writer to write the result into.
+     * @throws IOException If something goes wrong.
+     */
+    void evaluate(Writer writer) throws IOException;
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/annotation/Parameter.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/annotation/Parameter.java
new file mode 100644
index 000000000..6f9c99b8c
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/annotation/Parameter.java
@@ -0,0 +1,56 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.autotag.core.runtime.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies behaviour for a parameter of the "execute" method of a template class.
+ *
+ * @version $Rev$ $Date$
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.PARAMETER)
+public @interface Parameter {
+
+    /**
+     * Indicates to use the parameter name itself for the exported name.
+     */
+    String SAME_NAME = "USE THE SAME NAME";
+
+    /**
+     * Returns the name of the exported property name.
+     */
+    String name() default SAME_NAME;
+
+    /**
+     * Indicates that this parameter is required.
+     */
+    boolean required() default false;
+
+    /**
+     * Indicates the default value, as it will be written in Java code.
+     */
+    String defaultValue() default "null";
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/annotation/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/annotation/package-info.java
index 38e506552..261b48ebd 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/annotation/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Annotations to be used in template classes.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.autotag.core.runtime.annotation;
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/package-info.java
index 38e506552..1e8324a66 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Runtime part for all Autotag generated code.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.autotag.core.runtime;
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/util/NullWriter.java
similarity index 54%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/util/NullWriter.java
index 38e506552..6a66fb3a3 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/util/NullWriter.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,33 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+package org.apache.tiles.autotag.core.runtime.util;
+
+import java.io.Writer;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * A writer that does not write anything.
  *
- * @since Struts 2.0.2
+ * @version $Rev$ $Date$
  */
-public class StrutsTilesListener extends AbstractTilesListener {
+public class NullWriter extends Writer {
 
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+    /** {@inheritDoc} */
+    @Override
+    public void close() {
+        // Does nothing
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void flush() {
+        // Does nothing
+    }
 
+    /** {@inheritDoc} */
     @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
+    public void write(char[] cbuf, int off, int len) {
+        // Does nothing
     }
-}
\ No newline at end of file
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/util/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/util/package-info.java
index 38e506552..7f2e35629 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/core/runtime/util/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Utilities for Autotag core runtime.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.autotag.core.runtime.util;
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateClass.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateClass.java
new file mode 100644
index 000000000..f869c9526
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateClass.java
@@ -0,0 +1,195 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.autotag.model;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * It represents a parsed template class.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TemplateClass {
+
+    /**
+     * The class name.
+     */
+    private String name;
+
+    /**
+     * The name of the tag.
+     */
+    private String tagName;
+
+    /**
+     * The prefix of the tag class.
+     */
+    private String tagClassPrefix;
+
+    /**
+     * Documentation about this tag.
+     */
+    private String documentation;
+
+    /**
+     * The method that executes the template class.
+     */
+    private TemplateMethod executeMethod;
+
+    /**
+     * Constructor.
+     *
+     * @param name The name of the template class.
+     */
+    public TemplateClass(String name) {
+        this(name, null, null, null);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param name The name of the template class.
+     * @param tagName The name of the tag.
+     * @param tagClassPrefix The tag class prefix.
+     * @param executeMethod The method that executes the template class.
+     */
+    public TemplateClass(String name, String tagName, String tagClassPrefix,
+            TemplateMethod executeMethod) {
+        this.name = name;
+        this.tagName = tagName;
+        this.tagClassPrefix = tagClassPrefix;
+        this.executeMethod = executeMethod;
+    }
+
+    /**
+     * The name of the parsed class.
+     *
+     * @return The name of the class.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the name of the class, without the package part.
+     *
+     * @return The simple class name.
+     */
+    public String getSimpleName() {
+        int pos = name.lastIndexOf('.');
+        if (pos >= 0) {
+            return name.substring(pos + 1);
+        }
+        return name;
+    }
+
+    /**
+     * Returns the tag name.
+     *
+     * @return The tag name.
+     */
+    public String getTagName() {
+        return tagName;
+    }
+
+    /**
+     * Returns the tag class prefix.
+     *
+     * @return The tag class prefix.
+     */
+    public String getTagClassPrefix() {
+        return tagClassPrefix;
+    }
+
+    /**
+     * Returns the documentation for this class.
+     *
+     * @return The documentation.
+     */
+    public String getDocumentation() {
+        return documentation;
+    }
+
+    /**
+     * Sets the documentation for this class.
+     *
+     * @param documentation The documentation.
+     */
+    public void setDocumentation(String documentation) {
+        this.documentation = documentation;
+    }
+
+    /**
+     * Returns the method that execute this class.
+     *
+     * @return The execute method.
+     */
+    public TemplateMethod getExecuteMethod() {
+        return executeMethod;
+    }
+
+    /**
+     * Returns the collection of regular parameters (no request, no body)
+     * of the execute method.
+     *
+     * @return The regular parameters.
+     */
+    public Collection<TemplateParameter> getParameters() {
+        Map<String, TemplateParameter> params = new LinkedHashMap<String, TemplateParameter>();
+        fillRegularParameters(params, executeMethod);
+        return params.values();
+    }
+
+    /**
+     * Indicates that this class needs a tag body.
+     *
+     * @return <code>true</code> if tag body is needed.
+     */
+    public boolean hasBody() {
+        return executeMethod.hasBody();
+    }
+
+    @Override
+    public String toString() {
+        return "TemplateClass [name=" + name + ", tagName=" + tagName
+                + ", tagClassPrefix=" + tagClassPrefix + ", documentation="
+                + documentation + ", executeMethod=" + executeMethod + "]";
+    }
+
+    /**
+     * Creates regular parameters map.
+     *
+     * @param params The map to fill.
+     * @param method The method to analyze.
+     */
+    private void fillRegularParameters(Map<String, TemplateParameter> params,
+            TemplateMethod method) {
+        if (method != null) {
+            for (TemplateParameter param : method.getParameters()) {
+                if (!param.isRequest() && !param.isBody()) {
+                    params.put(param.getName(), param);
+                }
+            }
+        }
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateMethod.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateMethod.java
new file mode 100644
index 000000000..d5703c54e
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateMethod.java
@@ -0,0 +1,131 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.autotag.model;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * It represents a parsed method in a parsed template class.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TemplateMethod {
+
+    /**
+     * The name of the method.
+     */
+    private String name;
+
+    /**
+     * Documentation about the method.
+     */
+    private String documentation;
+
+    /**
+     * The map of parameters.
+     */
+    private Map<String, TemplateParameter> parameters;
+
+    /**
+     * Constructor.
+     *
+     * @param name The name of the method.
+     * @param parameters The map of parameters.
+     */
+    public TemplateMethod(String name,
+            Iterable<? extends TemplateParameter> parameters) {
+        this.name = name;
+        this.parameters = new LinkedHashMap<String, TemplateParameter>();
+        for (TemplateParameter parameter : parameters) {
+            this.parameters.put(parameter.getName(), parameter);
+        }
+    }
+
+    /**
+     * Returns the name of the method.
+     *
+     * @return The name of the method.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the documentation for this method.
+     *
+     * @return The documentation.
+     */
+    public String getDocumentation() {
+        return documentation;
+    }
+
+    /**
+     * Sets the documentation for this method.
+     *
+     * @param documentation The documentation.
+     */
+    public void setDocumentation(String documentation) {
+        this.documentation = documentation;
+    }
+
+    /**
+     * Returns the parameters of this method.
+     *
+     * @return The parameters.
+     */
+    public Collection<TemplateParameter> getParameters() {
+        return parameters.values();
+    }
+
+    /**
+     * Returns a parameter given its name.
+     *
+     * @param name The name of the parameter.
+     * @return The parameter.
+     */
+    public TemplateParameter getParameterByName(String name) {
+        return parameters.get(name);
+    }
+
+    /**
+     * Indicates that this method needs a tag body.
+     *
+     * @return <code>true</code> if tag body is needed.
+     */
+    public boolean hasBody() {
+        if (parameters.size() >= 2) {
+            for (TemplateParameter param : parameters.values()) {
+                if (param.isBody()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "TemplateMethod [name=" + name + ", documentation="
+                + documentation + ", parameters=" + parameters + "]";
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateParameter.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateParameter.java
new file mode 100644
index 000000000..360e019ee
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateParameter.java
@@ -0,0 +1,186 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.autotag.model;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+
+/**
+ * It represents a parameter in a method in a parsed template class.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TemplateParameter {
+
+    /**
+     * The name of the parameter.
+     */
+    private String name;
+
+    /**
+     * The exported name, i.e. the name of the parameter in created code. Usually
+     * helpful if this exported name is a reserved word.
+     */
+    private String exportedName;
+
+    /**
+     * The parameter documentation.
+     */
+    private String documentation;
+
+    /**
+     * The type of the parameter.
+     */
+    private String type;
+
+    /**
+     * The default value, as it will be written in Java code.
+     */
+    private String defaultValue;
+
+    /**
+     * Indicates that this parameter is required.
+     */
+    private boolean required;
+
+    /**
+     * Indicates that this parameter is the request.
+     */
+    private boolean request;
+
+    /**
+     * Constructor.
+     *
+     * @param name The name of the parameter.
+     * @param exportedName The exported name, i.e. the name of the parameter in created code. Usually
+     * helpful if this exported name is a reserved word.
+     * @param type The type of the parameter.
+     * @param defaultValue The default value, as it will be written in Java code.
+     * @param required Indicates that this parameter is required.
+     */
+    public TemplateParameter(String name, String exportedName, String type, String defaultValue, boolean required, boolean request) {
+        this.name = name;
+        this.exportedName = exportedName;
+        this.type = type;
+        this.defaultValue = defaultValue;
+        this.required = required;
+        this.request = request;
+    }
+
+    /**
+     * Returns the documentation for this parameter.
+     *
+     * @return The documentation.
+     */
+    public String getDocumentation() {
+        return documentation;
+    }
+
+    /**
+     * Sets the documentation for this parameter.
+     *
+     * @param documentation The documentation.
+     */
+    public void setDocumentation(String documentation) {
+        this.documentation = documentation;
+    }
+
+    /**
+     * Returns the name of the parameter.
+     *
+     * @return The name of the parameter.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the exported name, i.e. the name of the parameter in created code. Usually
+     * helpful if this exported name is a reserved word.
+     *
+     * @return The exported name.
+     */
+    public String getExportedName() {
+        return exportedName;
+    }
+
+    /**
+     * Returns the type of the parameter.
+     *
+     * @return The type.
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * Returns the default value, as it will be written in Java code.
+     *
+     * @return The default value.
+     */
+    public String getDefaultValue() {
+        return defaultValue;
+    }
+
+    /**
+     * Indicates that this parameter is required.
+     *
+     * @return <code>true</code> if the parameter is required.
+     */
+    public boolean isRequired() {
+        return required;
+    }
+
+    /**
+     * Indicates that this parameter implements {@link ModelBody}.
+     *
+     * @return <code>true</code> if the parameter is a body.
+     */
+    public boolean isBody() {
+        return ModelBody.class.getName().equals(type);
+    }
+
+    /**
+     * Indicates that this parameter implements {@link Request}.
+     *
+     * @return <code>true</code> if the parameter is a request.
+     */
+    public boolean isRequest() {
+        return request;
+    }
+
+    /**
+     * Returns the suffix for getter and setter of the property generated by
+     * this parameter.
+     *
+     * @return The getter and setter suffix.
+     */
+    public String getGetterSetterSuffix() {
+        return exportedName.substring(0, 1).toUpperCase() + exportedName.substring(1);
+    }
+
+    @Override
+    public String toString() {
+        return "TemplateParameter [name=" + name + ", exportedName="
+                + exportedName + ", documentation=" + documentation + ", type="
+                + type + ", defaultValue=" + defaultValue + ", required="
+                + required + ", request=" + request + "]";
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateSuite.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateSuite.java
new file mode 100644
index 000000000..25361c866
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/TemplateSuite.java
@@ -0,0 +1,129 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.autotag.model;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * It represents a suite of template classes.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TemplateSuite {
+
+    /**
+     * The name of the suite.
+     */
+    private String name;
+
+    /**
+     * The documentation of this suite.
+     */
+    private String documentation;
+
+    /**
+     * The map of template classes.
+     */
+    private Map<String, TemplateClass> templateClasses;
+
+    /**
+     * Constructor.
+     *
+     * @param name The name of the suite.
+     * @param documentation The documentation.
+     */
+    public TemplateSuite(String name, String documentation) {
+        this(name, documentation, null);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param name The name of the suite.
+     * @param documentation The documentation.
+     * @param classes The template classes.
+     */
+    public TemplateSuite(String name, String documentation,
+            Iterable<? extends TemplateClass> classes) {
+        this.name = name;
+        this.documentation = documentation;
+        templateClasses = new LinkedHashMap<String, TemplateClass>();
+        if (classes != null) {
+            for (TemplateClass templateClass : classes) {
+                templateClasses.put(templateClass.getName(), templateClass);
+            }
+        }
+    }
+
+    /**
+     * Returns the template suite name.
+     *
+     * @return The name.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the documentation.
+     *
+     * @return The documentation.
+     */
+    public String getDocumentation() {
+        return documentation;
+    }
+
+    /**
+     * Adds a new template class.
+     *
+     * @param clazz The template class.
+     */
+    public void addTemplateClass(TemplateClass clazz) {
+        templateClasses.put(clazz.getName(), clazz);
+    }
+
+    /**
+     * Returns the template classes.
+     *
+     * @return The template classes.
+     */
+    public Collection<TemplateClass> getTemplateClasses() {
+        return templateClasses.values();
+    }
+
+    /**
+     * Returns a template class given its name.
+     *
+     * @param name The name of the class.
+     * @return The template class instance.
+     */
+    public TemplateClass getTemplateClassByName(String name) {
+        return templateClasses.get(name);
+    }
+
+    @Override
+    public String toString() {
+        return "TemplateSuite [name=" + name + ", documentation="
+                + documentation + ", templateClasses=" + templateClasses + "]";
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/autotag/model/package-info.java
index 38e506552..a1130d2aa 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/autotag/model/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Domain model classes representing a parsed template suite.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.autotag.model;
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/DefinitionsFactory.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/DefinitionsFactory.java
new file mode 100644
index 000000000..150d66294
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/DefinitionsFactory.java
@@ -0,0 +1,79 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.core.definition;
+
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.request.Request;
+
+/**
+ * Interface for creating a {@link Definition}s and managing their contents.
+ * <p/>
+ * <p>
+ * DefinitionsFactory implementations are responsible for maintaining the data
+ * sources of Tiles configuration data and using the data to create Definitions
+ * sets. Implementations also know how to append locale-specific configuration
+ * data to an existing Definitions set.
+ * </p>
+ *
+ * @version $Rev$ $Date$
+ */
+public interface DefinitionsFactory {
+
+    /**
+     * Property name that specifies the implementation of the DefinitionsReader.
+     */
+    String READER_IMPL_PROPERTY =
+        "org.apache.tiles.definition.DefinitionsReader";
+
+    /**
+     * Property name that specifies the implementation of
+     * {@link org.apache.tiles.core.locale.LocaleResolver}.
+     */
+    String LOCALE_RESOLVER_IMPL_PROPERTY =
+        "org.apache.tiles.locale.LocaleResolver";
+
+    /**
+     * Constant representing the configuration parameter
+     * used to define the tiles definition resources.
+     *
+     * @since 2.1.0
+     */
+    String DEFINITIONS_CONFIG = "org.apache.tiles.definition.DefinitionsFactory.DEFINITIONS_CONFIG";
+
+    /**
+     * Constant representing the configuration parameter used to define the
+     * definition DAO to use.
+     */
+    String DEFINITION_DAO_INIT_PARAM =
+        "org.apache.tiles.definition.DefinitionsFactory.DefinitionDAO";
+
+    /**
+     * Returns a Definition object that matches the given name and
+     * Tiles context.
+     *
+     * @param name         The name of the Definition to return.
+     * @param tilesContext The Tiles context to use to resolve the definition.
+     * @return the Definition matching the given name or null if none
+     *         is found.
+     */
+    Definition getDefinition(String name, Request tilesContext);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/DefinitionsFactoryException.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/DefinitionsFactoryException.java
new file mode 100644
index 000000000..cc7468e3f
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/DefinitionsFactoryException.java
@@ -0,0 +1,74 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.definition;
+
+import org.apache.tiles.api.TilesException;
+
+/**
+ * Exception thrown when an error occurs while the impl tries to
+ * create a new instance mapper.
+ */
+public class DefinitionsFactoryException extends TilesException {
+
+    /**
+     * Constructor.
+     */
+    public DefinitionsFactoryException() {
+        super();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The error or warning message.
+     */
+    public DefinitionsFactoryException(String message) {
+        super(message);
+    }
+
+
+    /**
+     * Create a new <code>DefinitionsFactoryException</code> wrapping an existing exception.
+     * <p/>
+     * <p>The existing exception will be embedded in the new
+     * one and its message will become the default message for
+     * the DefinitionsFactoryException.</p>
+     *
+     * @param e The exception to be wrapped.
+     */
+    public DefinitionsFactoryException(Throwable e) {
+        super(e);
+    }
+
+
+    /**
+     * Create a new <code>DefinitionsFactoryException</code> from an existing exception.
+     * <p/>
+     * <p>The existing exception will be embedded in the new
+     * one, but the new exception will have its own message.</p>
+     *
+     * @param message The detail message.
+     * @param e       The exception to be wrapped.
+     */
+    public DefinitionsFactoryException(String message, Throwable e) {
+        super(message, e);
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/DefinitionsReader.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/DefinitionsReader.java
new file mode 100644
index 000000000..3369f6aa0
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/DefinitionsReader.java
@@ -0,0 +1,54 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.core.definition;
+
+import org.apache.tiles.api.Definition;
+
+import java.util.Map;
+
+/**
+ * Interface for reading <code>{@link Definition}</code> from a source.
+ * <p/>
+ * <p>This interface provides a standard way to read
+ * <code>{@link Definition}</code> objects from a source.  Implementations
+ * should define what the source is, whether it be a persistent store such as a
+ * configuration file or database, or something like a web service.  The
+ * DefinitionsReader is responsible for reading from a single location.  It does
+ * not perform any internationalization duties or inheritance of Definitions.
+ * It only reads from the source and returns a Map of objects read.</p>
+ */
+public interface DefinitionsReader {
+
+    /**
+     * Reads <code>{@link Definition}</code> objects from a source.
+     * <p/>
+     * Implementations should publish what type of source object is expected.
+     *
+     * @param source The source from which definitions will be read.
+     * @return a Map of <code>Definition</code> objects read from
+     * the source.
+     * @throws DefinitionsFactoryException if the source is invalid or
+     *                                     an error occurs when reading definitions.
+     */
+    Map<String, Definition> read(Object source);
+
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/NoSuchDefinitionException.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/NoSuchDefinitionException.java
index 38e506552..460dcd78b 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/NoSuchDefinitionException.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+package org.apache.tiles.core.definition;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * Exception thrown when a definition is not found.
  *
- * @since Struts 2.0.2
+ * @version $Rev$ $Date$
  */
-public class StrutsTilesListener extends AbstractTilesListener {
+public class NoSuchDefinitionException extends DefinitionsFactoryException {
 
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
+    /**
+     * Constructor.
+     *
+     * @param msg Message.
+     */
+    public NoSuchDefinitionException(String msg) {
+        super(msg);
     }
-}
\ No newline at end of file
+
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/RefreshMonitor.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/RefreshMonitor.java
index 38e506552..bcd5569d9 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/RefreshMonitor.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+package org.apache.tiles.core.definition;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * Implementing this interface means that the object monitors the sources it
+ * uses to check when they change.
  *
- * @since Struts 2.0.2
+ * @since 2.1.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public interface RefreshMonitor {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+    /**
+     * Indicates whether the sources are out of date and need to be reloaded.
+     *
+     * @return <code>true</code> if the sources need to be refreshed.
+     * @since 2.1.0
+     */
+    boolean refreshRequired();
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/UnresolvingLocaleDefinitionsFactory.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/UnresolvingLocaleDefinitionsFactory.java
new file mode 100644
index 000000000..4fac31f71
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/UnresolvingLocaleDefinitionsFactory.java
@@ -0,0 +1,90 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.core.definition;
+
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.request.Request;
+import org.apache.tiles.core.definition.dao.DefinitionDAO;
+import org.apache.tiles.core.locale.LocaleResolver;
+
+import java.util.Locale;
+
+/**
+ * {@link DefinitionsFactory DefinitionsFactory} implementation that manages
+ * Definitions configuration data from URLs, without resolving definition
+ * inheritance when a definition is returned.<p/>
+ * <p>
+ * The Definition objects are read from the
+ * {@link org.apache.tiles.core.definition.digester.DigesterDefinitionsReader}
+ * class unless another implementation is specified.
+ * </p>
+ *
+ * @version $Rev$ $Date$
+ * @since 2.2.1
+ */
+public class UnresolvingLocaleDefinitionsFactory implements DefinitionsFactory {
+
+    /**
+     * The definition DAO that extracts the definitions from the sources.
+     *
+     * @since 2.2.1
+     */
+    protected DefinitionDAO<Locale> definitionDao;
+
+    /**
+     * The locale resolver object.
+     *
+     * @since 2.2.1
+     */
+    protected LocaleResolver localeResolver;
+
+    /**
+     * Sets the locale resolver to use.
+     *
+     * @param localeResolver The locale resolver.
+     * @since 2.2.1
+     */
+    public void setLocaleResolver(LocaleResolver localeResolver) {
+        this.localeResolver = localeResolver;
+    }
+
+    /**
+     * Sets the definition DAO to use. It must be locale-based.
+     *
+     * @param definitionDao The definition DAO.
+     * @since 2.2.1
+     */
+    public void setDefinitionDAO(DefinitionDAO<Locale> definitionDao) {
+        this.definitionDao = definitionDao;
+    }
+
+    /** {@inheritDoc} */
+    public Definition getDefinition(String name, Request tilesContext) {
+        Locale locale = null;
+
+        if (tilesContext != null) {
+            locale = localeResolver.resolveLocale(tilesContext);
+        }
+
+        return definitionDao.getDefinition(name, locale);
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/BaseLocaleUrlDefinitionDAO.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/BaseLocaleUrlDefinitionDAO.java
new file mode 100644
index 000000000..d5766fc7d
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/BaseLocaleUrlDefinitionDAO.java
@@ -0,0 +1,164 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.definition.dao;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.core.definition.DefinitionsFactoryException;
+import org.apache.tiles.core.definition.DefinitionsReader;
+import org.apache.tiles.core.definition.RefreshMonitor;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.ApplicationResource;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Base abstract class for a DAO that is based on URLs and locale as a
+ * customization key.
+ *
+ * @since 2.1.0
+ */
+public abstract class BaseLocaleUrlDefinitionDAO implements DefinitionDAO<Locale>, RefreshMonitor {
+
+    /**
+     * The logging object.
+     */
+    private static final Logger LOG = LogManager.getLogger(BaseLocaleUrlDefinitionDAO.class);
+
+    /**
+     * Contains the URL objects identifying where configuration data is found.
+     *
+     * @since 2.1.0
+     */
+    protected List<ApplicationResource> sources;
+
+    /**
+     * Contains the dates that the URL sources were last modified.
+     *
+     * @since 2.1.0
+     */
+    protected Map<String, Long> lastModifiedDates;
+
+    /**
+     * Reader used to get definitions from the sources.
+     *
+     * @since 2.1.0
+     */
+    protected DefinitionsReader reader;
+
+    /**
+     * ApplicationContext to locate the source files.
+     *
+     * @since 3.0.0
+     */
+    protected ApplicationContext applicationContext;
+
+    /**
+     * Constructor.
+     */
+    public BaseLocaleUrlDefinitionDAO(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+        lastModifiedDates = new HashMap<>();
+    }
+
+    public void setSources(List<ApplicationResource> sources) {
+        // filter out any sources that are already localized
+        ArrayList<ApplicationResource> defaultSources = new ArrayList<>();
+        for(ApplicationResource source: sources) {
+            if(Locale.ROOT.equals(source.getLocale())) {
+                defaultSources.add(source);
+            }
+        }
+        this.sources = defaultSources;
+    }
+
+    public void setReader(DefinitionsReader reader) {
+        this.reader = reader;
+    }
+
+    /** {@inheritDoc} */
+    public boolean refreshRequired() {
+        boolean status = false;
+
+        Set<String> paths = lastModifiedDates.keySet();
+
+        try {
+            for (String path : paths) {
+                Long lastModifiedDate = lastModifiedDates.get(path);
+                ApplicationResource resource = applicationContext.getResource(path);
+                long newModDate = resource.getLastModified();
+                if (newModDate != lastModifiedDate) {
+                    status = true;
+                    break;
+                }
+            }
+        } catch (IOException e) {
+            LOG.warn("Exception while monitoring update times.", e);
+            return true;
+        }
+        return status;
+    }
+
+    /**
+     * Loads definitions from an URL without loading from "parent" URLs.
+     *
+     * @param resource The URL to read.
+     * @return The definition map that has been read.
+     */
+    protected Map<String, Definition> loadDefinitionsFromResource(ApplicationResource resource) {
+        Map<String, Definition> defsMap = null;
+
+        InputStream stream = null;
+        try {
+            lastModifiedDates.put(resource.getLocalePath(), resource
+                    .getLastModified());
+
+            // Definition must be collected, starting from the base
+            // source up to the last localized file.
+            stream = resource.getInputStream();
+            defsMap = reader.read(stream);
+        } catch (FileNotFoundException e) {
+            // File not found. continue.
+            LOG.debug("File {} not found, continue", resource);
+        } catch (IOException e) {
+            throw new DefinitionsFactoryException("I/O error processing configuration.", e);
+        } finally {
+            try {
+                if (stream != null) {
+                    stream.close();
+                }
+            } catch (IOException e) {
+                throw new DefinitionsFactoryException("I/O error closing " + resource, e);
+            }
+        }
+
+        return defsMap;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/CachingLocaleUrlDefinitionDAO.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/CachingLocaleUrlDefinitionDAO.java
new file mode 100644
index 000000000..84c44f949
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/CachingLocaleUrlDefinitionDAO.java
@@ -0,0 +1,275 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.core.definition.dao;
+
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.core.definition.pattern.PatternDefinitionResolver;
+import org.apache.tiles.core.definition.pattern.PatternDefinitionResolverAware;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.ApplicationResource;
+import org.apache.tiles.request.locale.LocaleUtil;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * <p>
+ * A definitions DAO (loading URLs and using Locale as a customization key) that
+ * caches definitions that have been loaded in a raw way (i.e. with inheritance
+ * that is not resolved).
+ * </p>
+ * <p>
+ * It can check if the URLs change, but by default this feature is turned off.
+ * </p>
+ *
+ * @since 2.1.0
+ */
+public class CachingLocaleUrlDefinitionDAO extends BaseLocaleUrlDefinitionDAO implements PatternDefinitionResolverAware<Locale> {
+
+    /**
+     * Initialization parameter to set whether we want to refresh URLs when they
+     * change.
+     *
+     * @since 2.1.0
+     */
+    public static final String CHECK_REFRESH_INIT_PARAMETER = "org.apache.tiles.definition.dao.LocaleUrlDefinitionDAO.CHECK_REFRESH";
+
+    /**
+     * The locale-specific set of definitions objects.
+     *
+     * @since 2.1.0
+     */
+    protected Map<Locale, Map<String, Definition>> locale2definitionMap;
+
+    /**
+     * Flag that, when <code>true</code>, enables automatic checking of URLs
+     * changing.
+     *
+     * @since 2.1.0
+     */
+    protected boolean checkRefresh = false;
+
+    /**
+     * Resolves definitions using patterns.
+     *
+     * @since 2.2.0
+     */
+    protected PatternDefinitionResolver<Locale> definitionResolver;
+
+    /**
+     * Constructor.
+     *
+     * @since 2.1.0
+     */
+    public CachingLocaleUrlDefinitionDAO(ApplicationContext applicationContext) {
+        super(applicationContext);
+        locale2definitionMap = new HashMap<>();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setPatternDefinitionResolver(
+        PatternDefinitionResolver<Locale> definitionResolver) {
+        this.definitionResolver = definitionResolver;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Definition getDefinition(String name, Locale customizationKey) {
+        Definition retValue = null;
+        if (customizationKey == null) {
+            customizationKey = Locale.ROOT;
+        }
+        Map<String, Definition> definitions = getDefinitions(customizationKey);
+        if (definitions != null) {
+            retValue = definitions.get(name);
+
+            if (retValue == null) {
+                retValue = getDefinitionFromResolver(name, customizationKey);
+
+                if (retValue != null) {
+                    synchronized (definitions) {
+                        definitions.put(name, retValue);
+                    }
+                }
+            }
+        }
+
+        return retValue;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Map<String, Definition> getDefinitions(Locale customizationKey) {
+        if (customizationKey == null) {
+            customizationKey = Locale.ROOT;
+        }
+        Map<String, Definition> retValue = locale2definitionMap
+            .get(customizationKey);
+        if (retValue == null || (checkRefresh && refreshRequired())) {
+            retValue = checkAndloadDefinitions(customizationKey);
+        }
+        return retValue;
+    }
+
+    /**
+     * Sets the flag to check source refresh. If not called, the default is
+     * <code>false</code>.
+     *
+     * @param checkRefresh When <code>true</code>, enables automatic checking
+     *                     of sources changing.
+     * @since 2.1.0
+     */
+    public void setCheckRefresh(boolean checkRefresh) {
+        this.checkRefresh = checkRefresh;
+    }
+
+    /**
+     * Returns a definition from the definition resolver.
+     *
+     * @param name             The name of the definition.
+     * @param customizationKey The customization key to use.
+     * @return The resolved definition.
+     */
+    protected Definition getDefinitionFromResolver(String name,
+                                                   Locale customizationKey) {
+        return definitionResolver.resolveDefinition(name,
+            customizationKey);
+    }
+
+    /**
+     * Checks if sources have changed. If yes, it clears the cache. Then continues
+     * loading definitions.
+     *
+     * @param customizationKey The locale to use when loading sources.
+     * @return The loaded definitions.
+     * @since 2.1.0
+     */
+    protected synchronized Map<String, Definition> checkAndloadDefinitions(Locale customizationKey) {
+        Map<String, Definition> existingDefinitions = locale2definitionMap.get(customizationKey);
+        boolean definitionsAlreadyLoaded = existingDefinitions != null;
+        if (definitionsAlreadyLoaded) {
+            return existingDefinitions;
+        }
+        if (checkRefresh && refreshRequired()) {
+            locale2definitionMap.clear();
+            definitionResolver.clearPatternPaths(customizationKey);
+        }
+        loadDefinitions(customizationKey);
+        return locale2definitionMap.get(customizationKey);
+    }
+
+    /**
+     * Tries to load definitions if necessary.
+     *
+     * @param customizationKey The locale to use when loading sources.
+     * @return The loaded definitions.
+     * @since 2.1.0
+     */
+    protected Map<String, Definition> loadDefinitions(Locale customizationKey) {
+        Map<String, Definition> localeDefsMap = locale2definitionMap
+            .get(customizationKey);
+        if (localeDefsMap != null) {
+            return localeDefsMap;
+        }
+
+        return loadDefinitionsFromResources(customizationKey);
+    }
+
+    /**
+     * Loads definitions from the sources.
+     *
+     * @param customizationKey The locale to use when loading Resources.
+     * @return The loaded definitions.
+     * @since 2.1.0
+     */
+    protected Map<String, Definition> loadDefinitionsFromResources(Locale customizationKey) {
+        Map<String, Definition> localeDefsMap = loadRawDefinitionsFromResources(customizationKey);
+        Map<String, Definition> defsMap = definitionResolver
+            .storeDefinitionPatterns(copyDefinitionMap(localeDefsMap),
+                customizationKey);
+        locale2definitionMap.put(customizationKey, defsMap);
+        return localeDefsMap;
+    }
+
+    /**
+     * Loads the raw definitions from the sources associated with a locale.
+     *
+     * @param customizationKey The locale to use when loading Resources.
+     * @return The loaded definitions.
+     * @since 2.1.3
+     */
+    protected Map<String, Definition> loadRawDefinitionsFromResources(
+        Locale customizationKey) {
+        Map<String, Definition> localeDefsMap;
+
+        Locale parentLocale = LocaleUtil.getParentLocale(customizationKey);
+        localeDefsMap = new LinkedHashMap<>();
+        if (parentLocale != null) {
+            Map<String, Definition> parentDefs = loadRawDefinitionsFromResources(parentLocale);
+            if (parentDefs != null) {
+                localeDefsMap.putAll(parentDefs);
+            }
+        }
+        // For each source, the resource must be loaded.
+        for (ApplicationResource resource : sources) {
+            ApplicationResource newResource = applicationContext.getResource(resource, customizationKey);
+            if (newResource != null) {
+                Map<String, Definition> defsMap = loadDefinitionsFromResource(newResource);
+                if (defsMap != null) {
+                    localeDefsMap.putAll(defsMap);
+                }
+            }
+        }
+        return localeDefsMap;
+    }
+
+    /**
+     * Loads parent definitions, i.e. definitions mapped to a parent locale.
+     *
+     * @param parentLocale The locale to use when loading URLs.
+     * @return The loaded parent definitions.
+     * @since 2.1.0
+     */
+    protected Map<String, Definition> loadParentDefinitions(Locale parentLocale) {
+        return loadDefinitions(parentLocale);
+    }
+
+    /**
+     * Copies the definition map to be passed to a higher level of customization
+     * key.
+     *
+     * @param localeDefsMap The map of definition to be copied.
+     * @return The copy of the definition map. This particular implementation
+     * return the <code>localeDefsMap</code> itself.
+     * @since 2.1.4
+     */
+    protected Map<String, Definition> copyDefinitionMap(
+        Map<String, Definition> localeDefsMap) {
+        return localeDefsMap;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/DefinitionDAO.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/DefinitionDAO.java
new file mode 100644
index 000000000..e42772fe6
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/DefinitionDAO.java
@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.definition.dao;
+
+import org.apache.tiles.api.Definition;
+
+import java.util.Map;
+
+/**
+ * It represents an object that provides definitions, depending on a
+ * customization key.
+ *
+ * @param <K> The customization key class.
+ * @version $Rev$ $Date$
+ * @since 2.1.0
+ */
+public interface DefinitionDAO<K> {
+
+    /**
+     * Returns a definition, given its name and the customization key.
+     *
+     * @param name The name of the definition.
+     * @param customizationKey The customization key.
+     * @return The requested definition, if found, otherwise <code>null</code>.
+     * The inheritance of the definition must not be resolved.
+     * @since 2.1.0
+     */
+    Definition getDefinition(String name, K customizationKey);
+
+    /**
+     * Returns all the definitions used of a customization key.
+     *
+     * @param customizationKey The customization key.
+     * @return All the definitions that are connected to the customization key.
+     * The inheritance of the definitions must not be resolved.
+     * @since 2.1.0
+     */
+    Map<String, Definition> getDefinitions(K customizationKey);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/ResolvingLocaleUrlDefinitionDAO.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/ResolvingLocaleUrlDefinitionDAO.java
new file mode 100644
index 000000000..df7eef304
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/ResolvingLocaleUrlDefinitionDAO.java
@@ -0,0 +1,174 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.core.definition.dao;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.core.definition.NoSuchDefinitionException;
+import org.apache.tiles.request.ApplicationContext;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <p>
+ * A definitions DAO (loading URLs and using Locale as a customization key) that
+ * caches definitions that have been loaded and resolves inheritances.
+ * </p>
+ * <p>
+ * It can check if the URLs change, but by default this feature is turned off.
+ * </p>
+ *
+ * @since 2.1.0
+ */
+public class ResolvingLocaleUrlDefinitionDAO extends CachingLocaleUrlDefinitionDAO {
+
+    /**
+     * The logging object.
+     */
+    private static final Logger LOG = LogManager.getLogger(ResolvingLocaleUrlDefinitionDAO.class);
+
+    public ResolvingLocaleUrlDefinitionDAO(ApplicationContext applicationContext) {
+        super(applicationContext);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Map<String, Definition> loadParentDefinitions(Locale parentLocale) {
+        return loadRawDefinitionsFromResources(parentLocale);
+    }
+
+    @Override
+    protected Map<String, Definition> loadDefinitions(Locale customizationKey) {
+        Map<String, Definition> localeDefsMap = super.loadDefinitions(customizationKey);
+        Map<String, Definition> defsMap = definitionResolver
+                .storeDefinitionPatterns(copyDefinitionMap(localeDefsMap),
+                        customizationKey);
+        resolveInheritances(defsMap, customizationKey);
+        locale2definitionMap.put(customizationKey, defsMap);
+        return defsMap;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Definition getDefinitionFromResolver(String name,
+            Locale customizationKey) {
+        Definition retValue = super.getDefinitionFromResolver(name, customizationKey);
+        if (retValue != null && retValue.getExtends() != null) {
+            Definition parent = getDefinition(retValue.getExtends(), customizationKey);
+            retValue.inherit(parent);
+        }
+
+        return retValue;
+    }
+
+    /**
+     * Resolve locale-specific extended instances.
+     *
+     * @param map The definition map containing the definitions to resolve.
+     * @param locale The locale to use.
+     * @throws NoSuchDefinitionException If a parent definition is not found.
+     * @since 2.1.0
+     */
+    protected void resolveInheritances(Map<String, Definition> map, Locale locale) {
+        if (map != null) {
+            Set<String> alreadyResolvedDefinitions = new HashSet<>();
+            for (Definition definition : map.values()) {
+                resolveInheritance(definition, map, locale,
+                        alreadyResolvedDefinitions);
+            }  // end loop
+        }
+    }
+
+    /**
+     * Resolve locale-specific inheritance. First, resolve parent's inheritance,
+     * then set template to the parent's template. Also copy attributes setted
+     * in parent, and not set in child If instance doesn't extend anything, do
+     * nothing.
+     *
+     * @param definition The definition to resolve
+     * @param definitions The definitions to take when obtaining a parent
+     * definition.
+     * @param locale The locale to use.
+     * @param alreadyResolvedDefinitions The set of the definitions that have
+     * been already resolved.
+     * @throws NoSuchDefinitionException If an inheritance can not be solved.
+     * @since 2.1.0
+     */
+    protected void resolveInheritance(Definition definition,
+            Map<String, Definition> definitions, Locale locale,
+            Set<String> alreadyResolvedDefinitions) {
+        // Already done, or not needed ?
+        if (!definition.isExtending() || alreadyResolvedDefinitions.contains(definition.getName())) {
+            return;
+        }
+
+        LOG.debug("Resolve definition for child name='{}' extends='{}.", definition.getName(), definition.getExtends());
+
+        // Set as visited to avoid endless recursivity.
+        alreadyResolvedDefinitions.add(definition.getName());
+
+        // Resolve parent before itself.
+        Definition parent = definitions.get(definition.getExtends());
+        if (parent == null) { // error
+            String msg = "Error while resolving definition inheritance: child '"
+                + definition.getName()
+                + "' can't find its ancestor '"
+                + definition.getExtends()
+                + "'. Please check your description file.";
+            // to do : find better exception
+            throw new NoSuchDefinitionException(msg);
+        }
+
+        resolveInheritance(parent, definitions, locale,
+                alreadyResolvedDefinitions);
+
+        definition.inherit(parent);
+    }
+
+    /**
+     * Copies the definition map to be passed to a higher level of customization
+     * key.
+     *
+     * @param localeDefsMap The map of definition to be copied.
+     * @return The copy of the definition map. This particular implementation
+     * deep-copies the <code>localeDefsMap</code> into a {@link LinkedHashMap}.
+     * @since 2.1.4
+     */
+    @Override
+    protected Map<String, Definition> copyDefinitionMap(
+            Map<String, Definition> localeDefsMap) {
+        Map<String, Definition> retValue = new LinkedHashMap<>(
+            localeDefsMap.size());
+
+        for (Map.Entry<String, Definition> entry : localeDefsMap.entrySet()) {
+            Definition definition = new Definition(entry.getValue());
+            retValue.put(entry.getKey(), definition);
+        }
+
+        return retValue;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/package-info.java
index 38e506552..225d581ba 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/dao/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Classes to simply load definitions depending on a customization key.
+ * The package contains also basic implementations.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.core.definition.dao;
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/digester/DigesterDefinitionsReader.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/digester/DigesterDefinitionsReader.java
new file mode 100644
index 000000000..9fd3700f0
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/digester/DigesterDefinitionsReader.java
@@ -0,0 +1,468 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.core.definition.digester;
+
+import org.apache.commons.digester.Digester;
+import org.apache.commons.digester.Rule;
+import org.apache.tiles.api.Attribute;
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.api.Expression;
+import org.apache.tiles.api.ListAttribute;
+import org.apache.tiles.core.definition.DefinitionsFactoryException;
+import org.apache.tiles.core.definition.DefinitionsReader;
+import org.xml.sax.Attributes;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Reads {@link Definition} objects from
+ * an XML InputStream using Digester. <p/>
+ * <p>
+ * This <code>DefinitionsReader</code> implementation expects the source to be
+ * passed as an <code>InputStream</code>. It parses XML data from the source
+ * and builds a Map of Definition objects.
+ * </p>
+ * <p/>
+ * <p>
+ * The Digester object can be configured by passing in initialization
+ * parameters. Currently the only parameter that is supported is the
+ * <code>validating</code> parameter. This value is set to <code>false</code>
+ * by default. To enable DTD validation for XML Definition files, give the init
+ * method a parameter with a key of
+ * <code>org.apache.tiles.definition.digester.DigesterDefinitionsReader.PARSER_VALIDATE</code>
+ * and a value of <code>&quot;true&quot;</code>. <p/>
+ * <p>
+ * The Definition objects are stored internally in a Map. The Map is stored as
+ * an instance variable rather than a local variable in the <code>read</code>
+ * method. This means that instances of this class are <strong>not</strong>
+ * thread-safe and access by multiple threads must be synchronized.
+ * </p>
+ *
+ * @version $Rev$ $Date$
+ */
+public class DigesterDefinitionsReader implements DefinitionsReader {
+
+    /**
+     * Digester validation parameter name.
+     */
+    public static final String PARSER_VALIDATE_PARAMETER_NAME = "org.apache.tiles.definition.digester.DigesterDefinitionsReader.PARSER_VALIDATE";
+
+    // Digester rules constants for tag interception.
+
+    /**
+     * Intercepts a &lt;definition&gt; tag.
+     */
+    private static final String DEFINITION_TAG = "tiles-definitions/definition";
+
+    /**
+     * Intercepts a &lt;put-attribute&gt; tag.
+     */
+    private static final String PUT_TAG = "*/definition/put-attribute";
+
+    /**
+     * Intercepts a &lt;definition&gt; inside a  &lt;put-attribute&gt; tag.
+     */
+    private static final String PUT_DEFINITION_TAG = "*/put-attribute/definition";
+
+    /**
+     * Intercepts a &lt;definition&gt; inside an &lt;add-attribute&gt; tag.
+     */
+    private static final String ADD_DEFINITION_TAG = "*/add-attribute/definition";
+
+    /**
+     * Intercepts a &lt;put-list-attribute&gt; tag inside a %lt;definition&gt;
+     * tag.
+     */
+    private static final String DEF_LIST_TAG = "*/definition/put-list-attribute";
+
+    /**
+     * Intercepts a &lt;add-attribute&gt; tag.
+     */
+    private static final String ADD_LIST_ELE_TAG = "*/add-attribute";
+
+    /**
+     * Intercepts a &lt;add-list-attribute&gt; tag.
+     */
+    private static final String NESTED_LIST = "*/add-list-attribute";
+
+    // Handler class names.
+
+    /**
+     * The handler to create definitions.
+     *
+     * @since 2.1.0
+     */
+    protected static final String DEFINITION_HANDLER_CLASS =
+        Definition.class.getName();
+
+    /**
+     * The handler to create attributes.
+     *
+     * @since 2.1.0
+     */
+    protected static final String PUT_ATTRIBUTE_HANDLER_CLASS =
+        Attribute.class.getName();
+
+    /**
+     * The handler to create list attributes.
+     *
+     * @since 2.1.0
+     */
+    protected static final String LIST_HANDLER_CLASS =
+        ListAttribute.class.getName();
+
+    /**
+     * Digester rule to manage definition filling.
+     *
+     * @since 2.1.2
+     */
+    public static class FillDefinitionRule extends Rule {
+
+        /** {@inheritDoc} */
+        @Override
+        public void begin(String namespace, String name, Attributes attributes) {
+            Definition definition = (Definition) digester.peek();
+            definition.setName(attributes.getValue("name"));
+            definition.setPreparer(attributes.getValue("preparer"));
+            String extendsAttribute = attributes.getValue("extends");
+            definition.setExtends(extendsAttribute);
+
+            String template = attributes.getValue("template");
+            Attribute attribute = Attribute.createTemplateAttribute(template);
+            attribute.setExpressionObject(Expression
+                    .createExpressionFromDescribedExpression(attributes
+                            .getValue("templateExpression")));
+            attribute.setRole(attributes.getValue("role"));
+            String templateType = attributes.getValue("templateType");
+            if (templateType != null) {
+                attribute.setRenderer(templateType);
+            } else if (extendsAttribute != null) {
+                attribute.setRenderer(null);
+            }
+            definition.setTemplateAttribute(attribute);
+        }
+    }
+
+    /**
+     * Digester rule to manage attribute filling.
+     *
+     * @since 2.1.0
+     */
+    public static class FillAttributeRule extends Rule {
+
+        /** {@inheritDoc} */
+        @Override
+        public void begin(String namespace, String name, Attributes attributes) {
+            Attribute attribute = (Attribute) digester.peek();
+            attribute.setValue(attributes.getValue("value"));
+            String expression = attributes.getValue("expression");
+            attribute.setExpressionObject(Expression
+                    .createExpressionFromDescribedExpression(expression));
+            attribute.setRole(attributes.getValue("role"));
+            attribute.setRenderer(attributes.getValue("type"));
+        }
+    }
+
+    /**
+     * Digester rule to manage assignment of the attribute to the parent
+     * element.
+     *
+     * @since 2.1.0
+     */
+    public static class PutAttributeRule extends Rule {
+
+        /** {@inheritDoc} */
+        @Override
+        public void begin(String namespace, String name, Attributes attributes) {
+            Attribute attribute = (Attribute) digester.peek(0);
+            Definition definition = (Definition) digester.peek(1);
+            definition.putAttribute(attributes.getValue("name"), attribute,
+                    "true".equals(attributes.getValue("cascade")));
+        }
+    }
+
+    /**
+     * Digester rule to manage assignment of a nested definition in an attribute
+     * value.
+     *
+     * @since 2.1.0
+     */
+    public class AddNestedDefinitionRule extends Rule {
+
+        /** {@inheritDoc} */
+        @Override
+        public void begin(String namespace, String name, Attributes attributes) {
+            Definition definition = (Definition) digester.peek(0);
+            if (definition.getName() == null) {
+                definition.setName(getNextUniqueDefinitionName(definitions));
+            }
+            Attribute attribute = (Attribute) digester.peek(1);
+            attribute.setValue(definition.getName());
+            attribute.setRenderer("definition");
+        }
+    }
+
+    /**
+     * <code>Digester</code> object used to read Definition data
+     * from the source.
+     */
+    protected Digester digester;
+
+    /**
+     * The set of public identifiers, and corresponding resource names for
+     * the versions of the configuration file DTDs we know about.  There
+     * <strong>MUST</strong> be an even number of Strings in this list!
+     */
+    protected String[] registrations;
+
+    /**
+     * Stores Definition objects.
+     */
+    private Map<String, Definition> definitions;
+
+    /**
+     * Index to be used to create unique definition names for anonymous
+     * (nested) definitions.
+     */
+    private int anonymousDefinitionIndex = 1;
+
+    /**
+     * Creates a new instance of DigesterDefinitionsReader.
+     */
+    public DigesterDefinitionsReader() {
+        digester = new Digester();
+        digester.setNamespaceAware(true);
+        digester.setUseContextClassLoader(true);
+        digester.setErrorHandler(new ThrowingErrorHandler());
+
+        // Register our local copy of the DTDs that we can find
+        String[] registrations = getRegistrations();
+        for (int i = 0; i < registrations.length; i += 2) {
+            URL url = this.getClass().getResource(
+                registrations[i + 1]);
+            if (url != null) {
+                digester.register(registrations[i], url.toString());
+            }
+        }
+
+        initSyntax(digester);
+    }
+
+    /**
+     * Sets the validation of XML files.
+     *
+     * @param validating <code>true</code> means that XML validation is turned
+     * on. <code>false</code> otherwise.
+     * @since 3.3.0
+     */
+    public void setValidating(boolean validating) {
+        digester.setValidating(validating);
+    }
+
+    /**
+     * Reads <code>{@link Definition}</code> objects from a source.
+     * <p/>
+     * Implementations should publish what type of source object is expected.
+     *
+     * @param source The <code>InputStream</code> source from which definitions
+     *               will be read.
+     * @return a Map of <code>Definition</code> objects read from
+     *         the source.
+     * @throws DefinitionsFactoryException If the source is invalid or
+     *          an error occurs when reading definitions.
+     */
+    public Map<String, Definition> read(Object source) {
+        // This is an instance variable instead of a local variable because
+        // we want to be able to call the addDefinition method to populate it.
+        // But we reset the Map here, which, of course, has threading implications.
+        definitions = new LinkedHashMap<>();
+
+        if (source == null) {
+            // Perhaps we should throw an exception here.
+            return null;
+        }
+
+        InputStream input;
+        try {
+            input = (InputStream) source;
+        } catch (ClassCastException e) {
+            throw new DefinitionsFactoryException(
+                "Invalid source type.  Requires java.io.InputStream.", e);
+        }
+
+        try {
+            // set first object in stack
+            //digester.clear();
+            digester.push(this);
+            // parse
+            digester.parse(input);
+
+        } catch (SAXException e) {
+            throw new DefinitionsFactoryException(
+                "XML error reading definitions.", e);
+        } catch (IOException e) {
+            throw new DefinitionsFactoryException(
+                "I/O Error reading definitions.", e);
+        } finally {
+            digester.clear();
+        }
+
+        return definitions;
+    }
+
+    /**
+     * Initialised the syntax for reading XML files containing Tiles
+     * definitions.
+     *
+     * @param digester The digester to initialize.
+     */
+    protected void initSyntax(Digester digester) {
+        initDigesterForTilesDefinitionsSyntax(digester);
+    }
+
+
+    /**
+     * Init digester for Tiles syntax with first element = tiles-definitions.
+     *
+     * @param digester Digester instance to use.
+     */
+    private void initDigesterForTilesDefinitionsSyntax(Digester digester) {
+        // syntax rules
+        digester.addObjectCreate(DEFINITION_TAG, DEFINITION_HANDLER_CLASS);
+        digester.addRule(DEFINITION_TAG, new FillDefinitionRule());
+        digester.addSetNext(DEFINITION_TAG, "addDefinition", DEFINITION_HANDLER_CLASS);
+
+        // nested definition rules
+        digester.addObjectCreate(PUT_DEFINITION_TAG, DEFINITION_HANDLER_CLASS);
+        digester.addRule(PUT_DEFINITION_TAG, new FillDefinitionRule());
+        digester.addSetRoot(PUT_DEFINITION_TAG, "addDefinition");
+        digester.addRule(PUT_DEFINITION_TAG, new AddNestedDefinitionRule());
+        digester.addObjectCreate(ADD_DEFINITION_TAG, DEFINITION_HANDLER_CLASS);
+        digester.addRule(ADD_DEFINITION_TAG, new FillDefinitionRule());
+        digester.addSetRoot(ADD_DEFINITION_TAG, "addDefinition");
+        digester.addRule(ADD_DEFINITION_TAG, new AddNestedDefinitionRule());
+
+        // put / putAttribute rules
+        // Rules for a same pattern are called in order, but rule.end() are called
+        // in reverse order.
+        // SetNext and CallMethod use rule.end() method. So, placing SetNext in
+        // first position ensure it will be called last (sic).
+        digester.addObjectCreate(PUT_TAG, PUT_ATTRIBUTE_HANDLER_CLASS);
+        digester.addRule(PUT_TAG, new FillAttributeRule());
+        digester.addRule(PUT_TAG, new PutAttributeRule());
+        // Definition level list rules
+        // This is rules for lists nested in a definition
+        digester.addObjectCreate(DEF_LIST_TAG, LIST_HANDLER_CLASS);
+        digester.addSetProperties(DEF_LIST_TAG);
+        digester.addRule(DEF_LIST_TAG, new PutAttributeRule());
+        // list elements rules
+        // We use Attribute class to avoid rewriting a new class.
+        // Name part can't be used in listElement attribute.
+        digester.addObjectCreate(ADD_LIST_ELE_TAG, PUT_ATTRIBUTE_HANDLER_CLASS);
+        digester.addRule(ADD_LIST_ELE_TAG, new FillAttributeRule());
+        digester.addSetNext(ADD_LIST_ELE_TAG, "add", PUT_ATTRIBUTE_HANDLER_CLASS);
+
+        // nested list elements rules
+        // Create a list handler, and add it to parent list
+        digester.addObjectCreate(NESTED_LIST, LIST_HANDLER_CLASS);
+        digester.addSetProperties(NESTED_LIST);
+        digester.addSetNext(NESTED_LIST, "add", PUT_ATTRIBUTE_HANDLER_CLASS);
+    }
+
+    /**
+     * Adds a new <code>Definition</code> to the internal Map or replaces
+     * an existing one.
+     *
+     * @param definition The Definition object to be added.
+     */
+    public void addDefinition(Definition definition) {
+        String name = definition.getName();
+        if (name == null) {
+            throw new DigesterDefinitionsReaderException("A root definition has been defined with no name");
+        }
+
+        definitions.put(name, definition);
+    }
+
+    /**
+     * Error Handler that throws every exception it receives.
+     */
+    private static class ThrowingErrorHandler implements ErrorHandler {
+
+        /** {@inheritDoc} */
+        public void warning(SAXParseException exception) throws SAXException {
+            throw exception;
+        }
+
+        /** {@inheritDoc} */
+        public void error(SAXParseException exception) throws SAXException {
+            throw exception;
+        }
+
+        /** {@inheritDoc} */
+        public void fatalError(SAXParseException exception) throws SAXException {
+            throw exception;
+        }
+    }
+
+    /**
+     * Returns the registrations for local DTDs.
+     *
+     * @return An array containing the locations for registrations of local
+     * DTDs.
+     * @since 2.1.0
+     */
+    protected String[] getRegistrations() {
+        if (registrations == null) {
+            registrations = new String[] {
+                "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN",
+                "/org/apache/tiles/resources/tiles-config_3_0.dtd"};
+        }
+        return registrations;
+    }
+
+    /**
+     * Create a unique definition name usable to store anonymous definitions.
+     *
+     * @param definitions The already created definitions.
+     * @return The unique definition name to be used to store the definition.
+     * @since 2.1.0
+     */
+    protected String getNextUniqueDefinitionName(
+            Map<String, Definition> definitions) {
+        String candidate;
+
+        do {
+            candidate = "$anonymousDefinition" + anonymousDefinitionIndex;
+            anonymousDefinitionIndex++;
+        } while (definitions.containsKey(candidate));
+
+        return candidate;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/digester/DigesterDefinitionsReaderException.java
similarity index 53%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/digester/DigesterDefinitionsReaderException.java
index 38e506552..cd9c1c6c5 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/digester/DigesterDefinitionsReaderException.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
+package org.apache.tiles.core.definition.digester;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+import org.apache.tiles.api.TilesException;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * Indicates that something went wrong during the use of
+ * {@link DigesterDefinitionsReader}.
  *
- * @since Struts 2.0.2
+ * @version $Rev$ $Date$
+ * @since 2.1.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public class DigesterDefinitionsReaderException extends TilesException {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @since 2.1.0
+     */
+    public DigesterDefinitionsReaderException(String message) {
+        super(message);
     }
-}
\ No newline at end of file
+
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/digester/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/digester/package-info.java
index 38e506552..f76e46d68 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/digester/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Allows reading definitions with the use of Jakarta Commons Digester.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.core.definition.digester;
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/package-info.java
index 38e506552..2e570e061 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * It contains classes and interfaces to allow manipulations of "definitions", i.e.
+ * objects made of a template page and a number of filled attributes.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.core.definition;
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/AbstractPatternDefinitionResolver.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/AbstractPatternDefinitionResolver.java
new file mode 100644
index 000000000..dae01776f
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/AbstractPatternDefinitionResolver.java
@@ -0,0 +1,108 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.definition.pattern;
+
+import org.apache.tiles.api.Definition;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A pattern definition resolver that stores {@link DefinitionPatternMatcher}
+ * separated by customization key. <br>
+ * Implementations should provide a way to translate a definition to a
+ * {@link DefinitionPatternMatcher}.
+ *
+ * @param <T> The type of the customization key.
+ * @since 2.2.0
+ */
+public abstract class AbstractPatternDefinitionResolver<T> implements PatternDefinitionResolver<T> {
+
+    /**
+     * Stores patterns depending on the locale they refer to.
+     */
+    private final Map<T, List<DefinitionPatternMatcher>> localePatternPaths = new HashMap<>();
+
+    /** {@inheritDoc} */
+    public Definition resolveDefinition(String name, T customizationKey) {
+        Definition retValue = null;
+        if (localePatternPaths.containsKey(customizationKey)) {
+            retValue = searchAndResolveDefinition(localePatternPaths
+                    .get(customizationKey), name);
+        }
+        return retValue;
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, Definition> storeDefinitionPatterns(Map<String, Definition> localeDefsMap, T customizationKey) {
+        List<DefinitionPatternMatcher> lpaths = localePatternPaths.computeIfAbsent(customizationKey, k -> new ArrayList<>());
+        return addDefinitionsAsPatternMatchers(lpaths, localeDefsMap);
+    }
+
+    /**
+     * Adds definitions, filtering and adding them to the list of definition
+     * pattern matchers. Only a subset of definitions will be transformed into
+     * definition pattern matchers.
+     *
+     * @param matchers The list containing the currently stored definition pattern
+     * matchers.
+     * @param defsMap The definition map to parse.
+     * @return The map of the definitions not recognized as containing
+     * definition patterns.
+     * @since 2.2.1
+     */
+    protected abstract Map<String, Definition> addDefinitionsAsPatternMatchers(List<DefinitionPatternMatcher> matchers, Map<String, Definition> defsMap);
+
+    /**
+     * Try to resolve a definition by iterating all pattern matchers.
+     *
+     * @param paths The list containing the currently stored paths.
+     * @param name The name of the definition to resolve.
+     * @return A definition, if found, or <code>null</code> if not.
+     */
+    private Definition searchAndResolveDefinition(List<DefinitionPatternMatcher> paths, String name) {
+        Definition d = null;
+
+        for (DefinitionPatternMatcher wm : paths) {
+            d = wm.createDefinition(name);
+            if (d != null) {
+                break;
+            }
+        }
+
+        return d;
+    }
+
+
+    /**
+     * Used to clear all entries in the localePatternPaths for a specific locale. Necessary when reloading definition
+     * files to ensure that the list is cleared first
+     *
+     * @param customizationKey customization key
+     */
+    @Override
+    public void clearPatternPaths(T customizationKey) {
+        if (localePatternPaths.get(customizationKey) != null)
+            localePatternPaths.get(customizationKey).clear();
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/BasicPatternDefinitionResolver.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/BasicPatternDefinitionResolver.java
new file mode 100644
index 000000000..a2d5fbed7
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/BasicPatternDefinitionResolver.java
@@ -0,0 +1,77 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.definition.pattern;
+
+import org.apache.tiles.api.Definition;
+
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A pattern definition resolver that stores {@link org.apache.tiles.core.definition.pattern.DefinitionPatternMatcher}
+ * separated by customization key. <br>
+ * It delegates creation of definition pattern matchers to a
+ * {@link DefinitionPatternMatcherFactory} and recgnizes patterns through the
+ * use of a {@link PatternRecognizer}.
+ *
+ * @param <T> The type of the customization key.
+ * @since 2.2.0
+ */
+public class BasicPatternDefinitionResolver<T> extends AbstractPatternDefinitionResolver<T> {
+
+    /**
+     * The factory of pattern matchers.
+     */
+    private final DefinitionPatternMatcherFactory definitionPatternMatcherFactory;
+
+    /**
+     * The pattern recognizer.
+     */
+    private final PatternRecognizer patternRecognizer;
+
+    /**
+     * Constructor.
+     *
+     * @param definitionPatternMatcherFactory The definition pattern matcher factory.
+     * @param patternRecognizer The pattern recognizer.
+     */
+    public BasicPatternDefinitionResolver(DefinitionPatternMatcherFactory definitionPatternMatcherFactory, PatternRecognizer patternRecognizer) {
+        this.definitionPatternMatcherFactory = definitionPatternMatcherFactory;
+        this.patternRecognizer = patternRecognizer;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Map<String, Definition> addDefinitionsAsPatternMatchers(List<DefinitionPatternMatcher> matchers, Map<String, Definition> defsMap) {
+        Set<String> excludedKeys = new LinkedHashSet<>();
+        for (Map.Entry<String, Definition> de : defsMap.entrySet()) {
+            String key = de.getKey();
+            if (patternRecognizer.isPatternRecognized(key)) {
+                matchers.add(definitionPatternMatcherFactory.createDefinitionPatternMatcher(key, de.getValue()));
+            } else {
+                excludedKeys.add(key);
+            }
+        }
+        return PatternUtil.createExtractedMap(defsMap, excludedKeys);
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/DefinitionPatternMatcher.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/DefinitionPatternMatcher.java
index 38e506552..59461af25 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/DefinitionPatternMatcher.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
+package org.apache.tiles.core.definition.pattern;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+import org.apache.tiles.api.Definition;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * Matches a definition name to a definition, through pattern-matching. The
+ * matched pattern should be a single one.
  *
- * @since Struts 2.0.2
+ * @version $Rev$ $Date$
+ * @since 2.2.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public interface DefinitionPatternMatcher {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+    /**
+     * Creates a definition, given the definition name, through the use of
+     * pattern matching.
+     *
+     * @param definitionName The definition name to match.
+     * @return The created definition, if matched, or <code>null</code> if not
+     * matched.
+     * @since 2.2.0
+     */
+    Definition createDefinition(String definitionName);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/DefinitionPatternMatcherFactory.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/DefinitionPatternMatcherFactory.java
new file mode 100644
index 000000000..454dada62
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/DefinitionPatternMatcherFactory.java
@@ -0,0 +1,45 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.definition.pattern;
+
+import org.apache.tiles.api.Definition;
+
+/**
+ * Creates a new definition pattern matcher for the given pattern and the given
+ * base definition with pattern expressions.
+ *
+ * @version $Rev$ $Date$
+ * @since 2.2.0
+ */
+public interface DefinitionPatternMatcherFactory {
+
+    /**
+     * Creates a new definition pattern matcher.
+     *
+     * @param pattern The pattern to be matched.
+     * @param definition The base definition. Created definitions by
+     * {@link DefinitionPatternMatcher#createDefinition(String)} will created
+     * with this one as a basis.
+     * @return The definition pattern matcher.
+     * @since 2.2.0
+     */
+    DefinitionPatternMatcher createDefinitionPatternMatcher(String pattern, Definition definition);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternDefinitionResolver.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternDefinitionResolver.java
new file mode 100644
index 000000000..07c78157d
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternDefinitionResolver.java
@@ -0,0 +1,66 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.definition.pattern;
+
+import org.apache.tiles.api.Definition;
+
+import java.util.Map;
+
+/**
+ * Resolves a definition starting from patterns stored in definition maps.
+ *
+ * @param <T> The type of the customization key.
+ * @version $Rev$ $Date$
+ * @since 2.2.0
+ */
+public interface PatternDefinitionResolver<T> {
+
+    /**
+     * Stores definition patterns.
+     *
+     * @param localeDefsMap The map of definitions that may contain also
+     * patterns.
+     * @param customizationKey The customization key.
+     * @return The map of the definitions not recognized as containing
+     * definition patterns.
+     * @since 2.2.1
+     */
+    Map<String, Definition> storeDefinitionPatterns(Map<String, Definition> localeDefsMap, T customizationKey);
+
+    /**
+     * Resolves a definition searching in all patterns for the requested
+     * customization key.
+     *
+     * @param name The name of the definition.
+     * @param customizationKey The customization key.
+     * @return The resolved definition.
+     * @since 2.2.0
+     */
+    Definition resolveDefinition(String name, T customizationKey);
+
+    /**
+     * Used to clear all entries in the localePatternPaths for a specific locale. Necessary when reloading definition
+     * files to ensure that the list is cleared first
+     *
+     * @param customizationKey customization key
+     */
+    void clearPatternPaths(T customizationKey);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternDefinitionResolverAware.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternDefinitionResolverAware.java
index 38e506552..790a64e8c 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternDefinitionResolverAware.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+package org.apache.tiles.core.definition.pattern;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * It indicates an object that uses a {@link org.apache.tiles.core.definition.pattern.PatternDefinitionResolver}.
  *
- * @since Struts 2.0.2
+ * @param <T> The type of the customization key.
+ * @since 2.2.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public interface PatternDefinitionResolverAware<T> {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+    /**
+     * Sets the pattern definition resolver to use.
+     *
+     * @param definitionResolver The pattern definition resolver.
+     * @since 2.2.0
+     */
+    void setPatternDefinitionResolver(PatternDefinitionResolver<T> definitionResolver);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternRecognizer.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternRecognizer.java
index 38e506552..3ae899f9e 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternRecognizer.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+package org.apache.tiles.core.definition.pattern;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * Checks if a pattern (or a candidate one) is recognized as a pattern.
  *
- * @since Struts 2.0.2
+ * @since 2.2.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public interface PatternRecognizer {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+    /**
+     * Checks if a pattern is recognized as a pattern.
+     *
+     * @param candidatePattern The pattern to check.
+     * @return <code>true</code> if the pattern has been recognized.
+     * @since 2.2.0
+     */
+    boolean isPatternRecognized(String candidatePattern);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternUtil.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternUtil.java
new file mode 100644
index 000000000..d7894b922
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PatternUtil.java
@@ -0,0 +1,242 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.core.definition.pattern;
+
+import org.apache.tiles.api.Attribute;
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.api.Expression;
+import org.apache.tiles.api.ListAttribute;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Utilities for pattern matching and substitution.
+ *
+ * @since 2.2.0
+ */
+public final class PatternUtil {
+
+    /**
+     * The root locale. Notice that this is a replacement for {@link Locale#ROOT} for
+     * Java 1.6.
+     */
+    private static final Locale ROOT_LOCALE = new Locale("", "");
+
+    /** Pattern to find {.*} occurrences that do not match {[0-9]+} so to prevent MessageFormat from crashing.
+     */
+    private static final Pattern INVALID_FORMAT_ELEMENT = Pattern.compile("\\{[^}0-9]+}");
+
+    /**
+     * Private constructor to avoid instantiation.
+     */
+    private PatternUtil() {
+    }
+
+    /**
+     * Creates a definition given its representation with wildcards and
+     * attribute values with placeholders, replacing real values into
+     * placeholders.
+     *
+     * @param d The definition to replace.
+     * @param name The name of the definition to be created.
+     * @param varsOrig The variables to be substituted.
+     * @return The definition that can be rendered.
+     * @since 2.2.0
+     */
+    public static Definition replacePlaceholders(Definition d, String name,
+                                                 Object... varsOrig) {
+
+        Object[] vars = replaceNullsWithBlank(varsOrig);
+
+        Definition nudef = new Definition();
+
+        nudef.setExtends(replace(d.getExtends(), vars));
+        nudef.setName(name);
+        nudef.setPreparer(replace(d.getPreparer(), vars));
+        Attribute templateAttribute = d.getTemplateAttribute();
+        if (templateAttribute != null) {
+            nudef.setTemplateAttribute(replaceVarsInAttribute(
+                    templateAttribute, vars));
+        }
+
+        Set<String> attributeNames = d.getLocalAttributeNames();
+        if (attributeNames != null && !attributeNames.isEmpty()) {
+            for (String attributeName : attributeNames) {
+                Attribute attr = d.getLocalAttribute(attributeName);
+                Attribute nuattr = replaceVarsInAttribute(attr, vars);
+
+                nudef.putAttribute(replace(attributeName, vars), nuattr);
+            }
+        }
+
+        attributeNames = d.getCascadedAttributeNames();
+        if (attributeNames != null && !attributeNames.isEmpty()) {
+            for (String attributeName : attributeNames) {
+                Attribute attr = d.getCascadedAttribute(attributeName);
+                Attribute nuattr = replaceVarsInAttribute(attr, vars);
+
+                nudef.putAttribute(replace(attributeName, vars), nuattr, true);
+            }
+        }
+
+        return nudef;
+    }
+
+    /**
+     * Creates a new map that contains all the entries of the
+     * <code>defsMap</code> whose keys are contained in <code>keys</code>.
+     *
+     * @param map The map to read.
+     * @param keys The keys to extract.
+     * @param <K> The key of the map.
+     * @param <V> The value of the map.
+     * @return The extracted map.
+     * @since 2.2.1
+     */
+    public static <K, V> Map<K, V> createExtractedMap(Map<K, V> map, Set<K> keys) {
+        Map<K, V> retValue = new LinkedHashMap<>();
+        for (K key : keys) {
+            retValue.put(key, map.get(key));
+        }
+        return retValue;
+    }
+
+    /**
+     * Replaces variables into an attribute.
+     *
+     * @param attr The attribute to be used as a basis, containing placeholders
+     * for variables.
+     * @param vars The variables to replace.
+     * @return A new instance of an attribute, whose properties have been
+     * replaced with variables' values.
+     */
+    private static Attribute replaceVarsInAttribute(Attribute attr,
+            Object... vars) {
+        Attribute nuattr;
+        if (attr instanceof ListAttribute) {
+            nuattr = replaceVarsInListAttribute((ListAttribute) attr, vars);
+        } else {
+            nuattr = replaceVarsInSimpleAttribute(attr, vars);
+        }
+        return nuattr;
+    }
+
+    /**
+     * Replaces variables into a simple (not list) attribute.
+     *
+     * @param attr The attribute to be used as a basis, containing placeholders
+     * for variables.
+     * @param vars The variables to replace.
+     * @return A new instance of an attribute, whose properties have been
+     * replaced with variables' values.
+     */
+    private static Attribute replaceVarsInSimpleAttribute(Attribute attr,
+            Object... vars) {
+        Attribute nuattr;
+        nuattr = new Attribute();
+
+        nuattr.setRole(replace(attr.getRole(), vars));
+        nuattr.setRenderer(attr.getRenderer());
+        Expression expressionObject = attr.getExpressionObject();
+        if (expressionObject != null) {
+            Expression newExpressionObject = Expression
+                    .createExpression(replace(expressionObject.getExpression(), vars), expressionObject.getLanguage());
+            nuattr.setExpressionObject(newExpressionObject);
+        }
+
+        Object value = attr.getValue();
+        if (value instanceof String) {
+            value = replace((String) value, vars);
+        }
+        nuattr.setValue(value);
+        return nuattr;
+    }
+
+    /**
+     * Replaces variables into a list attribute.
+     *
+     * @param listAttr The attribute to be used as a basis, containing attributes
+     * that may contain placeholders for variables.
+     * @param vars The variables to replace.
+     * @return A new instance of an attribute, whose properties have been
+     * replaced with variables' values.
+     */
+    private static Attribute replaceVarsInListAttribute(ListAttribute listAttr,
+            Object... vars) {
+        Attribute nuattr;
+        ListAttribute nuListAttr = new ListAttribute();
+        nuListAttr.setInherit(listAttr.isInherit());
+        List<Attribute> nuItems = nuListAttr.getValue();
+        for (Attribute item : listAttr.getValue()) {
+            Attribute child = item;
+            child = replaceVarsInAttribute(child, vars);
+            nuItems.add(child);
+        }
+        nuattr = nuListAttr;
+        return nuattr;
+    }
+
+    /**
+     * Replaces a string with placeholders using values of a variable map.
+     *
+     * @param st The string to replace.
+     * @param vars The variables.
+     * @return The replaced string.
+     */
+    private static String replace(String st, Object... vars) {
+        if (st != null && st.indexOf('{') >= 0) {
+
+            // replace them with markers
+            List<String> originals = new ArrayList<>();
+            for(Matcher m = INVALID_FORMAT_ELEMENT.matcher(st); m.find() ; m = INVALID_FORMAT_ELEMENT.matcher(st)) {
+                originals.add(m.group());
+                st = m.replaceFirst("INVALID_FORMAT_ELEMENT");
+            }
+
+            // do the MessageFormat replacement (escaping quote characters)
+            st = new MessageFormat(st.replaceAll("'", "'''"), ROOT_LOCALE)
+                    .format(vars, new StringBuffer(), null).toString();
+
+            // return the markers to their original invalid occurrences
+            for (String original : originals) {
+                st = st.replaceFirst("INVALID_FORMAT_ELEMENT", original);
+            }
+        }
+        return st;
+    }
+
+    private static Object[] replaceNullsWithBlank(Object[] varsOrig) {
+        Object[] vars = new Object[varsOrig.length];
+        for(int i = 0; i < varsOrig.length; ++i) {
+            vars[i] = null != varsOrig[i] ? varsOrig[i] : "";
+        }
+        return vars;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PrefixedPatternDefinitionResolver.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PrefixedPatternDefinitionResolver.java
new file mode 100644
index 000000000..f50f4498b
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/PrefixedPatternDefinitionResolver.java
@@ -0,0 +1,106 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.definition.pattern;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.api.Expression;
+
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This resolver allows the use of multiple pattern matching languages. The
+ * syntax of definition names must be <code>LANGUAGENAME:expression</code>.<br>
+ * The different languages must be registered through the use of
+ * {@link #registerDefinitionPatternMatcherFactory(String, DefinitionPatternMatcherFactory)}
+ * method before using this resolver.
+ *
+ * @param <T> The type of the customization key.
+ * @version $Rev$ $Date$
+ * @since 2.2.0
+ */
+public class PrefixedPatternDefinitionResolver<T> extends AbstractPatternDefinitionResolver<T> {
+
+    /**
+     * The logging object.
+     */
+    private static final Logger LOG = LogManager.getLogger(PrefixedPatternDefinitionResolver.class);
+
+    /**
+     * Matches languages names to the corresponding
+     * {@link DefinitionPatternMatcherFactory}.
+     */
+    private final Map<String, DefinitionPatternMatcherFactory> language2matcherFactory;
+
+    /**
+     * Constructor.
+     *
+     * @since 2.2.0
+     */
+    public PrefixedPatternDefinitionResolver() {
+        language2matcherFactory = new HashMap<>();
+    }
+
+    /**
+     * Registers a {@link DefinitionPatternMatcherFactory} connected to a
+     * particular language.
+     *
+     * @param language The name of the language.
+     * @param factory The pattern matcher factory to register.
+     * @since 2.2.0
+     */
+    public void registerDefinitionPatternMatcherFactory(String language,
+            DefinitionPatternMatcherFactory factory) {
+        language2matcherFactory.put(language, factory);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Map<String, Definition> addDefinitionsAsPatternMatchers(List<DefinitionPatternMatcher> matchers, Map<String, Definition> defsMap) {
+        Set<String> excludedKeys = new LinkedHashSet<String>();
+        for (Map.Entry<String, Definition> entry : defsMap.entrySet()) {
+            String key = entry.getKey();
+            Expression expression = Expression
+                    .createExpressionFromDescribedExpression(key);
+            if (expression.getLanguage() != null) {
+                DefinitionPatternMatcherFactory factory = language2matcherFactory
+                        .get(expression.getLanguage());
+                if (factory != null) {
+                    DefinitionPatternMatcher matcher = factory
+                            .createDefinitionPatternMatcher(expression
+                                    .getExpression(), new Definition(entry
+                                    .getValue()));
+                    matchers.add(matcher);
+                } else {
+                    LOG.warn("Cannot find a DefinitionPatternMatcherFactory for expression '{}'", key);
+                }
+            } else {
+                excludedKeys.add(key);
+            }
+        }
+        return PatternUtil.createExtractedMap(defsMap, excludedKeys);
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/package-info.java
index 38e506552..13c53ec83 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Classes to manage pattern matching in definition names, and substitution in attributes.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.core.definition.pattern;
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/regexp/RegexpDefinitionPatternMatcher.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/regexp/RegexpDefinitionPatternMatcher.java
new file mode 100644
index 000000000..b575c2a27
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/regexp/RegexpDefinitionPatternMatcher.java
@@ -0,0 +1,74 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.core.definition.pattern.regexp;
+
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.core.definition.pattern.DefinitionPatternMatcher;
+import org.apache.tiles.core.definition.pattern.PatternUtil;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Matches regular expression patterns in definitions.
+ *
+ * @since 2.2.0
+ */
+public class RegexpDefinitionPatternMatcher implements DefinitionPatternMatcher {
+
+    /**
+     * The pattern to match.
+     */
+    private final Pattern pattern;
+
+    /**
+     * The definition to use as a basis.
+     */
+    private final Definition definition;
+
+    /**
+     * Constructor.
+     *
+     * @param pattern The pattern to use, in string form.
+     * @param definition The definition to use as a basis.
+     * @since 2.2.0
+     */
+    public RegexpDefinitionPatternMatcher(String pattern, Definition definition) {
+        this.pattern = Pattern.compile(pattern);
+        this.definition = definition;
+    }
+
+    /** {@inheritDoc} */
+    public Definition createDefinition(String definitionName) {
+        Definition retValue = null;
+        Matcher matcher = pattern.matcher(definitionName);
+        if (matcher.matches()) {
+            int groupCount = matcher.groupCount() + 1;
+            Object[] vars = new Object[groupCount];
+            for (int i = 0; i < groupCount; i++) {
+                vars[i] = matcher.group(i);
+            }
+            retValue = PatternUtil.replacePlaceholders(definition, definitionName, vars);
+        }
+        return retValue;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/regexp/RegexpDefinitionPatternMatcherFactory.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/regexp/RegexpDefinitionPatternMatcherFactory.java
index 38e506552..3cbffaf99 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/regexp/RegexpDefinitionPatternMatcherFactory.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
+package org.apache.tiles.core.definition.pattern.regexp;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.core.definition.pattern.DefinitionPatternMatcher;
+import org.apache.tiles.core.definition.pattern.DefinitionPatternMatcherFactory;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Creates instances of {@link RegexpDefinitionPatternMatcher}.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public class RegexpDefinitionPatternMatcherFactory implements DefinitionPatternMatcherFactory {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
+    /**
+     * {@inheritDoc}
+     */
+    public DefinitionPatternMatcher createDefinitionPatternMatcher(String pattern, Definition definition) {
+        return new RegexpDefinitionPatternMatcher(pattern, definition);
     }
-}
\ No newline at end of file
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/regexp/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/regexp/package-info.java
index 38e506552..15389afdb 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/regexp/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * In Tiles it is possible to use regular expression patterns thanks to this package.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.core.definition.pattern.regexp;
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/wildcard/WildcardDefinitionPatternMatcher.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/wildcard/WildcardDefinitionPatternMatcher.java
new file mode 100644
index 000000000..fe2d54349
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/wildcard/WildcardDefinitionPatternMatcher.java
@@ -0,0 +1,80 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.definition.pattern.wildcard;
+
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.core.definition.pattern.DefinitionPatternMatcher;
+import org.apache.tiles.core.definition.pattern.PatternUtil;
+import org.apache.tiles.core.util.WildcardHelper;
+
+import java.util.List;
+
+/**
+ * Matches wildcard patterns in definitions.
+ *
+ * @since 2.2.0
+ */
+public class WildcardDefinitionPatternMatcher implements DefinitionPatternMatcher {
+
+    /**
+     * Allows to parse wildcard expressions and to recognize substitution
+     * variables.
+     */
+    private final WildcardHelper wildcardHelper;
+
+    /**
+     * The definition to use as a basis.
+     */
+    private final Definition definition;
+
+    /**
+     * The pattern to use.
+     */
+    private final int[] pattern;
+
+    /**
+     * Constructor.
+     *
+     * @param pattern The pattern to use, in string form.
+     * @param definition The definition to use as a basis.
+     * @param wildcardHelper The object that parses wildcard expressions and
+     * recognized substitution variables.
+     * @since 2.2.0
+     */
+    public WildcardDefinitionPatternMatcher(String pattern,
+            Definition definition, WildcardHelper wildcardHelper) {
+        this.wildcardHelper = wildcardHelper;
+        this.definition = definition;
+        this.pattern = wildcardHelper.compilePattern(pattern);
+    }
+
+    /** {@inheritDoc} */
+    public Definition createDefinition(String definitionName) {
+        List<String> vars = wildcardHelper.match(definitionName, pattern);
+        Definition d = null;
+
+        if (vars != null) {
+            d = PatternUtil.replacePlaceholders(definition, definitionName, vars.toArray());
+        }
+
+        return d;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/wildcard/WildcardDefinitionPatternMatcherFactory.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/wildcard/WildcardDefinitionPatternMatcherFactory.java
new file mode 100644
index 000000000..0804a9d21
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/wildcard/WildcardDefinitionPatternMatcherFactory.java
@@ -0,0 +1,56 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.definition.pattern.wildcard;
+
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.core.definition.pattern.DefinitionPatternMatcher;
+import org.apache.tiles.core.definition.pattern.DefinitionPatternMatcherFactory;
+import org.apache.tiles.core.definition.pattern.PatternRecognizer;
+import org.apache.tiles.core.util.WildcardHelper;
+
+/**
+ * Creates instances of {@link WildcardDefinitionPatternMatcher}.
+ *
+ * @since 2.2.0
+ */
+public class WildcardDefinitionPatternMatcherFactory implements DefinitionPatternMatcherFactory, PatternRecognizer {
+
+    /**
+     * Allows to parse wildcard expressions and to recognize substitution
+     * variables.
+     */
+    private final WildcardHelper wildcardHelper = new WildcardHelper();
+
+    /**
+     * {@inheritDoc}
+     */
+    public DefinitionPatternMatcher createDefinitionPatternMatcher(
+        String pattern, Definition definition) {
+        return new WildcardDefinitionPatternMatcher(pattern, definition, wildcardHelper);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isPatternRecognized(String candidatePattern) {
+        return candidatePattern.indexOf('*') >= 0;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/wildcard/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/wildcard/package-info.java
index 38e506552..dfbb05225 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/definition/pattern/wildcard/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * In Tiles it is possible to use wildcard patterns thanks to this package.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.core.definition.pattern.wildcard;
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AbstractAttributeEvaluator.java b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AbstractAttributeEvaluator.java
new file mode 100644
index 000000000..702ba5a6d
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AbstractAttributeEvaluator.java
@@ -0,0 +1,52 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.evaluator;
+
+import org.apache.tiles.api.Attribute;
+import org.apache.tiles.api.Expression;
+import org.apache.tiles.request.Request;
+
+/**
+ * Abstract class to link a correct evaluation of an attribute, by evaluating
+ * {@link Attribute#getValue()} and then {@link Attribute#getExpressionObject()}.
+ *
+ * @since 2.1.2
+ */
+public abstract class AbstractAttributeEvaluator implements AttributeEvaluator {
+
+    /** {@inheritDoc} */
+    public Object evaluate(Attribute attribute, Request request) {
+        if (attribute == null) {
+            throw new IllegalArgumentException("The attribute cannot be null");
+        }
+
+        Object retValue = attribute.getValue();
+
+        if (retValue == null) {
+            Expression expression = attribute.getExpressionObject();
+            if (expression != null) {
+                retValue = evaluate(attribute.getExpressionObject().getExpression(), request);
+            }
+        }
+
+        return retValue;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AttributeEvaluator.java b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AttributeEvaluator.java
new file mode 100644
index 000000000..93feb168a
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AttributeEvaluator.java
@@ -0,0 +1,52 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.evaluator;
+
+import org.apache.tiles.api.Attribute;
+import org.apache.tiles.request.Request;
+
+/**
+ * It represents an object that resolves a string to return an object.
+ *
+ * @since 2.1.0
+ */
+public interface AttributeEvaluator {
+
+    /**
+     * Evaluates an expression.
+     *
+     * @param expression The expression to evaluate.
+     * @param request    The request object.
+     * @return The evaluated object.
+     * @since 2.1.0
+     */
+    Object evaluate(String expression, Request request);
+
+    /**
+     * Evaluates an attribute value.
+     *
+     * @param attribute The attribute to evaluate.
+     * @param request   The request object.
+     * @return The evaluated object.
+     * @since 2.1.0
+     */
+    Object evaluate(Attribute attribute, Request request);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AttributeEvaluatorFactory.java b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AttributeEvaluatorFactory.java
new file mode 100644
index 000000000..efac52ed4
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AttributeEvaluatorFactory.java
@@ -0,0 +1,50 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tiles.core.evaluator;
+
+import org.apache.tiles.api.Attribute;
+
+/**
+ * Creates an attribute evaluator using the language or an attribute.
+ *
+ * @since 2.2.0
+ */
+public interface AttributeEvaluatorFactory {
+
+    /**
+     * Creates and attribute evaluator using an attribute.
+     *
+     * @param attribute The attribute used to obtain the evaluator.
+     * @return The attribute evaluator. It must not be <code>null</code>.
+     * @since 2.2.0
+     */
+    AttributeEvaluator getAttributeEvaluator(Attribute attribute);
+
+    /**
+     * Creates and attribute evaluator for the given expression language.
+     *
+     * @param language The name of the expression language.
+     * @return The attribute evaluator. It must not be <code>null</code>.
+     * @since 2.2.0
+     */
+    AttributeEvaluator getAttributeEvaluator(String language);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AttributeEvaluatorFactoryAware.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AttributeEvaluatorFactoryAware.java
index 38e506552..9e472c555 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/AttributeEvaluatorFactoryAware.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+package org.apache.tiles.core.evaluator;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * It represents an object that can use an {@link AttributeEvaluatorFactory}.
  *
- * @since Struts 2.0.2
+ * @since 2.2.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public interface AttributeEvaluatorFactoryAware {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+    /**
+     * Sets the attribute evaluator factory.
+     *
+     * @param attributeEvaluatorFactory The attribute evaluator factory to use.
+     * @since 2.2.0
+     */
+    void setAttributeEvaluatorFactory(AttributeEvaluatorFactory attributeEvaluatorFactory);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/BasicAttributeEvaluatorFactory.java b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/BasicAttributeEvaluatorFactory.java
new file mode 100644
index 000000000..e6f8a47b4
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/BasicAttributeEvaluatorFactory.java
@@ -0,0 +1,90 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.evaluator;
+
+import org.apache.tiles.api.Attribute;
+import org.apache.tiles.api.Expression;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Basic implementation of {@link AttributeEvaluatorFactory}. It supports a
+ * default attribute evaluator, in case the language is not recognized.
+ *
+ * @since 2.2.0
+ */
+public class BasicAttributeEvaluatorFactory implements AttributeEvaluatorFactory {
+
+    /**
+     * The default evaluator to return if it is not found in the map of known
+     * languages.
+     */
+    private final AttributeEvaluator defaultEvaluator;
+
+    /**
+     * Maps names of expression languages to their attribute evaluator.
+     *
+     * @since 2.2.0
+     */
+    private final Map<String, AttributeEvaluator> language2evaluator;
+
+    /**
+     * Constructor.
+     *
+     * @param defaultEvaluator The default evaluator to return if it is not
+     * found in the map of known languages.
+     * @since 2.2.0
+     */
+    public BasicAttributeEvaluatorFactory(AttributeEvaluator defaultEvaluator) {
+        this.defaultEvaluator = defaultEvaluator;
+        language2evaluator = new HashMap<>();
+    }
+
+    /**
+     * Registers a known expression language with its attribute evaluator.
+     *
+     * @param language The name of the expression language.
+     * @param evaluator The associated attribute evaluator.
+     * @since 2.2.0
+     */
+    public void registerAttributeEvaluator(String language, AttributeEvaluator evaluator) {
+        language2evaluator.put(language, evaluator);
+    }
+
+    /** {@inheritDoc} */
+    public AttributeEvaluator getAttributeEvaluator(String language) {
+        AttributeEvaluator retValue = language2evaluator.get(language);
+        if (retValue == null) {
+            retValue = defaultEvaluator;
+        }
+        return retValue;
+    }
+
+    /** {@inheritDoc} */
+    public AttributeEvaluator getAttributeEvaluator(Attribute attribute) {
+        Expression expression = attribute.getExpressionObject();
+        if (expression != null) {
+            return getAttributeEvaluator(expression.getLanguage());
+        }
+        return defaultEvaluator;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/EvaluationException.java
similarity index 53%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/EvaluationException.java
index 38e506552..7f5cb9c80 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/EvaluationException.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,35 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
+package org.apache.tiles.core.evaluator;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+import org.apache.tiles.api.TilesException;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * Exception raised when an expression language evaluation fails.
  *
- * @since Struts 2.0.2
+ * @since 2.2.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
+public class EvaluationException extends TilesException {
 
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+    /**
+     * Constructor.
+     *
+     * @param e The cause.
+     * @since 2.2.0
+     */
+    public EvaluationException(Throwable e) {
+        super(e);
+    }
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
+    /**
+     * Constructor.
+     *
+     * @param message The message-
+     * @param e       The cause.
+     * @since 2.2.0
+     */
+    public EvaluationException(String message, Throwable e) {
+        super(message, e);
     }
-}
\ No newline at end of file
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/impl/DirectAttributeEvaluator.java
similarity index 53%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/impl/DirectAttributeEvaluator.java
index 38e506552..35157812d 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/impl/DirectAttributeEvaluator.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,21 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
+package org.apache.tiles.core.evaluator.impl;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+import org.apache.tiles.core.evaluator.AbstractAttributeEvaluator;
+import org.apache.tiles.request.Request;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * Resolves a string and returns the string itself. It is useful for backward
+ * compatibility.
  *
- * @since Struts 2.0.2
+ * @since 2.1.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public class DirectAttributeEvaluator extends AbstractAttributeEvaluator {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
+    /** {@inheritDoc} */
+    public Object evaluate(String expression, Request request) {
+        return expression;
     }
-}
\ No newline at end of file
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/impl/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/impl/package-info.java
index 38e506552..40bc521ec 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/impl/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Classes to manage attribute value evaluation.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.core.evaluator.impl;
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/package-info.java
index 38e506552..da5b4bc26 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/evaluator/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Interfaces to manage attribute value evaluation.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.core.evaluator;
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/factory/AbstractTilesContainerFactory.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/factory/AbstractTilesContainerFactory.java
index 38e506552..fc5ca1f17 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/factory/AbstractTilesContainerFactory.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
+package org.apache.tiles.core.factory;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+import org.apache.tiles.api.TilesContainer;
+import org.apache.tiles.request.ApplicationContext;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * Abstract Factory that creates instances of {@link TilesContainer}.
  *
- * @since Struts 2.0.2
+ * @since 2.1.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public abstract class AbstractTilesContainerFactory {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+    /**
+     * Creates a Tiles container.
+     *
+     * @param applicationContext The Tiles application context object.
+     * @return The created container.
+     * @throws TilesContainerFactoryException If something goes wrong during
+     * instantiation.
+     * @since 2.1.1
+     */
+    public abstract TilesContainer createContainer(ApplicationContext applicationContext);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/factory/BasicTilesContainerFactory.java b/plugins/tiles/src/main/java/org/apache/tiles/core/factory/BasicTilesContainerFactory.java
new file mode 100644
index 000000000..311172e28
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/factory/BasicTilesContainerFactory.java
@@ -0,0 +1,408 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.factory;
+
+import org.apache.tiles.api.TilesContainer;
+import org.apache.tiles.core.definition.DefinitionsFactory;
+import org.apache.tiles.core.definition.DefinitionsReader;
+import org.apache.tiles.core.definition.UnresolvingLocaleDefinitionsFactory;
+import org.apache.tiles.core.definition.dao.BaseLocaleUrlDefinitionDAO;
+import org.apache.tiles.core.definition.dao.DefinitionDAO;
+import org.apache.tiles.core.definition.dao.ResolvingLocaleUrlDefinitionDAO;
+import org.apache.tiles.core.definition.digester.DigesterDefinitionsReader;
+import org.apache.tiles.core.definition.pattern.BasicPatternDefinitionResolver;
+import org.apache.tiles.core.definition.pattern.PatternDefinitionResolver;
+import org.apache.tiles.core.definition.pattern.PatternDefinitionResolverAware;
+import org.apache.tiles.core.definition.pattern.wildcard.WildcardDefinitionPatternMatcherFactory;
+import org.apache.tiles.core.evaluator.AttributeEvaluatorFactory;
+import org.apache.tiles.core.evaluator.BasicAttributeEvaluatorFactory;
+import org.apache.tiles.core.evaluator.impl.DirectAttributeEvaluator;
+import org.apache.tiles.core.impl.BasicTilesContainer;
+import org.apache.tiles.core.locale.LocaleResolver;
+import org.apache.tiles.core.locale.impl.DefaultLocaleResolver;
+import org.apache.tiles.core.prepare.factory.BasicPreparerFactory;
+import org.apache.tiles.core.prepare.factory.PreparerFactory;
+import org.apache.tiles.core.renderer.DefinitionRenderer;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.ApplicationResource;
+import org.apache.tiles.request.render.BasicRendererFactory;
+import org.apache.tiles.request.render.ChainedDelegateRenderer;
+import org.apache.tiles.request.render.DispatchRenderer;
+import org.apache.tiles.request.render.Renderer;
+import org.apache.tiles.request.render.RendererFactory;
+import org.apache.tiles.request.render.StringRenderer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Factory that builds a standard Tiles container using only Java code.
+ *
+ * @since 2.1.0
+ */
+public class BasicTilesContainerFactory extends AbstractTilesContainerFactory {
+
+    /**
+     * The string renderer name.
+     */
+    protected static final String STRING_RENDERER_NAME = "string";
+
+    /**
+     * The template renderer name.
+     */
+    protected static final String TEMPLATE_RENDERER_NAME = "template";
+
+    /**
+     * The definition renderer name.
+     */
+    protected static final String DEFINITION_RENDERER_NAME = "definition";
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public TilesContainer createContainer(ApplicationContext applicationContext) {
+        BasicTilesContainer container = instantiateContainer(applicationContext);
+        container.setApplicationContext(applicationContext);
+        LocaleResolver resolver = createLocaleResolver(applicationContext);
+        container.setDefinitionsFactory(createDefinitionsFactory(applicationContext, resolver));
+        AttributeEvaluatorFactory attributeEvaluatorFactory = createAttributeEvaluatorFactory(applicationContext, resolver);
+        container.setAttributeEvaluatorFactory(attributeEvaluatorFactory);
+        container.setPreparerFactory(createPreparerFactory(applicationContext));
+        TilesContainer injectedContainer = createDecoratedContainer(container, applicationContext);
+        container.setRendererFactory(createRendererFactory(applicationContext, injectedContainer, attributeEvaluatorFactory));
+        return injectedContainer;
+    }
+
+    /**
+     * Instantiate the container, without initialization.
+     *
+     * @param context The Tiles application context object.
+     * @return The instantiated container.
+     * @since 2.1.1
+     */
+    protected BasicTilesContainer instantiateContainer(ApplicationContext context) {
+        return new BasicTilesContainer();
+    }
+
+    /**
+     * Instantiate the container that will be injected to child objects.
+     *
+     * @param originalContainer The original instantiated container.
+     * @param context           The Tiles application context object.
+     * @return The instantiated container.
+     * @since 3.0.0
+     */
+    protected TilesContainer createDecoratedContainer(
+        TilesContainer originalContainer,
+        ApplicationContext context
+    ) {
+        return originalContainer;
+    }
+
+    /**
+     * Creates the definitions factory. By default it creates a
+     * {@link UnresolvingLocaleDefinitionsFactory} with default dependencies.
+     *
+     * @param applicationContext The Tiles application context.
+     * @param resolver           The locale resolver.
+     * @return The definitions factory.
+     * @since 2.1.1
+     */
+    protected DefinitionsFactory createDefinitionsFactory(
+        ApplicationContext applicationContext,
+        LocaleResolver resolver
+    ) {
+        UnresolvingLocaleDefinitionsFactory factory = instantiateDefinitionsFactory(applicationContext, resolver);
+        factory.setLocaleResolver(resolver);
+        factory.setDefinitionDAO(createLocaleDefinitionDao(applicationContext, resolver));
+        return factory;
+    }
+
+    /**
+     * Instantiate a new definitions factory based on Locale.
+     *
+     * @param applicationContext The Tiles application context.
+     * @param resolver           The locale resolver.
+     * @return The definitions factory.
+     * @since 2.2.1
+     */
+    protected UnresolvingLocaleDefinitionsFactory instantiateDefinitionsFactory(
+        ApplicationContext applicationContext,
+        LocaleResolver resolver
+    ) {
+        return new UnresolvingLocaleDefinitionsFactory();
+    }
+
+
+    /**
+     * Instantiate (and does not initialize) a Locale-based definition DAO.
+     *
+     * @param applicationContext The Tiles application context.
+     * @param resolver           The locale resolver.
+     * @return The definition DAO.
+     * @since 2.1.1
+     */
+    protected BaseLocaleUrlDefinitionDAO instantiateLocaleDefinitionDao(
+        ApplicationContext applicationContext,
+        LocaleResolver resolver
+    ) {
+        return new ResolvingLocaleUrlDefinitionDAO(applicationContext);
+    }
+
+    /**
+     * Creates a Locale-based definition DAO.
+     *
+     * @param applicationContext The Tiles application context.
+     * @param resolver           The locale resolver.
+     * @return The definition DAO.
+     * @since 2.1.1
+     */
+    @SuppressWarnings("unchecked")
+    protected DefinitionDAO<Locale> createLocaleDefinitionDao(ApplicationContext applicationContext, LocaleResolver resolver) {
+        BaseLocaleUrlDefinitionDAO definitionDao = instantiateLocaleDefinitionDao(applicationContext, resolver);
+        definitionDao.setReader(createDefinitionsReader(applicationContext));
+        definitionDao.setSources(getSources(applicationContext));
+        if (definitionDao instanceof PatternDefinitionResolverAware) {
+            ((PatternDefinitionResolverAware<Locale>) definitionDao)
+                .setPatternDefinitionResolver(createPatternDefinitionResolver(Locale.class));
+        }
+        return definitionDao;
+    }
+
+    /**
+     * Creates the locale resolver. By default it creates a
+     * {@link DefaultLocaleResolver}.
+     *
+     * @param applicationContext The Tiles application context.
+     * @return The locale resolver.
+     * @since 2.1.1
+     */
+    protected LocaleResolver createLocaleResolver(ApplicationContext applicationContext) {
+        return new DefaultLocaleResolver();
+    }
+
+    /**
+     * Creates the definitions reader. By default it creates a
+     * {@link DigesterDefinitionsReader}.
+     *
+     * @param applicationContext The Tiles application context.
+     * @return The definitions reader.
+     * @since 2.1.1
+     */
+    protected DefinitionsReader createDefinitionsReader(ApplicationContext applicationContext) {
+        return new DigesterDefinitionsReader();
+    }
+
+    /**
+     * Returns a list containing the resources to be parsed. By default, it returns a
+     * list containing the resource at "/WEB-INF/tiles.xml".
+     *
+     * @param applicationContext The Tiles application context.
+     * @return The resources.
+     * @since 2.1.1
+     */
+    protected List<ApplicationResource> getSources(ApplicationContext applicationContext) {
+        List<ApplicationResource> retValue = new ArrayList<>(1);
+        retValue.add(applicationContext.getResource("/WEB-INF/tiles.xml"));
+        return retValue;
+    }
+
+    /**
+     * Creates the attribute evaluator factory to use. By default it returns a
+     * {@link BasicAttributeEvaluatorFactory} containing the
+     * {@link DirectAttributeEvaluator} as the default evaluator.
+     *
+     * @param applicationContext The Tiles application context.
+     * @param resolver           The locale resolver.
+     * @return The evaluator factory.
+     * @since 2.2.0
+     */
+    protected AttributeEvaluatorFactory createAttributeEvaluatorFactory(
+        ApplicationContext applicationContext,
+        LocaleResolver resolver) {
+        return new BasicAttributeEvaluatorFactory(new DirectAttributeEvaluator());
+    }
+
+    /**
+     * Creates the preparer factory to use. By default it returns a
+     * {@link BasicPreparerFactory}.
+     *
+     * @param applicationContext The Tiles application context.
+     * @return The preparer factory.
+     * @since 2.1.1
+     */
+    protected PreparerFactory createPreparerFactory(ApplicationContext applicationContext) {
+        return new BasicPreparerFactory();
+    }
+
+    /**
+     * Creates a renderer factory. By default, it returns a
+     * {@link BasicRendererFactory}, composed of an
+     * {@link ChainedDelegateRenderer} as default, and delegates of
+     * {@link StringRenderer}, {@link DispatchRenderer},
+     * {@link DefinitionRenderer}.
+     *
+     * @param applicationContext        The Tiles application context.
+     * @param container                 The container.
+     * @param attributeEvaluatorFactory The attribute evaluator factory.
+     * @return The renderer factory.
+     * @since 2.2.0
+     */
+    protected RendererFactory createRendererFactory(ApplicationContext applicationContext,
+                                                    TilesContainer container,
+                                                    AttributeEvaluatorFactory attributeEvaluatorFactory) {
+        BasicRendererFactory retValue = new BasicRendererFactory();
+        registerAttributeRenderers(retValue, applicationContext, container,
+            attributeEvaluatorFactory);
+        retValue.setDefaultRenderer(createDefaultAttributeRenderer(retValue,
+            applicationContext, container, attributeEvaluatorFactory));
+        return retValue;
+    }
+
+    /**
+     * Creates the default attribute renderer. By default it is an
+     * {@link ChainedDelegateRenderer}.
+     *
+     * @param rendererFactory           The renderer factory to configure.
+     * @param applicationContext        The Tiles application context.
+     * @param container                 The container.
+     * @param attributeEvaluatorFactory The attribute evaluator factory.
+     * @return The default attribute renderer.
+     * @since 3.0.0
+     */
+    protected Renderer createDefaultAttributeRenderer(
+        BasicRendererFactory rendererFactory,
+        ApplicationContext applicationContext,
+        TilesContainer container,
+        AttributeEvaluatorFactory attributeEvaluatorFactory) {
+        ChainedDelegateRenderer retValue = new ChainedDelegateRenderer();
+        retValue.addAttributeRenderer(rendererFactory.getRenderer(DEFINITION_RENDERER_NAME));
+        retValue.addAttributeRenderer(rendererFactory.getRenderer(TEMPLATE_RENDERER_NAME));
+        retValue.addAttributeRenderer(rendererFactory.getRenderer(STRING_RENDERER_NAME));
+        return retValue;
+    }
+
+    /**
+     * Creates a new pattern definition resolver. By default, it instantiate a
+     * {@link BasicPatternDefinitionResolver} with
+     * {@link WildcardDefinitionPatternMatcherFactory} to manage wildcard
+     * substitution.
+     *
+     * @param <T>                   The type of the customization key.
+     * @param customizationKeyClass The customization key class.
+     * @return The pattern definition resolver.
+     * @since 2.2.0
+     */
+    protected <T> PatternDefinitionResolver<T> createPatternDefinitionResolver(Class<T> customizationKeyClass) {
+        WildcardDefinitionPatternMatcherFactory definitionPatternMatcherFactory = new WildcardDefinitionPatternMatcherFactory();
+        return new BasicPatternDefinitionResolver<>(definitionPatternMatcherFactory, definitionPatternMatcherFactory);
+    }
+
+    /**
+     * Registers attribute renderers in a {@link BasicRendererFactory}. By
+     * default, it registers delegates to {@link StringRenderer},
+     * {@link DispatchRenderer} and {@link DefinitionRenderer}.
+     *
+     * @param rendererFactory           The renderer factory to configure.
+     * @param applicationContext        The Tiles application context.
+     * @param container                 The container.
+     * @param attributeEvaluatorFactory The attribute evaluator factory.
+     * @since 2.2.0
+     */
+    protected void registerAttributeRenderers(
+        BasicRendererFactory rendererFactory,
+        ApplicationContext applicationContext,
+        TilesContainer container,
+        AttributeEvaluatorFactory attributeEvaluatorFactory
+    ) {
+        rendererFactory.registerRenderer(
+            STRING_RENDERER_NAME,
+            createStringAttributeRenderer(rendererFactory, applicationContext, container, attributeEvaluatorFactory)
+        );
+        rendererFactory.registerRenderer(
+            TEMPLATE_RENDERER_NAME,
+            createTemplateAttributeRenderer(rendererFactory, applicationContext, container, attributeEvaluatorFactory)
+        );
+        rendererFactory.registerRenderer(
+            DEFINITION_RENDERER_NAME,
+            createDefinitionAttributeRenderer(rendererFactory, applicationContext, container, attributeEvaluatorFactory)
+        );
+    }
+
+    /**
+     * Creates an attribute renderer to render strings.
+     *
+     * @param rendererFactory           The renderer factory to configure.
+     * @param applicationContext        The Tiles application context.
+     * @param container                 The container.
+     * @param attributeEvaluatorFactory The attribute evaluator factory.
+     * @return The renderer.
+     * @since 3.0.0
+     */
+    protected Renderer createStringAttributeRenderer(
+        BasicRendererFactory rendererFactory,
+        ApplicationContext applicationContext,
+        TilesContainer container,
+        AttributeEvaluatorFactory attributeEvaluatorFactory
+    ) {
+        return new StringRenderer();
+    }
+
+    /**
+     * Creates a {@link Renderer} that uses a {@link DispatchRenderer}.
+     *
+     * @param rendererFactory           The renderer factory to configure.
+     * @param applicationContext        The Tiles application context.
+     * @param container                 The container.
+     * @param attributeEvaluatorFactory The attribute evaluator factory.
+     * @return The renderer.
+     * @since 2.2.1
+     */
+    protected Renderer createTemplateAttributeRenderer(
+        BasicRendererFactory rendererFactory,
+        ApplicationContext applicationContext,
+        TilesContainer container,
+        AttributeEvaluatorFactory attributeEvaluatorFactory
+    ) {
+        return new DispatchRenderer();
+    }
+
+    /**
+     * Creates a {@link Renderer} using a {@link DefinitionRenderer}.
+     *
+     * @param rendererFactory           The renderer factory to configure.
+     * @param applicationContext        The Tiles application context.
+     * @param container                 The container.
+     * @param attributeEvaluatorFactory The attribute evaluator factory.
+     * @return The renderer.
+     * @since 3.0.0
+     */
+    protected Renderer createDefinitionAttributeRenderer(
+        BasicRendererFactory rendererFactory,
+        ApplicationContext applicationContext,
+        TilesContainer container,
+        AttributeEvaluatorFactory attributeEvaluatorFactory
+    ) {
+        return new DefinitionRenderer(container);
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/factory/TilesContainerFactoryException.java
similarity index 53%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/factory/TilesContainerFactoryException.java
index 38e506552..24d927788 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/factory/TilesContainerFactoryException.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,26 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
+package org.apache.tiles.core.factory;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+import org.apache.tiles.api.TilesException;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * Indicates that something went wrong in {@link AbstractTilesContainerFactory} use.
  *
- * @since Struts 2.0.2
+ * @since 2.1.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public class TilesContainerFactoryException extends TilesException {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @param e The exception to be wrapped.
+     * @since 2.1.0
+     */
+    public TilesContainerFactoryException(String message, Throwable e) {
+        super(message, e);
     }
-}
\ No newline at end of file
+
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/factory/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/factory/package-info.java
index 38e506552..c305b60c7 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/factory/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * Factory classes, to allow creation of container instances.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.core.factory;
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/impl/BasicTilesContainer.java b/plugins/tiles/src/main/java/org/apache/tiles/core/impl/BasicTilesContainer.java
new file mode 100644
index 000000000..400c3bc44
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/impl/BasicTilesContainer.java
@@ -0,0 +1,400 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.impl;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.tiles.api.Attribute;
+import org.apache.tiles.api.AttributeContext;
+import org.apache.tiles.api.BasicAttributeContext;
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.api.TilesContainer;
+import org.apache.tiles.api.preparer.ViewPreparer;
+import org.apache.tiles.core.definition.DefinitionsFactory;
+import org.apache.tiles.core.definition.NoSuchDefinitionException;
+import org.apache.tiles.core.evaluator.AttributeEvaluator;
+import org.apache.tiles.core.evaluator.AttributeEvaluatorFactory;
+import org.apache.tiles.core.evaluator.AttributeEvaluatorFactoryAware;
+import org.apache.tiles.core.prepare.factory.NoSuchPreparerException;
+import org.apache.tiles.core.prepare.factory.PreparerFactory;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.render.CannotRenderException;
+import org.apache.tiles.request.render.Renderer;
+import org.apache.tiles.request.render.RendererFactory;
+
+import java.io.IOException;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * Basic implementation of the tiles container interface.
+ * In most cases, this container will be customized by
+ * injecting customized services, not necessarily by
+ * override the container
+ *
+ * @since 2.0
+ */
+public class BasicTilesContainer implements TilesContainer, AttributeEvaluatorFactoryAware {
+
+    /**
+     * Name used to store attribute context stack.
+     */
+    private static final String ATTRIBUTE_CONTEXT_STACK =
+        "org.apache.tiles.AttributeContext.STACK";
+
+    /**
+     * Log instance for all BasicTilesContainer
+     * instances.
+     */
+    private static final Logger LOG = LogManager.getLogger(BasicTilesContainer.class);
+
+    /**
+     * The Tiles application context object.
+     */
+    private ApplicationContext context;
+
+    /**
+     * The definitions factory.
+     */
+    private DefinitionsFactory definitionsFactory;
+
+    /**
+     * The preparer factory.
+     */
+    private PreparerFactory preparerFactory;
+
+    /**
+     * The renderer factory.
+     */
+    private RendererFactory rendererFactory;
+
+    /**
+     * The attribute evaluator.
+     */
+    private AttributeEvaluatorFactory attributeEvaluatorFactory;
+
+    /**
+     * {@inheritDoc}
+     */
+    public AttributeContext startContext(Request request) {
+        AttributeContext context = new BasicAttributeContext();
+        Deque<AttributeContext> stack = getContextStack(request);
+        if (!stack.isEmpty()) {
+            AttributeContext parent = stack.peek();
+            context.inheritCascadedAttributes(parent);
+        }
+        stack.push(context);
+        return context;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void endContext(Request request) {
+        popContext(request);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void renderContext(Request request) {
+        AttributeContext attributeContext = getAttributeContext(request);
+
+        render(request, attributeContext);
+    }
+
+    /**
+     * Returns the Tiles application context used by this container.
+     *
+     * @return the application context for this container.
+     */
+    public ApplicationContext getApplicationContext() {
+        return context;
+    }
+
+    /**
+     * Sets the Tiles application context to use.
+     *
+     * @param context The Tiles application context.
+     */
+    public void setApplicationContext(ApplicationContext context) {
+        this.context = context;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public AttributeContext getAttributeContext(Request request) {
+        AttributeContext context = getContext(request);
+        if (context == null) {
+            context = new BasicAttributeContext();
+            pushContext(context, request);
+        }
+        return context;
+
+    }
+
+    /**
+     * Set the definitions factory. This method first ensures
+     * that the container has not yet been initialized.
+     *
+     * @param definitionsFactory the definitions factory for this instance.
+     */
+    public void setDefinitionsFactory(DefinitionsFactory definitionsFactory) {
+        this.definitionsFactory = definitionsFactory;
+    }
+
+    /**
+     * Set the preparerInstance factory.  This method first ensures
+     * that the container has not yet been initialized.
+     *
+     * @param preparerFactory the preparerInstance factory for this conainer.
+     */
+    public void setPreparerFactory(PreparerFactory preparerFactory) {
+        this.preparerFactory = preparerFactory;
+    }
+
+    /**
+     * Sets the renderer instance factory.
+     *
+     * @param rendererFactory the renderer instance factory for this container.
+     * @since 2.1.0
+     */
+    public void setRendererFactory(RendererFactory rendererFactory) {
+        this.rendererFactory = rendererFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setAttributeEvaluatorFactory(
+        AttributeEvaluatorFactory attributeEvaluatorFactory) {
+        this.attributeEvaluatorFactory = attributeEvaluatorFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void prepare(String preparer, Request request) {
+        prepare(request, preparer, false);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void render(String definitionName, Request request) {
+        LOG.debug("Render request received for definition '{}'", definitionName);
+
+        Definition definition = getDefinition(definitionName, request);
+
+        if (definition == null) {
+            throw new NoSuchDefinitionException("Unable to find the definition '" + definitionName + "'");
+        }
+
+        render(definition, request);
+    }
+
+    /**
+     * Renders the specified definition.
+     *
+     * @param definition The definition to render.
+     * @param request    The request context.
+     * @since 2.1.3
+     */
+    public void render(Definition definition, Request request) {
+        AttributeContext originalContext = getAttributeContext(request);
+        BasicAttributeContext subContext = new BasicAttributeContext(originalContext);
+        subContext.inherit(definition);
+
+        pushContext(subContext, request);
+
+        try {
+            render(request, subContext);
+        } finally {
+            popContext(request);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void render(Attribute attr, Request request)
+        throws IOException {
+        if (attr == null) {
+            throw new CannotRenderException("Cannot render a null attribute");
+        }
+
+        if (attr.isPermitted(request)) {
+            Renderer renderer = rendererFactory.getRenderer(attr.getRenderer());
+            Object value = evaluate(attr, request);
+            if (!(value instanceof String)) {
+                throw new CannotRenderException(
+                    "Cannot render an attribute that is not a string, toString returns: "
+                        + value);
+            }
+            renderer.render((String) value, request);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object evaluate(Attribute attribute, Request request) {
+        AttributeEvaluator evaluator = attributeEvaluatorFactory.getAttributeEvaluator(attribute);
+        return evaluator.evaluate(attribute, request);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isValidDefinition(String definitionName, Request request) {
+        try {
+            Definition definition = getDefinition(definitionName, request);
+            return definition != null;
+        } catch (NoSuchDefinitionException nsde) {
+            LOG.debug("Cannot find definition '{}'", definitionName);
+            LOG.debug("Exception related to the not found definition", nsde);
+            return false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Definition getDefinition(String definitionName, Request request) {
+        return definitionsFactory.getDefinition(definitionName, request);
+    }
+
+    /**
+     * Returns the context stack.
+     *
+     * @param tilesContext The Tiles context object to use.
+     * @return The needed stack of contexts.
+     * @since 2.0.6
+     */
+    @SuppressWarnings("unchecked")
+    protected Deque<AttributeContext> getContextStack(Request tilesContext) {
+        Map<String, Object> requestScope = tilesContext.getContext("request");
+        Deque<AttributeContext> contextStack = (Deque<AttributeContext>) requestScope
+            .get(ATTRIBUTE_CONTEXT_STACK);
+        if (contextStack == null) {
+            contextStack = new LinkedList<>();
+            requestScope.put(ATTRIBUTE_CONTEXT_STACK, contextStack);
+        }
+
+        return contextStack;
+    }
+
+    /**
+     * Pushes a context object in the stack.
+     *
+     * @param context      The context to push.
+     * @param tilesContext The Tiles context object to use.
+     * @since 2.0.6
+     */
+    protected void pushContext(AttributeContext context,
+                               Request tilesContext) {
+        Deque<AttributeContext> contextStack = getContextStack(tilesContext);
+        contextStack.push(context);
+    }
+
+    /**
+     * Pops a context object out of the stack.
+     *
+     * @param tilesContext The Tiles context object to use.
+     * @return The popped context object.
+     * @since 2.0.6
+     */
+    protected AttributeContext popContext(Request tilesContext) {
+        Deque<AttributeContext> contextStack = getContextStack(tilesContext);
+        return contextStack.pop();
+    }
+
+    /**
+     * Get attribute context from request.
+     *
+     * @param tilesContext current Tiles application context.
+     * @return BasicAttributeContext or null if context is not found.
+     * @since 2.0.6
+     */
+    protected AttributeContext getContext(Request tilesContext) {
+        Deque<AttributeContext> contextStack = getContextStack(tilesContext);
+        if (!contextStack.isEmpty()) {
+            return contextStack.peek();
+        }
+        return null;
+    }
+
+    /**
+     * Execute a preparer.
+     *
+     * @param context       The request context.
+     * @param preparerName  The name of the preparer.
+     * @param ignoreMissing If <code>true</code> if the preparer is not found,
+     *                      it ignores the problem.
+     * @throws NoSuchPreparerException If the preparer is not found (and
+     *                                 <code>ignoreMissing</code> is not set) or if the preparer itself threw an
+     *                                 exception.
+     */
+    private void prepare(Request context, String preparerName, boolean ignoreMissing) {
+
+        LOG.debug("Prepare request received for '{}'", preparerName);
+
+        ViewPreparer preparer = preparerFactory.getPreparer(preparerName, context);
+        if (preparer == null && ignoreMissing) {
+            return;
+        }
+
+        if (preparer == null) {
+            throw new NoSuchPreparerException("Preparer '" + preparerName + " not found");
+        }
+
+        AttributeContext attributeContext = getContext(context);
+
+        preparer.execute(context, attributeContext);
+    }
+
+    /**
+     * Renders the specified attribute context.
+     *
+     * @param request          The request context.
+     * @param attributeContext The context to render.
+     * @throws InvalidTemplateException If the template is not valid.
+     * @throws CannotRenderException    If something goes wrong during rendering.
+     * @since 2.1.3
+     */
+    protected void render(Request request,
+                          AttributeContext attributeContext) {
+
+        try {
+            if (attributeContext.getPreparer() != null) {
+                prepare(request, attributeContext.getPreparer(), true);
+            }
+
+            render(attributeContext.getTemplateAttribute(), request);
+        } catch (IOException e) {
+            throw new CannotRenderException(e.getMessage(), e);
+        }
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/impl/InvalidTemplateException.java
similarity index 53%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/impl/InvalidTemplateException.java
index 38e506552..51c46b215 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/impl/InvalidTemplateException.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,35 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
+package org.apache.tiles.core.impl;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+import org.apache.tiles.api.TilesException;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
+ * An invalid template has been identified.
  *
- * @since Struts 2.0.2
+ * @since 2.1.0
  */
-public class StrutsTilesListener extends AbstractTilesListener {
+public class InvalidTemplateException extends TilesException {
 
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @since 2.1.0
+     */
+    public InvalidTemplateException(String message) {
+        super(message);
+    }
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
+    /**
+     * Constructor.
+     *
+     * @param e The exception to be wrapped.
+     * @since 2.1.0
+     */
+    public InvalidTemplateException(Throwable e) {
+        super(e);
     }
-}
\ No newline at end of file
+
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/impl/mgmt/CachingTilesContainer.java b/plugins/tiles/src/main/java/org/apache/tiles/core/impl/mgmt/CachingTilesContainer.java
new file mode 100644
index 000000000..40e64c906
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/impl/mgmt/CachingTilesContainer.java
@@ -0,0 +1,224 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tiles.core.impl.mgmt;
+
+import org.apache.tiles.api.Definition;
+import org.apache.tiles.api.TilesContainer;
+import org.apache.tiles.api.TilesContainerWrapper;
+import org.apache.tiles.api.mgmt.MutableTilesContainer;
+import org.apache.tiles.core.definition.NoSuchDefinitionException;
+import org.apache.tiles.request.Request;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Manages custom and configured definitions, so they can be used by the
+ * container, instead of using a simple {@link org.apache.tiles.core.definition.DefinitionsFactory}.
+ */
+public class CachingTilesContainer extends TilesContainerWrapper implements MutableTilesContainer {
+
+    /**
+     * The default name of the attribute in which storing custom definitions.
+     */
+    private static final String DEFAULT_DEFINITIONS_ATTRIBUTE_NAME = "org.apache.tiles.impl.mgmt.DefinitionManager.DEFINITIONS";
+
+    /**
+     * The name of the attribute in which storing custom definitions.
+     */
+    private final String definitionsAttributeName;
+
+    /**
+     * Constructor.
+     *
+     * @param originalContainer The original container to wrap.
+     */
+    public CachingTilesContainer(TilesContainer originalContainer) {
+        super(originalContainer);
+        definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
+    }
+
+    /**
+     * Returns a definition by name.
+     *
+     * @param definition The name of the definition.
+     * @param request    The current request.
+     * @return The requested definition, either main or custom.
+     */
+    public Definition getDefinition(String definition, Request request) {
+        Definition retValue;
+        retValue = getCustomDefinition(definition, request);
+        if (retValue == null) {
+            retValue = super.getDefinition(definition, request);
+        }
+        return retValue;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isValidDefinition(String definition, Request request) {
+        if (getCustomDefinition(definition, request) != null) {
+            return true;
+        }
+        return super.isValidDefinition(definition, request);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void register(Definition definition, Request request) {
+        Map<String, Definition> definitions = getOrCreateDefinitions(request);
+        if (definition.getName() == null) {
+            definition.setName(getNextUniqueDefinitionName(definitions));
+        }
+
+        if (definition.isExtending()) {
+            this.resolveInheritance(definition, request);
+        }
+
+        definitions.put(definition.getName(), definition);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void render(String definition, Request request) {
+        Definition toRender = getDefinition(definition, request);
+        if (toRender == null) {
+            throw new NoSuchDefinitionException("Cannot find definition named '" + definition + "'");
+        }
+        super.render(toRender, request);
+    }
+
+    /**
+     * Resolve inheritance.
+     * First, resolve parent's inheritance, then set template to the parent's
+     * template.
+     * Also copy attributes set in parent, and not set in child
+     * If instance doesn't extend anything, do nothing.
+     *
+     * @param definition The definition that needs to have its inheritances
+     *                   resolved.
+     * @param request    The current request.
+     * @throws org.apache.tiles.core.definition.DefinitionsFactoryException If an
+     *                                                                      inheritance can not be solved.
+     */
+    private void resolveInheritance(Definition definition,
+                                    Request request) {
+        // Already done, or not needed ?
+        if (!definition.isExtending()) {
+            return;
+        }
+
+        String parentDefinitionName = definition.getExtends();
+
+        boolean recurse = true;
+        Definition parent = getCustomDefinition(parentDefinitionName, request);
+        if (parent == null) {
+            parent = container.getDefinition(parentDefinitionName, request);
+            recurse = false;
+        }
+
+        if (parent == null) {
+            throw new NoSuchDefinitionException(
+                "Error while resolving definition inheritance: child '"
+                    + definition.getName()
+                    + "' can't find its ancestor '"
+                    + parentDefinitionName
+                    + "'. Please check your description file.");
+        }
+
+        // Resolve parent before itself.
+        if (recurse) {
+            resolveInheritance(parent, request);
+        }
+        definition.inherit(parent);
+    }
+
+    /**
+     * Returns the map with custom definitions for the current request.
+     *
+     * @param request The current request.
+     * @return A map that connects a definition name to a definition.
+     */
+    @SuppressWarnings("unchecked")
+    private Map<String, Definition> getDefinitions(
+        Request request) {
+        return (Map<String, Definition>) request.getContext("request")
+            .get(definitionsAttributeName);
+    }
+
+    /**
+     * Returns a map of type "definition name -> definition" and, if it has not
+     * been defined before, creates one.
+     *
+     * @param request The current request.
+     * @return A map that connects a definition name to a definition.
+     */
+    @SuppressWarnings("unchecked")
+    private Map<String, Definition> getOrCreateDefinitions(Request request) {
+        Map<String, Definition> definitions = (Map<String, Definition>) request.getContext("request").get(definitionsAttributeName);
+        if (definitions == null) {
+            definitions = new HashMap<>();
+            request.getContext("request").put(definitionsAttributeName, definitions);
+        }
+
+        return definitions;
+    }
+
+    /**
+     * Create a unique definition name usable to store anonymous definitions.
+     *
+     * @param definitions The already created definitions.
+     * @return The unique definition name to be used to store the definition.
+     * @since 2.1.0
+     */
+    private String getNextUniqueDefinitionName(Map<String, Definition> definitions) {
+        String candidate;
+        int anonymousDefinitionIndex = 1;
+
+        do {
+            candidate = "$anonymousMutableDefinition" + anonymousDefinitionIndex;
+            anonymousDefinitionIndex++;
+        } while (definitions.containsKey(candidate));
+
+        return candidate;
+    }
+
+    /**
+     * Returns a custom definition from the cache.
+     *
+     * @param definition The definition to search.
+     * @param request    The request.
+     * @return The requested definition.
+     */
+    private Definition getCustomDefinition(String definition, Request request) {
+        Map<String, Definition> definitions = getDefinitions(request);
+        if (definitions != null) {
+            return definitions.get(definition);
+        }
+        return null;
+    }
+}
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/impl/mgmt/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/impl/mgmt/package-info.java
index 38e506552..ad44a742f 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/impl/mgmt/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * It contains the basic implementations of mutable Tiles containers.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.core.impl.mgmt;
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/impl/package-info.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/impl/package-info.java
index 38e506552..638599c00 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/impl/package-info.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
-
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * It contains the basic implementations of Tiles container.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
-
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+package org.apache.tiles.core.impl;
diff --git a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java b/plugins/tiles/src/main/java/org/apache/tiles/core/locale/LocaleResolver.java
similarity index 52%
copy from plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
copy to plugins/tiles/src/main/java/org/apache/tiles/core/locale/LocaleResolver.java
index 38e506552..e377fa8d6 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesListener.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/locale/LocaleResolver.java
@@ -1,4 +1,6 @@
 /*
+ * $Id$
+ *
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -7,7 +9,7 @@
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
  *
- *  http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
@@ -16,25 +18,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.struts2.tiles;
+package org.apache.tiles.core.locale;
+
+import org.apache.tiles.request.Request;
 
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.tiles.startup.TilesInitializer;
-import org.apache.tiles.web.startup.AbstractTilesListener;
+import java.util.Locale;
 
 /**
- * Listener used to automatically tie Tiles support into Struts
- *
- * @since Struts 2.0.2
+ * It represents an object able to resolve the current locale for the current
+ * request, where its strategy depends on its implementation.
  */
-public class StrutsTilesListener extends AbstractTilesListener {
-
-    private static final Logger LOG = LogManager.getLogger(StrutsTilesListener.class);
+public interface LocaleResolver {
 
-    @Override
-    protected TilesInitializer createTilesInitializer() {
-        LOG.info("Starting Struts Tiles 3 integration ...");
-        return new StrutsTilesInitializer();
-    }
-}
\ No newline at end of file
+    /**
+     * Resolves the locale.
+     *
+     * @param request The Tiles request object.
+     * @return The current locale for the current request.
+     */
+    Locale resolveLocale(Request request);
+}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/core/locale/impl/DefaultLocaleResolver.java b/plugins/tiles/src/main/java/org/apache/tiles/core/locale/impl/DefaultLocaleResolver.java
new file mode 100644
index 000000000..b592978a0
--- /dev/null
+++ b/plugins/tiles/src/main/java/org/apache/tiles/core/locale/impl/DefaultLocaleResolver.java
@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
... 15824 lines suppressed ...