moved reactor aside as a tag

git-svn-id: https://svn.apache.org/repos/asf/tiles/framework/tags/tiles-3-reactor@1333558 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/tiles-autotag/maven-autotag-plugin/pom.xml b/tiles-autotag/maven-autotag-plugin/pom.xml
new file mode 100644
index 0000000..72a7365
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/pom.xml
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>tiles-autotag</artifactId>
+    <groupId>org.apache.tiles</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.tiles.autotag.plugin</groupId>
+  <artifactId>maven-autotag-plugin</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>maven-plugin</packaging>
+  <name>maven-autotag-plugin Maven Mojo</name>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>2.2.1</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.8.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+    	<groupId>org.codehaus.plexus</groupId>
+    	<artifactId>plexus-compiler-api</artifactId>
+    	<version>1.7</version>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.tiles</groupId>
+    	<artifactId>tiles-autotag-core</artifactId>
+    	<version>1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+    	<groupId>com.thoughtworks.xstream</groupId>
+    	<artifactId>xstream</artifactId>
+    	<version>1.3.1</version>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.tiles</groupId>
+    	<artifactId>tiles-autotag-jsp</artifactId>
+    	<version>1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.maven</groupId>
+    	<artifactId>maven-project</artifactId>
+    	<version>2.2.1</version>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.tiles</groupId>
+    	<artifactId>tiles-autotag-freemarker</artifactId>
+    	<version>1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.tiles</groupId>
+    	<artifactId>tiles-autotag-velocity</artifactId>
+    	<version>1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+    	<groupId>org.easymock</groupId>
+    	<artifactId>easymock</artifactId>
+    	<version>3.0</version>
+    	<type>jar</type>
+    	<scope>test</scope>
+    </dependency>
+    <dependency>
+    	<groupId>commons-io</groupId>
+    	<artifactId>commons-io</artifactId>
+    	<version>2.0</version>
+    	<scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojo.java b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojo.java
new file mode 100644
index 0000000..b78e1ec
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojo.java
@@ -0,0 +1,175 @@
+/*
+ * $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.plugin;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.apache.tiles.autotag.generate.TemplateGenerator;
+import org.apache.tiles.autotag.generate.TemplateGeneratorFactory;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.velocity.app.VelocityEngine;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.converters.reflection.Sun14ReflectionProvider;
+
+/**
+ * Abstract class to generate boilerplate code starting from template model classes.
+ *
+ * @version $Rev$ $Date$
+ */
+public abstract class AbstractGenerateMojo extends AbstractMojo {
+    /**
+     * The position of the template suite XML descriptor.
+     */
+    static final String META_INF_TEMPLATE_SUITE_XML = "META-INF/template-suite.xml";
+
+    /**
+     * The classpath elements.
+     *
+     * @parameter expression="${project.compileClasspathElements}"
+     * @required
+     * @readonly
+     */
+    List<String> classpathElements;
+
+    /**
+     * Location of the generated classes.
+     *
+     * @parameter expression="${project.build.directory}/autotag-classes"
+     * @required
+     */
+    File classesOutputDirectory;
+
+    /**
+     * Location of the generated resources.
+     *
+     * @parameter expression="${project.build.directory}/autotag-resources"
+     * @required
+     */
+    File resourcesOutputDirectory;
+
+    /**
+     * Name of the package.
+     * @parameter expression="sample"
+     * @required
+     */
+    String packageName;
+
+    /**
+     * @parameter expression="${project}"
+     * @required
+     * @readonly
+     */
+    MavenProject project;
+
+    /** {@inheritDoc} */
+    public void execute() throws MojoExecutionException {
+        try {
+            InputStream stream = findTemplateSuiteDescriptor();
+            XStream xstream = new XStream(new Sun14ReflectionProvider());
+            TemplateSuite suite = (TemplateSuite) xstream.fromXML(stream);
+            stream.close();
+
+            Properties props = new Properties();
+            InputStream propsStream = getClass().getResourceAsStream("/org/apache/tiles/autotag/velocity.properties");
+            props.load(propsStream);
+            propsStream.close();
+            TemplateGenerator generator = createTemplateGeneratorFactory(
+                    new VelocityEngine(props)).createTemplateGenerator();
+            generator.generate(packageName, suite, getParameters(), getRuntimeClass());
+            if (generator.isGeneratingResources()) {
+                Resource resource = new Resource();
+                resource.setDirectory(resourcesOutputDirectory.getAbsolutePath());
+                project.addResource(resource);
+            }
+            if (generator.isGeneratingClasses()) {
+                project.addCompileSourceRoot(classesOutputDirectory.getAbsolutePath());
+            }
+        } catch (IOException e) {
+            throw new MojoExecutionException("error", e);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new MojoExecutionException("error", e);
+        }
+    }
+
+    /**
+     * Creates a template generator factory.
+     *
+     * @param velocityEngine The Velocity engine.
+     * @return The template generator factory.
+     */
+    protected abstract TemplateGeneratorFactory createTemplateGeneratorFactory(VelocityEngine velocityEngine);
+
+    /**
+     * Returns the map of parameters.
+     *
+     * @return The parameters.
+     */
+    protected abstract Map<String, String> getParameters();
+
+    /**
+     * Searches for the template suite descriptor in all dependencies and sources.
+     *
+     * @return The inputstream of the identified descriptor.
+     * @throws IOException If something goes wrong.
+     */
+    private InputStream findTemplateSuiteDescriptor() throws IOException {
+        InputStream retValue = null;
+
+        for (String path : classpathElements) {
+            File file = new File(path);
+            if (file.isDirectory()) {
+                File candidate = new File(file, META_INF_TEMPLATE_SUITE_XML);
+                if (candidate.exists()) {
+                    return new FileInputStream(candidate);
+                }
+            } else if (file.getPath().endsWith(".jar")) {
+                JarFile jar = new JarFile(file);
+                ZipEntry entry = jar.getEntry(META_INF_TEMPLATE_SUITE_XML);
+                if (entry != null) {
+                    return jar.getInputStream(entry);
+                }
+            }
+        }
+
+        return retValue;
+    }
+
+    /**
+     * Name of the Runtime class.
+     * @return The name of the Runtime class.
+     */
+    protected abstract String getRuntimeClass();
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojo.java b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojo.java
new file mode 100644
index 0000000..50fc500
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojo.java
@@ -0,0 +1,179 @@
+/*
+ * $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.plugin;
+
+/*
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.
+ */
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.apache.tiles.autotag.core.QDoxTemplateSuiteFactory;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
+import org.codehaus.plexus.compiler.util.scan.SimpleSourceInclusionScanner;
+import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
+import org.codehaus.plexus.compiler.util.scan.mapping.SourceMapping;
+
+import com.thoughtworks.xstream.XStream;
+
+/**
+ * Creates a descriptor for the template model in XML format.
+ *
+ * @goal create-descriptor
+ *
+ * @phase generate-resources
+ */
+public class CreateDescriptorMojo extends AbstractMojo {
+    /**
+     * Location of the file.
+     *
+     * @parameter expression="${project.build.directory}/autotag-template-suite"
+     * @required
+     */
+    File outputDirectory;
+
+    /**
+     * Location of the file.
+     *
+     * @parameter expression="${project.build.sourceDirectory}"
+     * @required
+     */
+    File sourceDirectory;
+
+    /**
+     * @parameter
+     */
+    Set<String> includes;
+
+    /**
+     * The name of the template.
+     *
+     * @parameter
+     * @required
+     */
+    String name;
+
+    /**
+     * The documentation of the suite.
+     *
+     * @parameter
+     */
+    String documentation;
+
+    /**
+     * @parameter
+     */
+    Set<String> excludes;
+
+    /**
+     * @parameter expression="${project}"
+     * @required
+     * @readonly
+     */
+    MavenProject project;
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    public void execute() throws MojoExecutionException {
+        try {
+            Set<File> filesSet = getSourceInclusionScanner().getIncludedSources(
+                    sourceDirectory, outputDirectory);
+            File[] files = new File[filesSet.size()];
+            QDoxTemplateSuiteFactory factory = new QDoxTemplateSuiteFactory(filesSet.toArray(files));
+            factory.setSuiteName(name);
+            factory.setSuiteDocumentation(documentation);
+            TemplateSuite suite = factory.createTemplateSuite();
+            XStream xstream = new XStream();
+            File dir = new File(outputDirectory, "META-INF");
+            dir.mkdirs();
+            File outputFile = new File(dir, "template-suite.xml");
+            outputFile.createNewFile();
+            Writer writer = new FileWriter(outputFile);
+            xstream.toXML(suite, writer);
+            writer.close();
+            Resource resource = new Resource();
+            resource.setDirectory(outputDirectory.getAbsolutePath());
+            project.addResource(resource);
+        } catch (InclusionScanException e) {
+            throw new MojoExecutionException("error", e);
+        } catch (IOException e) {
+            throw new MojoExecutionException("error", e);
+        }
+    }
+
+    /**
+     * Creates a source inclusion scanner.
+     *
+     * @return The inclusion scanner.
+     */
+    private SourceInclusionScanner getSourceInclusionScanner() {
+        SourceInclusionScanner scanner = null;
+        if (includes == null) {
+            includes = new HashSet<String>();
+        }
+        if (excludes == null) {
+            excludes = new HashSet<String>();
+        }
+
+        if (includes.isEmpty() && excludes.isEmpty()) {
+            includes = Collections.singleton("**/*Model.java");
+            scanner = new SimpleSourceInclusionScanner(includes, excludes);
+        } else {
+            if (includes.isEmpty()) {
+                includes = Collections.singleton("**/*Model.java");
+            }
+            scanner = new SimpleSourceInclusionScanner(includes, excludes);
+        }
+        scanner.addSourceMapping(new SourceMapping() {
+
+            @SuppressWarnings("rawtypes")
+            @Override
+            public Set getTargetFiles(File targetDir, String source) {
+                return null;
+            }
+        });
+
+        return scanner;
+    }
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/GenerateFreemarkerMojo.java b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/GenerateFreemarkerMojo.java
new file mode 100644
index 0000000..9498ec3
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/GenerateFreemarkerMojo.java
@@ -0,0 +1,65 @@
+/*
+ * $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.plugin;
+
+import java.util.Map;
+
+import org.apache.tiles.autotag.freemarker.FMTemplateGeneratorFactory;
+import org.apache.tiles.autotag.generate.TemplateGeneratorBuilder;
+import org.apache.tiles.autotag.generate.TemplateGeneratorFactory;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * Generates Freemarker code.
+ *
+ * @goal generate-freemarker
+ *
+ * @phase generate-sources
+ * @requiresDependencyResolution compile
+ */
+public class GenerateFreemarkerMojo extends AbstractGenerateMojo {
+
+    /**
+     * Name of the Runtime.
+     * @parameter expression="org.apache.tiles.autotag.freemarker.runtime.Runtime"
+     * @required
+     */
+    String freemarkerRuntime;
+
+    /** {@inheritDoc} */
+    @Override
+    protected Map<String, String> getParameters() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String getRuntimeClass() {
+        return freemarkerRuntime;
+    }
+
+    @Override
+    protected TemplateGeneratorFactory createTemplateGeneratorFactory(
+            VelocityEngine velocityEngine) {
+        return new FMTemplateGeneratorFactory(classesOutputDirectory,
+                velocityEngine, TemplateGeneratorBuilder.createNewInstance());
+    }
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/GenerateJspMojo.java b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/GenerateJspMojo.java
new file mode 100644
index 0000000..c76d536
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/GenerateJspMojo.java
@@ -0,0 +1,93 @@
+/*
+ * $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.plugin;
+
+/*
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.
+ */
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.tiles.autotag.generate.TemplateGeneratorBuilder;
+import org.apache.tiles.autotag.generate.TemplateGeneratorFactory;
+import org.apache.tiles.autotag.jsp.JspTemplateGeneratorFactory;
+import org.apache.velocity.app.VelocityEngine;
+
+
+/**
+ * Goal which touches a timestamp file.
+ *
+ * @goal generate-jsp
+ *
+ * @phase generate-sources
+ * @requiresDependencyResolution compile
+ */
+public class GenerateJspMojo extends AbstractGenerateMojo {
+
+    /**
+     * URI of the tag library.
+     *
+     * @parameter expression="http://www.example.com/tags/example"
+     */
+    String taglibURI;
+
+    /**
+     * Name of the Runtime.
+     * @parameter expression="org.apache.tiles.autotag.jsp.runtime.Runtime"
+     * @required
+     */
+    String jspRuntime;
+
+    /** {@inheritDoc} */
+    @Override
+    protected Map<String, String> getParameters() {
+        Map<String, String> params = new HashMap<String, String>();
+        params.put("taglibURI", taglibURI);
+        return params;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String getRuntimeClass() {
+        return jspRuntime;
+    }
+
+    @Override
+    protected TemplateGeneratorFactory createTemplateGeneratorFactory(
+            VelocityEngine velocityEngine) {
+        return new JspTemplateGeneratorFactory(classesOutputDirectory,
+                resourcesOutputDirectory, velocityEngine,
+                TemplateGeneratorBuilder.createNewInstance());
+    }
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/GenerateVelocityMojo.java b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/GenerateVelocityMojo.java
new file mode 100644
index 0000000..bda7ac4
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/GenerateVelocityMojo.java
@@ -0,0 +1,83 @@
+/*
+ * $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.plugin;
+
+/*
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.
+ */
+
+import java.util.Map;
+
+import org.apache.tiles.autotag.generate.TemplateGeneratorBuilder;
+import org.apache.tiles.autotag.generate.TemplateGeneratorFactory;
+import org.apache.tiles.autotag.velocity.VelocityTemplateGeneratorFactory;
+import org.apache.velocity.app.VelocityEngine;
+
+
+/**
+ * Generates Velocity code.
+ *
+ * @goal generate-velocity
+ *
+ * @phase generate-sources
+ * @requiresDependencyResolution compile
+ */
+public class GenerateVelocityMojo extends AbstractGenerateMojo {
+
+    /**
+     * Name of the Runtime.
+     * @parameter expression="org.apache.tiles.autotag.velocity.runtime.Runtime"
+     * @required
+     */
+    String velocityRuntime;
+
+    /** {@inheritDoc} */
+    @Override
+    protected Map<String, String> getParameters() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String getRuntimeClass() {
+        return velocityRuntime;
+    }
+
+    @Override
+    protected TemplateGeneratorFactory createTemplateGeneratorFactory(
+            VelocityEngine velocityEngine) {
+        return new VelocityTemplateGeneratorFactory(classesOutputDirectory,
+                resourcesOutputDirectory, velocityEngine,
+                TemplateGeneratorBuilder.createNewInstance());
+    }
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/package-info.java b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/package-info.java
new file mode 100644
index 0000000..e25ccb0
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/main/java/org/apache/tiles/autotag/plugin/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Contains the autotag mojos.
+ */
+package org.apache.tiles.autotag.plugin;
diff --git a/tiles-autotag/maven-autotag-plugin/src/site/site.xml b/tiles-autotag/maven-autotag-plugin/src/site/site.xml
new file mode 100644
index 0000000..0a7d00d
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Tiles Autotags">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Tiles Autotag"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojoTest.java b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojoTest.java
new file mode 100644
index 0000000..65e47ee
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/AbstractGenerateMojoTest.java
@@ -0,0 +1,93 @@
+/*
+ * $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.plugin;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.apache.tiles.autotag.generate.TemplateGenerator;
+import org.apache.tiles.autotag.generate.TemplateGeneratorFactory;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.Test;
+
+/**
+ * Tests {@link AbstractGenerateMojo}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AbstractGenerateMojoTest {
+
+    /**
+     * Tests {@link AbstractGenerateMojo#execute()}.
+     * @throws IOException If something goes wrong.
+     * @throws MojoExecutionException If something goes wrong.
+     */
+    @Test
+    public void testExecute() throws IOException, MojoExecutionException {
+        MavenProject mavenProject = createMock(MavenProject.class);
+        TemplateGeneratorFactory factory = createMock(TemplateGeneratorFactory.class);
+        TemplateGenerator generator = createMock(TemplateGenerator.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> params = createMock(Map.class);
+        AbstractGenerateMojo mojo = createMockBuilder(AbstractGenerateMojo.class).createMock();
+        List<String> classpathElements = new ArrayList<String>();
+        File source = new File(System.getProperty("basedir"), "src/test/resources");
+        classpathElements.add(source.getAbsolutePath());
+        mojo.classpathElements = classpathElements;
+        File temp = File.createTempFile("autotagmojogen", ".tmp");
+        temp.delete();
+        temp.mkdirs();
+        File resourcesOutputDirectory = new File(temp, "res/");
+        File classesOutputDirectory = new File(temp, "classes/");
+        resourcesOutputDirectory.mkdir();
+        classesOutputDirectory.mkdir();
+        mojo.resourcesOutputDirectory = resourcesOutputDirectory;
+        mojo.classesOutputDirectory = classesOutputDirectory;
+        mojo.packageName = "my.package";
+        mojo.project = mavenProject;
+
+        expect(mojo.createTemplateGeneratorFactory(isA(VelocityEngine.class))).andReturn(factory);
+        expect(factory.createTemplateGenerator()).andReturn(generator);
+        expect(mojo.getParameters()).andReturn(params);
+        expect(mojo.getRuntimeClass()).andReturn("my.package.Runtime");
+        generator.generate(eq("my.package"), isA(TemplateSuite.class), eq(params), eq("my.package.Runtime"));
+        expect(generator.isGeneratingClasses()).andReturn(true);
+        expect(generator.isGeneratingResources()).andReturn(true);
+        mavenProject.addResource(isA(Resource.class));
+        mavenProject.addCompileSourceRoot(classesOutputDirectory.getAbsolutePath());
+
+        replay(mavenProject, mojo, factory, generator, params);
+        mojo.execute();
+        FileUtils.deleteDirectory(temp);
+        verify(mavenProject, mojo, factory, generator, params);
+    }
+
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojoTest.java b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojoTest.java
new file mode 100644
index 0000000..ccd4850
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/CreateDescriptorMojoTest.java
@@ -0,0 +1,163 @@
+/*
+ * $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.plugin;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.maven.model.Resource;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateMethod;
+import org.apache.tiles.autotag.model.TemplateParameter;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.tiles.autotag.plugin.internal.AnnotatedExampleModel;
+import org.apache.tiles.autotag.plugin.internal.ExampleExecutableModel;
+import org.apache.tiles.autotag.plugin.internal.ExampleModel;
+import org.apache.tiles.autotag.plugin.internal.NotFeasibleExampleModel;
+import org.apache.tiles.request.Request;
+import org.junit.Test;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.converters.reflection.Sun14ReflectionProvider;
+
+/**
+ * Tests {@link CreateDescriptorMojo}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class CreateDescriptorMojoTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.plugin.CreateDescriptorMojo#execute()}.
+     * @throws IOException If something goes wrong.
+     * @throws MojoExecutionException If something goes wrong.
+     */
+    @Test
+    public void testExecute() throws IOException, MojoExecutionException {
+        MavenProject mavenProject = createMock(MavenProject.class);
+
+        CreateDescriptorMojo mojo = new CreateDescriptorMojo();
+        mojo.sourceDirectory = new File(System.getProperty("basedir"), "src/test/java");
+        File temp = File.createTempFile("autotagmojo", ".tmp");
+        temp.delete();
+        temp.mkdirs();
+        mojo.outputDirectory = temp;
+        mojo.name = "test";
+        mojo.documentation = "This are the docs";
+        mojo.project = mavenProject;
+
+        mavenProject.addResource(isA(Resource.class));
+
+        replay(mavenProject);
+        mojo.execute();
+        InputStream sis = new FileInputStream(new File(temp, "META-INF/template-suite.xml"));
+        XStream xstream = new XStream(new Sun14ReflectionProvider());
+        TemplateSuite suite = (TemplateSuite) xstream.fromXML(sis);
+        sis.close();
+        assertEquals("test", suite.getName());
+        assertEquals("This are the docs", suite.getDocumentation());
+        assertEquals(3, suite.getTemplateClasses().size());
+
+        TemplateClass templateClass = suite.getTemplateClassByName(ExampleModel.class.getName());
+        assertNotNull(templateClass);
+        assertEquals(ExampleModel.class.getName(), templateClass.getName());
+        assertEquals("Example start/stop template.", templateClass.getDocumentation());
+        TemplateMethod templateMethod = templateClass.getExecuteMethod();
+        assertNotNull(templateMethod);
+        assertTrue(templateMethod.hasBody());
+        assertTrue(templateClass.hasBody());
+        assertEquals("execute", templateMethod.getName());
+        assertEquals("It starts.", templateMethod.getDocumentation());
+        List<TemplateParameter> parameters = new ArrayList<TemplateParameter>(templateMethod.getParameters());
+        assertEquals(4, parameters.size());
+        TemplateParameter parameter = parameters.get(0);
+        assertEquals("one", parameter.getName());
+        assertEquals("java.lang.String", parameter.getType());
+        assertEquals("Parameter one.", parameter.getDocumentation());
+        parameter = parameters.get(1);
+        assertEquals("two", parameter.getName());
+        assertEquals("int", parameter.getType());
+        assertEquals("Parameter two.", parameter.getDocumentation());
+        parameter = parameters.get(2);
+        assertEquals("request", parameter.getName());
+        assertEquals(Request.class.getName(), parameter.getType());
+        assertEquals("The request.", parameter.getDocumentation());
+        parameter = parameters.get(3);
+        assertEquals("modelBody", parameter.getName());
+        assertEquals(ModelBody.class.getName(), parameter.getType());
+        assertEquals("The model body.", parameter.getDocumentation());
+
+        templateClass = suite.getTemplateClassByName(AnnotatedExampleModel.class.getName());
+        assertNotNull(templateClass);
+        assertEquals(AnnotatedExampleModel.class.getName(), templateClass.getName());
+        templateMethod = templateClass.getExecuteMethod();
+        assertNotNull(templateMethod);
+        assertEquals("execute", templateMethod.getName());
+        parameters = new ArrayList<TemplateParameter>(templateMethod.getParameters());
+        assertEquals(4, parameters.size());
+        parameter = parameters.get(0);
+        assertEquals("one", parameter.getName());
+        assertEquals("alternateOne", parameter.getExportedName());
+        assertEquals("java.lang.String", parameter.getType());
+        assertEquals("Parameter one.", parameter.getDocumentation());
+        assertEquals("hello", parameter.getDefaultValue());
+        assertTrue(parameter.isRequired());
+
+        templateClass = suite.getTemplateClassByName(ExampleExecutableModel.class.getName());
+        assertNotNull(templateClass);
+        assertEquals(ExampleExecutableModel.class.getName(), templateClass.getName());
+        assertEquals("Example executable template.", templateClass.getDocumentation());
+        templateMethod = templateClass.getExecuteMethod();
+        assertNotNull(templateMethod);
+        assertEquals("execute", templateMethod.getName());
+        assertEquals("It executes.", templateMethod.getDocumentation());
+        parameters = new ArrayList<TemplateParameter>(templateMethod.getParameters());
+        assertEquals(3, parameters.size());
+        parameter = parameters.get(0);
+        assertEquals("one", parameter.getName());
+        assertEquals("java.lang.String", parameter.getType());
+        assertEquals("Parameter one.", parameter.getDocumentation());
+        parameter = parameters.get(1);
+        assertEquals("two", parameter.getName());
+        assertEquals("int", parameter.getType());
+        assertEquals("Parameter two.", parameter.getDocumentation());
+        parameter = parameters.get(2);
+        assertEquals("request", parameter.getName());
+        assertEquals(Request.class.getName(), parameter.getType());
+        assertEquals("The request.", parameter.getDocumentation());
+
+        assertNull(suite.getTemplateClassByName(NotFeasibleExampleModel.class.getName()));
+        FileUtils.deleteDirectory(temp);
+        verify(mavenProject);
+    }
+
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/GenerateFreemarkerMojoTest.java b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/GenerateFreemarkerMojoTest.java
new file mode 100644
index 0000000..767cc38
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/GenerateFreemarkerMojoTest.java
@@ -0,0 +1,53 @@
+/*
+ * $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.plugin;
+
+import static org.junit.Assert.*;
+
+import org.apache.tiles.autotag.freemarker.FMTemplateGeneratorFactory;
+import org.junit.Test;
+
+/**
+ * Tests {@link GenerateFreemarkerMojo}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class GenerateFreemarkerMojoTest {
+
+    /**
+     * Test method for {@link GenerateFreemarkerMojo#createTemplateGeneratorFactory(VelocityEngine)}.
+     */
+    @Test
+    public void testCreateTemplateGeneratorFactory() {
+        GenerateFreemarkerMojo mojo = new GenerateFreemarkerMojo();
+        assertTrue(mojo.createTemplateGeneratorFactory(null) instanceof FMTemplateGeneratorFactory);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.plugin.GenerateFreemarkerMojo#getParameters()}.
+     */
+    @Test
+    public void testGetParameters() {
+        GenerateFreemarkerMojo mojo = new GenerateFreemarkerMojo();
+        assertNull(mojo.getParameters());
+    }
+
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/GenerateJspMojoTest.java b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/GenerateJspMojoTest.java
new file mode 100644
index 0000000..40e4a66
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/GenerateJspMojoTest.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.autotag.plugin;
+
+import static org.junit.Assert.*;
+
+import org.apache.tiles.autotag.jsp.JspTemplateGeneratorFactory;
+import org.junit.Test;
+
+/**
+ * Tests {@link GenerateJspMojo}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class GenerateJspMojoTest {
+
+    /**
+     * Test method for {@link GenerateJspMojo#createTemplateGeneratorFactory(VelocityEngine)}.
+     */
+    @Test
+    public void testCreateTemplateGeneratorFactory() {
+        GenerateJspMojo mojo = new GenerateJspMojo();
+        assertTrue(mojo.createTemplateGeneratorFactory(null) instanceof JspTemplateGeneratorFactory);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.plugin.GenerateJspMojo#getParameters()}.
+     */
+    @Test
+    public void testGetParameters() {
+        GenerateJspMojo mojo = new GenerateJspMojo();
+        mojo.taglibURI = "http://www.test.org/taglib";
+        assertEquals("http://www.test.org/taglib", mojo.getParameters().get("taglibURI"));
+    }
+
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/GenerateVelocityMojoTest.java b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/GenerateVelocityMojoTest.java
new file mode 100644
index 0000000..4f859ce
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/GenerateVelocityMojoTest.java
@@ -0,0 +1,53 @@
+/*
+ * $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.plugin;
+
+import static org.junit.Assert.*;
+
+import org.apache.tiles.autotag.velocity.VelocityTemplateGeneratorFactory;
+import org.junit.Test;
+
+/**
+ * Tests {@link GenerateVelocityMojo}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class GenerateVelocityMojoTest {
+
+    /**
+     * Test method for {@link GenerateVelocityMojo#createTemplateGeneratorFactory(VelocityEngine)}.
+     */
+    @Test
+    public void testCreateTemplateGeneratorFactory() {
+        GenerateVelocityMojo mojo = new GenerateVelocityMojo();
+        assertTrue(mojo.createTemplateGeneratorFactory(null) instanceof VelocityTemplateGeneratorFactory);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.plugin.GenerateVelocityMojo#getParameters()}.
+     */
+    @Test
+    public void testGetParameters() {
+        GenerateVelocityMojo mojo = new GenerateVelocityMojo();
+        assertNull(mojo.getParameters());
+    }
+
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/AnnotatedExampleModel.java b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/AnnotatedExampleModel.java
new file mode 100644
index 0000000..02e3678
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/AnnotatedExampleModel.java
@@ -0,0 +1,47 @@
+/*
+ * $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.plugin.internal;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.core.runtime.annotation.Parameter;
+import org.apache.tiles.request.Request;
+
+/**
+ * Example start/stop template.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AnnotatedExampleModel {
+
+    /**
+     * It starts.
+     *
+     * @param one Parameter one.
+     * @param two Parameter two.
+     * @param request The request.
+     * @param modelBody The model body.
+     */
+    public void execute(
+            @Parameter(defaultValue = "hello", name = "alternateOne", required = true) String one,
+            int two, Request request, ModelBody modelBody) {
+        // Does nothing.
+    }
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/ExampleExcluded.java b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/ExampleExcluded.java
new file mode 100644
index 0000000..a09ca23
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/ExampleExcluded.java
@@ -0,0 +1,44 @@
+/*
+ * $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.plugin.internal;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.request.Request;
+
+/**
+ * Example start/stop template.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ExampleExcluded {
+
+    /**
+     * It starts.
+     *
+     * @param one Parameter one.
+     * @param two Parameter two.
+     * @param request The request.
+     * @param modelBody The model body.
+     */
+    public void execute(String one, int two, Request request, ModelBody modelBody) {
+        // Does nothing.
+    }
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/ExampleExecutableModel.java b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/ExampleExecutableModel.java
new file mode 100644
index 0000000..9f49651
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/ExampleExecutableModel.java
@@ -0,0 +1,42 @@
+/*
+ * $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.plugin.internal;
+
+import org.apache.tiles.request.Request;
+
+/**
+ * Example executable template.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ExampleExecutableModel {
+
+    /**
+     * It executes.
+     *
+     * @param one Parameter one.
+     * @param two Parameter two.
+     * @param request The request.
+     */
+    public void execute(String one, int two, Request request) {
+        // Does nothing.
+    }
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/ExampleModel.java b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/ExampleModel.java
new file mode 100644
index 0000000..a080d01
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/ExampleModel.java
@@ -0,0 +1,44 @@
+/*
+ * $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.plugin.internal;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.request.Request;
+
+/**
+ * Example start/stop template.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ExampleModel {
+
+    /**
+     * It starts.
+     *
+     * @param one Parameter one.
+     * @param two Parameter two.
+     * @param request The request.
+     * @param modelBody The model body.
+     */
+    public void execute(String one, int two, Request request, ModelBody modelBody) {
+        // Does nothing.
+    }
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/NotFeasibleExampleModel.java b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/NotFeasibleExampleModel.java
new file mode 100644
index 0000000..a52eb62
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/test/java/org/apache/tiles/autotag/plugin/internal/NotFeasibleExampleModel.java
@@ -0,0 +1,38 @@
+/*
+ * $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.plugin.internal;
+
+/**
+ * This won't be registered.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NotFeasibleExampleModel {
+
+    /**
+     * It starts.
+     *
+     * @param whatever Doesn't matter.
+     */
+    public void start(String whatever) {
+        // Does nothing.
+    }
+}
diff --git a/tiles-autotag/maven-autotag-plugin/src/test/resources/META-INF/template-suite.xml b/tiles-autotag/maven-autotag-plugin/src/test/resources/META-INF/template-suite.xml
new file mode 100644
index 0000000..de0a133
--- /dev/null
+++ b/tiles-autotag/maven-autotag-plugin/src/test/resources/META-INF/template-suite.xml
@@ -0,0 +1,183 @@
+<!--
+/*
+ * $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.
+ */
+ -->
+<org.apache.tiles.autotag.model.TemplateSuite>
+  <name>test</name>
+  <templateClasses class="linked-hash-map">
+    <entry>
+      <string>org.apache.tiles.autotag.plugin.internal.ExampleExecutableModel</string>
+      <org.apache.tiles.autotag.model.TemplateClass>
+        <name>org.apache.tiles.autotag.plugin.internal.ExampleExecutableModel</name>
+        <tagName>exampleExecutable</tagName>
+        <tagClassPrefix>ExampleExecutable</tagClassPrefix>
+        <documentation>Example executable template.</documentation>
+        <executeMethod>
+          <name>execute</name>
+          <documentation>It executes.</documentation>
+          <parameters class="linked-hash-map">
+            <entry>
+              <string>one</string>
+              <org.apache.tiles.autotag.model.TemplateParameter>
+                <name>one</name>
+                <exportedName>one</exportedName>
+                <documentation>Parameter one.</documentation>
+                <type>java.lang.String</type>
+                <required>false</required>
+              </org.apache.tiles.autotag.model.TemplateParameter>
+            </entry>
+            <entry>
+              <string>two</string>
+              <org.apache.tiles.autotag.model.TemplateParameter>
+                <name>two</name>
+                <exportedName>two</exportedName>
+                <documentation>Parameter two.</documentation>
+                <type>int</type>
+                <required>false</required>
+              </org.apache.tiles.autotag.model.TemplateParameter>
+            </entry>
+            <entry>
+              <string>request</string>
+              <org.apache.tiles.autotag.model.TemplateParameter>
+                <name>request</name>
+                <exportedName>request</exportedName>
+                <documentation>The request.</documentation>
+                <type>org.apache.tiles.request.Request</type>
+                <required>false</required>
+              </org.apache.tiles.autotag.model.TemplateParameter>
+            </entry>
+          </parameters>
+        </executeMethod>
+      </org.apache.tiles.autotag.model.TemplateClass>
+    </entry>
+    <entry>
+      <string>org.apache.tiles.autotag.plugin.internal.ExampleModel</string>
+      <org.apache.tiles.autotag.model.TemplateClass>
+        <name>org.apache.tiles.autotag.plugin.internal.ExampleModel</name>
+        <tagName>example</tagName>
+        <tagClassPrefix>Example</tagClassPrefix>
+        <documentation>Example start/stop template.</documentation>
+        <executeMethod>
+          <name>execute</name>
+          <documentation>It starts.</documentation>
+          <parameters class="linked-hash-map">
+            <entry>
+              <string>one</string>
+              <org.apache.tiles.autotag.model.TemplateParameter>
+                <name>one</name>
+                <exportedName>one</exportedName>
+                <documentation>Parameter one.</documentation>
+                <type>java.lang.String</type>
+                <required>false</required>
+              </org.apache.tiles.autotag.model.TemplateParameter>
+            </entry>
+            <entry>
+              <string>two</string>
+              <org.apache.tiles.autotag.model.TemplateParameter>
+                <name>two</name>
+                <exportedName>two</exportedName>
+                <documentation>Parameter two.</documentation>
+                <type>int</type>
+                <required>false</required>
+              </org.apache.tiles.autotag.model.TemplateParameter>
+            </entry>
+            <entry>
+              <string>request</string>
+              <org.apache.tiles.autotag.model.TemplateParameter>
+                <name>request</name>
+                <exportedName>request</exportedName>
+                <documentation>The request.</documentation>
+                <type>org.apache.tiles.request.Request</type>
+                <required>false</required>
+              </org.apache.tiles.autotag.model.TemplateParameter>
+            </entry>
+            <entry>
+              <string>modelBody</string>
+              <org.apache.tiles.autotag.model.TemplateParameter>
+                <name>modelBody</name>
+                <exportedName>modelBody</exportedName>
+                <documentation>The model body.</documentation>
+                <type>org.apache.tiles.autotag.core.runtime.ModelBody</type>
+                <required>false</required>
+              </org.apache.tiles.autotag.model.TemplateParameter>
+            </entry>
+          </parameters>
+        </executeMethod>
+      </org.apache.tiles.autotag.model.TemplateClass>
+    </entry>
+    <entry>
+      <string>org.apache.tiles.autotag.plugin.internal.AnnotatedExampleModel</string>
+      <org.apache.tiles.autotag.model.TemplateClass>
+        <name>org.apache.tiles.autotag.plugin.internal.AnnotatedExampleModel</name>
+        <tagName>annotatedExample</tagName>
+        <tagClassPrefix>AnnotatedExample</tagClassPrefix>
+        <documentation>Example start/stop template.</documentation>
+        <executeMethod>
+          <name>execute</name>
+          <documentation>It starts.</documentation>
+          <parameters class="linked-hash-map">
+            <entry>
+              <string>one</string>
+              <org.apache.tiles.autotag.model.TemplateParameter>
+                <name>one</name>
+                <exportedName>alternateOne</exportedName>
+                <documentation>Parameter one.</documentation>
+                <type>java.lang.String</type>
+                <defaultValue>hello</defaultValue>
+                <required>true</required>
+              </org.apache.tiles.autotag.model.TemplateParameter>
+            </entry>
+            <entry>
+              <string>two</string>
+              <org.apache.tiles.autotag.model.TemplateParameter>
+                <name>two</name>
+                <exportedName>two</exportedName>
+                <documentation>Parameter two.</documentation>
+                <type>int</type>
+                <required>false</required>
+              </org.apache.tiles.autotag.model.TemplateParameter>
+            </entry>
+            <entry>
+              <string>request</string>
+              <org.apache.tiles.autotag.model.TemplateParameter>
+                <name>request</name>
+                <exportedName>request</exportedName>
+                <documentation>The request.</documentation>
+                <type>org.apache.tiles.request.Request</type>
+                <required>false</required>
+              </org.apache.tiles.autotag.model.TemplateParameter>
+            </entry>
+            <entry>
+              <string>modelBody</string>
+              <org.apache.tiles.autotag.model.TemplateParameter>
+                <name>modelBody</name>
+                <exportedName>modelBody</exportedName>
+                <documentation>The model body.</documentation>
+                <type>org.apache.tiles.autotag.core.runtime.ModelBody</type>
+                <required>false</required>
+              </org.apache.tiles.autotag.model.TemplateParameter>
+            </entry>
+          </parameters>
+        </executeMethod>
+      </org.apache.tiles.autotag.model.TemplateClass>
+    </entry>
+  </templateClasses>
+</org.apache.tiles.autotag.model.TemplateSuite>
\ No newline at end of file
diff --git a/tiles-autotag/pom.xml b/tiles-autotag/pom.xml
new file mode 100644
index 0000000..444155b
--- /dev/null
+++ b/tiles-autotag/pom.xml
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>tiles-master</artifactId>
+        <groupId>org.apache.tiles</groupId>
+        <version>4</version>
+        <relativePath/>
+    </parent>
+    <groupId>org.apache.tiles</groupId>
+    <artifactId>tiles-autotag</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>Autotags</name>
+    <description>Automatic generation of tags.</description>
+    <url>http://tiles.apache.org/tiles-autotag/</url>
+    <scm>
+        <connection>scm:svn:http://svn.eu.apache.org/repos/asf/tiles/framework/trunk/tiles-autotag/</connection>
+        <developerConnection>scm:svn:https://svn.eu.apache.org/repos/asf/tiles/framework/trunk/tiles-autotag/</developerConnection>
+        <url>http://svn.eu.apache.org/viewvc/tiles/framework/trunk/tiles-autotag/</url>
+    </scm>
+    <issueManagement>
+        <system>JIRA</system>
+        <url>https://issues.apache.org/jira/browse/AUTOTAG</url>
+    </issueManagement>
+
+    <modules>
+        <module>tiles-autotag-core-runtime</module>
+        <module>tiles-autotag-core</module>
+        <module>tiles-autotag-jsp</module>
+        <module>tiles-autotag-freemarker</module>
+        <module>tiles-autotag-velocity</module>
+        <module>maven-autotag-plugin</module>
+    </modules>
+    <distributionManagement>
+        <site>
+            <id>apache-site</id>
+            <url>scp://people.apache.org/www/tiles.apache.org/tiles-autotag</url>
+        </site>
+    </distributionManagement>
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>4.7</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>com.thoughtworks.qdox</groupId>
+                <artifactId>qdox</artifactId>
+                <version>1.10.1</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.tiles</groupId>
+                <artifactId>tiles-request-api</artifactId>
+                <version>1.0-SNAPSHOT</version>
+            </dependency>
+            <dependency>
+                <groupId>org.easymock</groupId>
+                <artifactId>easymock</artifactId>
+                <version>3.0</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.tiles</groupId>
+                <artifactId>tiles-autotag-core</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.tiles</groupId>
+                <artifactId>tiles-autotag-core-runtime</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.velocity</groupId>
+                <artifactId>velocity</artifactId>
+                <version>1.6.3</version>
+            </dependency>
+            <dependency>
+                <groupId>commons-io</groupId>
+                <artifactId>commons-io</artifactId>
+                <version>2.0</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+    <profiles>
+        <profile>
+            <id>apache-release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-install-plugin</artifactId>
+                        <configuration>
+                            <createChecksum>true</createChecksum>
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>rat-maven-plugin</artifactId>
+                        <version>1.0-alpha-3</version>
+                        <executions>
+                            <execution>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>check</goal>
+                                </goals>
+                                <configuration>
+                                    <addDefaultLicenseMatchers>false</addDefaultLicenseMatchers>
+                                    <licenseMatchers>
+                                        <classNames>
+                                            <className>rat.analysis.license.ApacheSoftwareLicense20</className>
+                                        </classNames>
+                                        <classNames>
+                                            <className>rat.analysis.generation.GeneratedLicenseNotRequired</className>
+                                        </classNames>
+                                    </licenseMatchers>
+                                    <includes>
+                                        <include>pom.xml</include>
+                                        <include>src/**</include>
+                                    </includes>
+                                    <excludes>
+                                        <exclude>**/*LICENSE.txt</exclude>
+                                        <exclude>**/*MANIFEST.MF</exclude>
+                                    </excludes>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>linkcheck</id>
+            <reporting>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-linkcheck-plugin</artifactId>
+                        <version>1.1</version>
+                        <configuration>
+                            <excludedLinks>
+                                <excludedLink>**/index.html</excludedLink>
+                                <excludedLink>**/logo.png</excludedLink>
+                            </excludedLinks>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </reporting>
+        </profile>
+
+    </profiles>
+
+</project>
diff --git a/tiles-autotag/src/site/apt/dev/release.apt b/tiles-autotag/src/site/apt/dev/release.apt
new file mode 100644
index 0000000..ab9bd52
--- /dev/null
+++ b/tiles-autotag/src/site/apt/dev/release.apt
@@ -0,0 +1,351 @@
+~~ $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.
+~~
+         -----------
+         Release Process
+         -----------
+
+Tiles Release Process
+
+  Here you will find the steps to create releases for Tiles.
+
+Prerequisites
+
+  To create a release you have to install:
+
+  * {{{http://java.sun.com/javase/6}Java 6.0}}. If you are using a newer
+  version of Java, it is suggested to <<change JAVA_HOME environment variable>>
+  when calling Maven, so it points to an instance of Java 6.0;
+
+  * {{{http://maven.apache.org/}Maven 2.2 or 3}};
+
+  * {{{http://www.gnupg.org/}GnuPG}};
+
+  * {{{http://www.openssh.com/}OpenSSH}};
+
+  * {{{http://www.graphviz.org/}GraphViz}};
+
+One-time operations
+
+  These operations need to be performed only one time.
+
+* Create and publish your GPG key
+
+  To create a GPG key, follow the
+  {{{http://www.apache.org/dev/openpgp.html}guidelines}}.
+  Include it in:
+
+-------------------------------------
+https://svn.apache.org/repos/asf/tiles/site/KEYS
+-------------------------------------
+
+  Publish your GPG key in a PGP key server, such as
+  {{{http://pgp.mit.edu/} MIT Keyserver}}.
+
+* Create and upload yout SSH key
+
+  * Generate your SSH key (in this case we will use DSA encryption):
+
+---------------------------------
+ssh-keygen -t rsa
+---------------------------------
+
+  * Copy your public key to the server:
+
+--------------------------------------
+scp ~/.ssh/id_rsa.pub user@people.apache.org:.ssh/authorized_keys
+--------------------------------------
+
+  * Try to login:
+
+---------------------------------
+ssh user@people.apache.org
+---------------------------------
+
+  If it does not ask you a password, everything is ok.
+
+* Modify <<<settings.xml>>>
+
+  Your <<<settings.xml>>> must be modified to allow deployment.
+
+  This is the minimal configuration, obviously if you already have a <<<settings.xml>>> file,
+  you must edit it:
+
+---------------------------
+<settings xmlns="http://maven.apache.org/POM/4.0.0"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
+    <servers>
+        <server>
+            <id>apache-site</id>
+            <username>YOUR_APACHE_USERNAME</username>
+            <filePermissions>664</filePermissions>
+            <directoryPermissions>775</directoryPermissions>
+        </server>
+        <server>
+            <id>apache.snapshots.https</id>
+            <username>YOUR_APACHE_USERNAME</username>
+            <password>YOUR_APACHE_PASSWORD</password>
+        </server>
+        <server>
+            <id>apache.releases.https</id>
+            <username>YOUR_APACHE_USERNAME</username>
+            <password>YOUR_APACHE_PASSWORD</password>
+        </server>
+    </servers>
+    <profiles>
+        <profile>
+            <id>release</id>
+            <properties>
+                <gpg.passphrase>YOUR_SECRET_PHRASE</gpg.passphrase>
+            </properties>
+            </profile>
+        </profile>
+    </profiles>
+</settings>
+---------------------------
+
+Repeatable operations
+
+  These steps must be performed <<for each>> release.
+
+* Prepare the release tag
+
+  To prepare the release Subversion tag, check out the branch/trunk from where
+  you are preparing the release and type:
+
+-----------------------------------
+mvn release:prepare -Dusername=YOUR_SVN_USER -Dpassword=YOUR_SVN_PASSWORD
+-----------------------------------
+
+  The plugin interactively will ask you the version to release, the Subversion
+  tag to use and the next snapshot version. It is reccomended to use the tag:
+  <<tiles-X.X.X>>.
+
+* Perform the Release
+
+  To perform the release, i.e. creating and deploying Maven artifacts, use:
+
+-----------------------------------
+mvn release:perform
+-----------------------------------
+
+* Close the staging repository
+
+  Login to {{{https://repository.apache.org} Nexus repository}} using your Apache LDAP credentials.
+  Click on "Staging". Then click on "tiles" in the list of repositories.
+  In the panel below you should see an open repository that is linked to your username and ip.
+  Right click on this repository and select "Close".
+  This will close the repository from future deployments and make it available for others to view.
+  If you are staging multiple releases together, skip this step until you have staged everything.
+  Enter the name and version of the artifact being released in the "Description" field and then click "Close".
+  This will make it easier to identify it later.
+
+* Verify the staged artifacts
+
+  If you click on your repository, a tree view will appear below.
+  You can then browse the contents to ensure the artifacts are as you expect them.
+  Pay particular attention to the existence of *.asc (signature) files.
+  If the you don't like the content of the repository, right click your repository and choose "Drop".
+  You can then rollback your release and repeat the process.
+
+  Note the repository URL, you will need this in your vote email.
+
+* Digest and upload assemblies
+
+  * Go into the release assembly target directory:
+
+-----------------------------------
+cd target/checkout/assembly/target/assembly/out
+-----------------------------------
+
+  * Create MD5 and SHA1 files for each files (including ASC files). You can do
+  it with this simple shell script:
+
+-----------------------------------
+#!/bin/sh
+
+for fileitem in *
+do
+  openssl md5 < $fileitem > $fileitem.md5
+  openssl sha1 < $fileitem > $fileitem.sha1
+done
+-----------------------------------
+
+  * Upload everything to the build site:
+
+-----------------------------------
+scp * user@people.apache.org:/www/people.apache.org/builds/tiles/${version}
+-----------------------------------
+
+* Release the JIRA version
+
+  * In JIRA go to the version that you want to release and release it.
+
+  * Create a new version, if it has not been done before.
+
+  * Create the release notes and <<write down the link>> that it uses.
+
+* Send announcement for the test build
+
+  In <<developers mailing list>> send an announcement for the test build:
+
+-----------------------------------
+Subject: [ANNOUNCE] Tiles ${version} test build available
+
+The test build of Tiles ${version} is available.
+
+
+No determination as to the quality ('alpha,' 'beta,' or 'GA') of Tiles
+${version} has been made, and at this time it is simply a "test build". We
+welcome any comments you may have, and will take all feedback into
+account if a quality vote is called for this build.
+
+Release notes:
+
+* ${jira.release.notes}
+
+Distribution:
+
+ * http://people.apache.org/builds/tiles/${version}/
+
+Maven 2 staging repository:
+
+ * https://repository.apache.org/content/repositories/tiles-[YOUR REPOSITORY ID]/
+
+A vote regarding the quality of this test build will be initiated
+within the next couple of days.
+-----------------------------------
+
+* Call for a vote
+
+  A few days after the test build announcement, call for a vote in
+  <<developers mailing list>>.
+
+-----------------------------------
+Subject: [VOTE] ${version} Release Quality
+
+The Tiles ${version} test build has been available since ${testBuildDate}.
+
+Release notes:
+
+* ${jira.release.notes}
+
+Distribution:
+
+ * http://people.apache.org/builds/tiles/${version}/
+
+Maven 2 staging repository:
+
+ * https://repository.apache.org/content/repositories/tiles-[YOUR REPOSITORY ID]/
+
+If you have had a chance to review the test build, please respond with
+a vote on its quality:
+
+ [ ] Leave at test build
+ [ ] Alpha
+ [ ] Beta
+ [ ] General Availability (GA)
+
+
+Everyone who has tested the build is invited to vote. Votes by PMC
+members are considered binding. A vote passes if there are at least
+three binding +1s and more +1s than -1s.
+-----------------------------------
+
+* Post-vote operations
+
+  After a vote is finished, and it has been decided that is
+  <<at least of alpha quality>>, there is the need of a post-vote process.
+
+** Promote staged artifacts
+
+  Once the release is deemed fit for public consumption it can be transfered to a production repository where it will be available to all users.
+
+  Login to {{{https://repository.apache.org}Nexus repository}} again.
+  Click on "Staging" and then on the repository with id "tiles-staging".
+  Find your closed staging repository, right click on it and choose "Promote".
+  Select the "Releases" repository and click "Promote".
+
+  Next click on "Repositories", select the "Releases" repository
+  and validate that your artifacts exist as you expect them.
+
+** Move assemblies
+
+  * Move assemblies to the Apache distribution mirrors:
+
+-------------------------------------------
+ssh user@people.apache.org
+
+cd /www/people.apache.org/builds/tiles/${version}
+mkdir /www/www.apache.org/dist/tiles/v${version}/
+cp * /www/www.apache.org/dist/tiles/v${version}/
+-------------------------------------------
+
+** Update the site
+
+  * Wait 24 hours to let the mirror sync to the release and then update the
+  site. In particular you have to update the index and the download pages:
+
+------------------------------------------------
+https://svn.apache.org/repos/asf/tiles/site/src/site/xdoc/index.xml
+https://svn.apache.org/repos/asf/tiles/site/src/site/apt/download.apt
+------------------------------------------------
+
+  Build and publish the site:
+
+--------------------------------------
+mvn site
+mvn site:deploy
+--------------------------------------
+
+** Send announcement
+
+  Finally, send an an announcement to the <<users>> and <<developers mailing
+  list>>:
+
+--------------------------------------
+Subject: [ANNOUNCE] Tiles ${version} ${quality} released
+
+The Apache Tiles team is pleased to announce the release of Tiles ${version}
+${quality}.
+
+Tiles ${version} is available in a binary and a source distribution.
+
+http://tiles.apache.org/download.html
+
+It is also available in the central Maven repository under Group ID
+"org.apache.tiles".
+
+The 2.0.x series of the Apache Tiles framework has a minimum
+requirement of the following specification versions:
+
+* Java Servlet 2.4 and JavaServer Pages (JSP) 2.0
+* Java Standard Edition (Java SE) 1.5
+
+The release notes are available online at:
+
+* ${jira.release.notes}
+
+Please feel free to test the distribution and post your comments to
+the user list, or, if appropriate, file a ticket with JIRA.
+--------------------------------------
+
+  <<You have finished!>>
diff --git a/tiles-autotag/src/site/apt/plugin.apt b/tiles-autotag/src/site/apt/plugin.apt
new file mode 100644
index 0000000..830a65d
--- /dev/null
+++ b/tiles-autotag/src/site/apt/plugin.apt
@@ -0,0 +1,105 @@
+~~ $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.
+~~
+         -----------
+         Building a tag library for Freemarker, JSP or Velocity
+         -----------
+         
+Building a tag library for Freemarker, JSP or Velocity
+
+  Once you have {{{./taglib.html}created a generic tag library}},
+  Autotag will automatically generate the corresponding library
+  for Freemarker, JSP, or Velocity. You just create a maven project 
+  and include in <<<pom.xml>>>:
+  
+  * The appropriate implementation of tiles-request as a dependency. 
+  For instance to build a JSP taglib:
+
+-----------------
+<dependency>
+  <groupId>org.apache.tiles</groupId>
+  <artifactId>tiles-request-jsp</artifactId>
+  <version>1.0</version>
+</dependency>
+-----------------
+  
+  * Your generic tag library as a dependency. For instance:
+
+-----------------
+<dependency>
+  <groupId>org.example</groupId>
+  <artifactId>my-taglib</artifactId>
+  <version>1.0-SNAPSHOT</version>
+</dependency>
+-----------------
+  
+  * The appropriate configuration of maven-autotag-plugin. For instance:
+  
+-----------------
+<plugin>
+  <groupId>org.apache.tiles.autotag.plugin</groupId>
+  <artifactId>maven-autotag-plugin</artifactId>
+  <version>1.0</version>
+  <executions>
+    <execution>
+      <goals>
+        <goal>generate-jsp</goal>
+      </goals>
+      <configuration>
+        <packageName>org.example.mytaglib.jsp</packageName>
+        <jspRuntime>org.apache.tiles.request.jsp.autotag.JspAutotagRuntime</jspRuntime>
+        <taglibURI>http://example.org/mytaglib</taglibURI>
+      </configuration> 
+    </execution>
+  </executions>
+</plugin>
+-----------------
+
+  No further code is required.
+
+Plugin goals
+
+  one or several of the following goals may be used:
+
+  [generate-jsp] generates the files required for a JSP tag library. It can then be included
+  in a JSP using the configured taglibURI.
+  
+  [generate-freemarker] generates the files required for freemarker user-defined directives.
+  The directives can then be made available to freemarker by adding an instance of the 
+  generated class <<<...FMModelRepository>>> to the freemarker template model.
+  
+  [generate-velocity] generates the files required for velocity user directives. The directives
+  can be made available to Velocity by setting the <<<userdirective>>> property, either manually
+  or by using the generated file <<<META-INF/velocity.properties>>>.
+  
+Plugin configuration
+
+  [packageName] the destination package for the generated classes.
+  
+  [freemarkerRuntime] the runtime implementation of TilesRequest to use for freemarker, for 
+  instance <<<org.apache.tiles.request.freemarker.autotag.FreemarkerAutotagRuntime>>>.
+  
+  [jspRuntime] the runtime implementation of TilesRequest to use for JSP, for 
+  instance <<<org.apache.tiles.request.jsp.autotag.JSPAutotagRuntime>>>.
+  
+  [velocityRuntime] the runtime implementation of TilesRequest to use for velocity, for 
+  instance <<<org.apache.tiles.request.velocity.autotag.VelocityAutotagRuntime>>>.
+  
+  [taglibURI] the URI to use for the JSP taglib.
+  
diff --git a/tiles-autotag/src/site/apt/taglib.apt b/tiles-autotag/src/site/apt/taglib.apt
new file mode 100644
index 0000000..88cdfa2
--- /dev/null
+++ b/tiles-autotag/src/site/apt/taglib.apt
@@ -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.
+~~
+         -----------
+         Creating a generic tag library with Autotag
+         -----------
+
+Creating a generic tag library
+
+  A generic tag library is a jar file including the classes that implement
+  the tags and a library descriptor <<<META-INF/template-suite.xml>>>, that can be used by
+  Autotag to build specific tag libraries for Freemarker, JSP, and Velocity.
+  
+  The structure of the library descriptor is rather complex; fortunately 
+  Autotag can generate it for you by parsing the sources.
+
+* The tag class
+
+  A tag in the library can be implemented by any java class that fits the following requirements:
+  
+  * The class name ends with <<<Model>>>.
+  
+  * It has a public, non-static, non-abstract method called <<<execute>>>.
+  
+  * The <<<execute>>> method returns <<<void>>>.
+  
+  * The parameters of the <<<execute>>> method are as follows:
+  
+  ** the first parameters may be anything you want; at runtime they will contain the values 
+  assigned to the attributes of the tag in the template. Those parameters may be annotated with
+  <<<org.apache.tiles.autotag.core.runtime.annotation.Parameter>>> in order to specify the name of
+  the attribute, the default value, or to make the parameter mandatory.
+  
+  ** then, one parameter of type <<<org.apache.tiles.request.Request>>>.
+  
+  ** and finally, one optional parameter of type <<<org.apache.tiles.autotag.core.runtime.ModelBody>>>;
+  at runtime it will contain the contents of the tag in the template.
+  
+  For instance:
+
+----------------
+public class MyTagModel {
+
+    public void execute( 
+        @Parameter(required=true) String mandatoryParam, 
+        String optionalParam, 
+        Request request, 
+        ModelBody body) {
+        ...
+    }
+
+}
+----------------
+  
+* Generating the library descriptor
+
+  the library descriptor can be generated using the create-descriptor goal of maven-autotag-plugin.
+  
+-----------------
+<plugin>
+  <groupId>org.apache.tiles.autotag.plugin</groupId>
+  <artifactId>maven-autotag-plugin</artifactId>
+  <version>1.0</version>
+  <executions>
+    <execution>
+      <goals>
+        <goal>create-descriptor</goal>
+      </goals>
+      <configuration>
+        ...
+      </configuration> 
+    </execution>
+  </executions>
+</plugin>
+-----------------
+
+** Configuration reference
+
+  [name] is the name of the tag library (for instance: <<<tiles>>>). It will be used for naming a
+  number of generated files.
+  
+  [documentation] is a documentation that will be included in the library descriptor, and then 
+  later in the appropriate artefacts when generating the taglibs.
+  
+  [includes] specifies what source files should be included, defaults to <<<**/*Model.java>>>.
+  It follows the usual conventions in maven.
+  
+  [excludes] specifies what source files should be included, defaults to nothing.
+  It follows the usual conventions in maven.
+  
+  [outputDirectory] specifies where to put the generated files, defaults to 
+  <<<target/autotag-template-suite>>>.
diff --git a/tiles-autotag/src/site/site.xml b/tiles-autotag/src/site/site.xml
new file mode 100644
index 0000000..dcfcd0c
--- /dev/null
+++ b/tiles-autotag/src/site/site.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Tiles Autotags">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../index.html"/>
+            <item
+                   name="Tiles Autotag"
+                   href="./index.html"/>
+        </menu>
+        <menu name="Reference">
+            <item
+                    name="Creating an generic tag library"
+                    href="./taglib.html"/>
+            <item
+                    name="Building a tag library"
+                    href="./plugin.html"/>
+            <item
+                    name="Javadoc"
+                    href="apidocs/index.html"/>
+        </menu>
+
+        <menu name="Developers">
+            <item
+                    name="Building"
+                    href="/dev/building.html"/>
+            <item
+                    name="Snapshots"
+                    href="/dev/snapshots.html"/>
+            <item
+                    name="Release Process"
+                    href="/dev/release.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-autotag/src/site/xdoc/dev/building.xml b/tiles-autotag/src/site/xdoc/dev/building.xml
new file mode 100644
index 0000000..97b102e
--- /dev/null
+++ b/tiles-autotag/src/site/xdoc/dev/building.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<!--
+/*
+ * $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.
+ */
+-->
+<document>
+
+    <properties>
+        <title>Apache Autotag</title>
+    </properties>
+
+    <body>
+        <section name="Building the Autotag project">
+          <subsection name="Prerequisites">
+            <p>For all the next instructions, we assume that you downloaded and
+            installed <a href="http://maven.apache.org/">Maven</a>.</p>
+            <p>To download packages from the source repository, you need to
+            download and install <a href="http://subversion.tigris.org/">
+            Subversion</a>.</p>
+            <p>If you want to build something including JavaDocs (assemblies,
+            sites and JavaDoc report itself) you need to install
+            <a href="http://www.graphviz.org/">GraphViz</a>, otherwise you will
+            notice missing pictures inside JavaDocs pages.</p>
+          </subsection>
+          <subsection name="Building main packages">
+            <p>To build the Autotag project from source you need to:</p>
+            <ul>
+            <li><p><a href="../../download.html">download</a> the source
+            distribution, or checkout the latest version:</p>
+            <p><source>svn co http://svn.apache.org/repos/asf/tiles/framework/trunk/tiles-autotag</source></p></li>
+            <li><p>go into the source directory and type:</p>
+            <p><source>mvn package</source></p></li>
+            </ul>
+            <p>You will find the generated JARs under:</p>
+            <ul>
+            <li>{autotag-dir}/maven-autotag-plugin/target/maven-autotag-plugin-${version}.jar</li>
+            <li>{autotag-dir}/tiles-autotag-core/target/tiles-autotag-core-${version}.jar</li>
+            <li>{autotag-dir}/tiles-autotag-core-runtime/target/tiles-autotag-core-runtime-${version}.jar</li>
+            <li>{autotag-dir}/tiles-autotag-freemarker/target/tiles-autotag-freemarker-${version}.jar</li>
+            <li>{autotag-dir}/tiles-autotag-jsp/target/tiles-autotag-jsp-${version}.jar</li>
+            <li>{autotag-dir}/tiles-autotag-velocity/target/tiles-autotag-velocity-${version}.jar</li>
+            </ul>
+          </subsection>
+        </section>
+        <section name="Building the websites">
+          <p>There are four Tiles websites: the main website and the projects
+          websites (tiles-request, tiles-autotag and framework).</p>
+          <subsection name="Building the main website">
+            <p>To build the main website:</p>
+            <ul>
+            <li><p>checkout the site from the source repository:</p>
+            <p><source>svn co http://svn.apache.org/repos/asf/tiles/site/</source></p></li>
+            <li><p>go into the site directory and type:</p>
+            <p><source>mvn site</source></p></li>
+            </ul>
+            <p>You will find the generated distribution under
+            <code>{tiles-site-dir}/target/site</code>.</p>
+          </subsection>
+          <subsection name="Building the tiles-autotag website">
+            <p>To build a project's website:</p>
+            <ul>
+            <li><p><a href="../../download.html">download</a> the source
+            distribution, or checkout the latest version:</p>
+            <p><source>svn co http://svn.apache.org/repos/asf/tiles/framework/trunk/tiles-autotag</source></p></li>
+            <li><p>go into the source directory and type:</p>
+            <p><source>mvn site site:stage</source></p></li>
+            </ul>
+            <p>You will find the generated website under:</p>
+            <ul>
+            <li>{tiles-autotag-dir}/target/staging</li>
+            </ul>
+          </subsection>
+        </section>
+    </body>
+
+</document>
diff --git a/tiles-autotag/src/site/xdoc/dev/snapshots.xml b/tiles-autotag/src/site/xdoc/dev/snapshots.xml
new file mode 100644
index 0000000..359f7cb
--- /dev/null
+++ b/tiles-autotag/src/site/xdoc/dev/snapshots.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<!--
+/*
+ * $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.
+ */
+-->
+<document>
+
+    <properties>
+        <title>Apache Tiles</title>
+    </properties>
+
+    <body>
+        <section name="Apache Tiles&#8482;">
+        <subsection name="Snapshots">
+
+        <p>Looking for snapshot builds of the latest version of Tiles?
+           Snapshots are occasionally published to Apache's Maven snapshot
+           repository, which can be accessed with the following configuration:</p>
+
+        <source>
+        <![CDATA[
+          <repository>
+              <id>apache.snapshots</id>
+              <name>Apache Maven Snapshot Repository</name>
+              <url>http://repository.apache.org/snapshots</url>
+          </repository>
+        ]]>
+        </source>
+
+        <p>After configuring the repository, declare a dependency on
+        Tiles Autotag:</p>
+
+        <source><![CDATA[
+          <dependency>
+              <groupId>org.apache.tiles</groupId>
+              <artifactId>tiles-autotag-core-runtime</artifactId>
+              <version>1.0-SNAPSHOT</version>
+          </dependency>
+        ]]>
+        </source>
+        
+        <p>Or the maven plugin:</p>
+        
+        <source><![CDATA[
+          <plugin>
+              <groupId>org.apache.tiles</groupId>
+              <artifactId>maven-autotag-plugin</artifactId>
+              <version>1.0-SNAPSHOT</version>
+              <configuration>
+                  <packageName>...</packageName>
+                  <freemarkerRuntime>...</freemarkerRuntime>
+                  <jspRuntime>...</jspRuntime>
+                  <velocityRuntime>...</velocityRuntime>
+              </configuration>
+          </plugin>
+        ]]>
+        </source>
+
+    </subsection>
+</section>
+</body>
+
+</document>
diff --git a/tiles-autotag/src/site/xdoc/index.xml b/tiles-autotag/src/site/xdoc/index.xml
new file mode 100644
index 0000000..8af02b0
--- /dev/null
+++ b/tiles-autotag/src/site/xdoc/index.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!--
+/*
+ * $Id: index.xml 1162124 2011-08-26 14:16:13Z mck $
+ *
+ * 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.
+ */
+-->
+<document>
+
+    <properties>
+        <title>Home</title>
+    </properties>
+
+    <body>
+
+        <section name="Apache Autotag project">
+
+            <p>Tiles-3 introduces a feature complete
+                <a href="http://svn.eu.apache.org/repos/asf/tiles/sandbox/trunk/tiles-autotag/" title="Autotag SVN directory">Autotag project</a>,
+                a project that automatically generates tags (or tag-like) artifact from a common template code for a range of templating languages.
+                Today JSP tags, Freemarker directive models and Velocity directives are generated from a common template models.
+            </p>
+            <p>Such template models must have a single public method, with this signature:</p>
+            <p>
+                <span class="Apple-style-span" style="font-family: monospace; white-space: pre;">public void execute(&lt;parameters&gt;, Request request, ModelBody modelBody);</span>&nbsp;
+            </p>
+            <p>The modelBody parameter is optional: if it is not specified, the template model does not have a body.&nbsp;</p>
+            <p>For more features (required fields, default values, a name different to the one specified in the parameter list) a new annotation @Parameter has been created.</p>
+            <p>A Maven 2 (based on 2.2.1) plugin contains four Mojos:</p>
+            <p>&nbsp;</p>
+            <ul>
+                <li>the first mojo (create-descriptor) reads the template models and produces an XML file containing the description of the read models;</li>
+                <li>the others (generate-jsp, generate-freemarker, generate-velocity) produce boilerplate code.</li>
+            </ul>
+                <p>The projects using the latter mojos must include some runtime dependencies.</p>
+                <p>The boilerplate code has been removed and uses the plugin instead: it's a lot of boilerplate, boring code, now generated automatically.
+                </p>
+        </section>
+    </body>
+
+</document>
diff --git a/tiles-autotag/tiles-autotag-core-runtime/pom.xml b/tiles-autotag/tiles-autotag-core-runtime/pom.xml
new file mode 100644
index 0000000..93a45c3
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/pom.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>tiles-autotag</artifactId>
+    <groupId>org.apache.tiles</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.tiles</groupId>
+  <artifactId>tiles-autotag-core-runtime</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Autotag - Core runtime</name>
+  <description>Autotag: runtime core classes.</description>
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <configuration>
+                        <archive>
+                            <manifestFile>${tiles.manifestfile}</manifestFile>
+                            <manifest>
+                                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+                                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                            </manifest>
+                        </archive>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>maven-bundle-plugin</artifactId>
+                    <version>2.3.7</version>
+                    <inherited>true</inherited>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <excludeDependencies>true</excludeDependencies>
+                    <manifestLocation>target/osgi</manifestLocation>
+                    <instructions>
+                        <_nouses>true</_nouses>
+                        <Bundle-SymbolicName>${tiles.osgi.symbolicName}</Bundle-SymbolicName>
+                        <Export-Package>${tiles.osgi.export}</Export-Package>
+                        <Private-Package>${tiles.osgi.private}</Private-Package>
+                        <Import-Package>${tiles.osgi.import}</Import-Package>
+                        <DynamicImport-Package>${tiles.osgi.dynamicImport}</DynamicImport-Package>
+                        <Bundle-DocURL>${project.url}</Bundle-DocURL>
+                        <Specification-Title>${project.name}</Specification-Title>
+                        <Specification-Version>${project.version}</Specification-Version>
+                        <Specification-Vendor>${project.organization.name}</Specification-Vendor>
+                        <Implementation-Title>${project.name}</Implementation-Title>
+                        <Implementation-Version>${project.version}</Implementation-Version>
+                        <Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
+                        <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <configuration>
+                    <releaseProfiles>apache-release</releaseProfiles><!-- xxx tiles still uses "release" instead of "apache-release" -->
+                    <goals>deploy site-deploy</goals>
+                </configuration>
+            </plugin>
+        </plugins>
+
+        <defaultGoal>install</defaultGoal>
+    </build>
+
+    <properties>
+        <tiles.osgi.symbolicName>org.apache.${project.artifactId}</tiles.osgi.symbolicName>
+        <tiles.osgi.export>org.apache.tiles.*;version=${project.version}</tiles.osgi.export>
+        <tiles.osgi.import>*</tiles.osgi.import>
+        <tiles.osgi.dynamicImport />
+        <tiles.osgi.private />
+        <tiles.manifestfile>target/osgi/MANIFEST.MF</tiles.manifestfile>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+  <dependencies>
+      <dependency>
+          <groupId>org.apache.tiles</groupId>
+          <artifactId>tiles-request-api</artifactId>
+      </dependency>
+      <dependency>
+      	<groupId>junit</groupId>
+      	<artifactId>junit</artifactId>
+      	<scope>test</scope>
+      </dependency>
+      <dependency>
+      	<groupId>org.easymock</groupId>
+      	<artifactId>easymock</artifactId>
+      	<scope>test</scope>
+      </dependency>
+  </dependencies>
+</project>
diff --git a/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/AbstractModelBody.java b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/AbstractModelBody.java
new file mode 100644
index 0000000..8fc9da9
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/AbstractModelBody.java
@@ -0,0 +1,83 @@
+/*
+ * $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.StringWriter;
+import java.io.Writer;
+
+import org.apache.tiles.autotag.core.runtime.util.NullWriter;
+
+/**
+ * Base class for the abstraction of the body.
+ *
+ * @version $Rev$ $Date$
+ */
+public abstract class AbstractModelBody implements ModelBody {
+
+    /**
+     * 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 = body.replaceAll("^\\s*|\\s*$", "");
+            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/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/AutotagRuntime.java b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/AutotagRuntime.java
new file mode 100644
index 0000000..d95d2ca
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/AutotagRuntime.java
@@ -0,0 +1,53 @@
+/*
+ * $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.request.Request;
+
+/**
+ * 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 {
+    /**
+     * Creates a new Request instance.
+     * 
+     * @return The Request.
+     */
+    Request 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.
+     */
+    Object getParameter(String name, Object defaultValue);
+}
diff --git a/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/ModelBody.java b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/ModelBody.java
new file mode 100644
index 0000000..81178ee
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/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/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/annotation/Parameter.java b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/annotation/Parameter.java
new file mode 100644
index 0000000..6f9c99b
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/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/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/annotation/package-info.java b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/annotation/package-info.java
new file mode 100644
index 0000000..261b48e
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/annotation/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Annotations to be used in template classes.
+ */
+package org.apache.tiles.autotag.core.runtime.annotation;
diff --git a/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/package-info.java b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/package-info.java
new file mode 100644
index 0000000..1e8324a
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Runtime part for all Autotag generated code.
+ */
+package org.apache.tiles.autotag.core.runtime;
diff --git a/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/util/NullWriter.java b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/util/NullWriter.java
new file mode 100644
index 0000000..6a66fb3
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/util/NullWriter.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.autotag.core.runtime.util;
+
+import java.io.Writer;
+
+/**
+ * A writer that does not write anything.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NullWriter extends Writer {
+
+    /** {@inheritDoc} */
+    @Override
+    public void close() {
+        // Does nothing
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void flush() {
+        // Does nothing
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(char[] cbuf, int off, int len) {
+        // Does nothing
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/util/package-info.java b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/util/package-info.java
new file mode 100644
index 0000000..7f2e356
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/src/main/java/org/apache/tiles/autotag/core/runtime/util/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Utilities for Autotag core runtime.
+ */
+package org.apache.tiles.autotag.core.runtime.util;
diff --git a/tiles-autotag/tiles-autotag-core-runtime/src/site/site.xml b/tiles-autotag/tiles-autotag-core-runtime/src/site/site.xml
new file mode 100644
index 0000000..0a7d00d
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Tiles Autotags">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Tiles Autotag"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-autotag/tiles-autotag-core-runtime/src/test/java/org/apache/tiles/autotag/core/runtime/AbstractModelBodyTest.java b/tiles-autotag/tiles-autotag-core-runtime/src/test/java/org/apache/tiles/autotag/core/runtime/AbstractModelBodyTest.java
new file mode 100644
index 0000000..c37ef45
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/src/test/java/org/apache/tiles/autotag/core/runtime/AbstractModelBodyTest.java
@@ -0,0 +1,154 @@
+/*
+ * $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 static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import org.apache.tiles.autotag.core.runtime.util.NullWriter;
+import org.junit.Test;
+
+/**
+ * Tests {@link AbstractModelBody}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AbstractModelBodyTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.core.runtime.AbstractModelBody#evaluate()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testEvaluate() throws IOException {
+        Writer writer = createMock(Writer.class);
+        AbstractModelBody modelBody = createMockBuilder(AbstractModelBody.class).withConstructor(writer).createMock();
+
+        modelBody.evaluate(writer);
+
+        replay(writer, modelBody);
+        modelBody.evaluate();
+        verify(writer, modelBody);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.core.runtime.AbstractModelBody#evaluateAsString()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testEvaluateAsString() throws IOException {
+        AbstractModelBody modelBody = new MockModelBody(null, "return me");
+        assertEquals("return me", modelBody.evaluateAsString());
+
+        modelBody = new MockModelBody(null, "\n   \n");
+        assertNull(modelBody.evaluateAsString());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.core.runtime.AbstractModelBody#evaluateAsString()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testEvaluateAsStringException() throws IOException {
+        Writer writer = createMock(Writer.class);
+        AbstractModelBody modelBody = createMockBuilder(AbstractModelBody.class).withConstructor(writer).createMock();
+
+        modelBody.evaluate(isA(StringWriter.class));
+        expectLastCall().andThrow(new IOException());
+
+        replay(writer, modelBody);
+        try {
+            modelBody.evaluateAsString();
+        } finally {
+            verify(writer, modelBody);
+        }
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.core.runtime.AbstractModelBody#evaluateWithoutWriting()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testEvaluateWithoutWriting() throws IOException {
+        Writer writer = createMock(Writer.class);
+        AbstractModelBody modelBody = createMockBuilder(AbstractModelBody.class).withConstructor(writer).createMock();
+
+        modelBody.evaluate(isA(NullWriter.class));
+
+        replay(writer, modelBody);
+        modelBody.evaluateWithoutWriting();
+        verify(writer, modelBody);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.core.runtime.AbstractModelBody#evaluateWithoutWriting()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testEvaluateWithoutWritingException() throws IOException {
+        Writer writer = createMock(Writer.class);
+        AbstractModelBody modelBody = createMockBuilder(AbstractModelBody.class).withConstructor(writer).createMock();
+
+        modelBody.evaluate(isA(NullWriter.class));
+        expectLastCall().andThrow(new IOException());
+
+        replay(writer, modelBody);
+        try {
+            modelBody.evaluateWithoutWriting();
+        } finally {
+            verify(writer, modelBody);
+        }
+    }
+
+    /**
+     * A mock model body.
+     *
+     * @version $Rev$ $Date$
+     */
+    public static class MockModelBody extends AbstractModelBody {
+
+        /**
+         * The result to return.
+         */
+        private String toReturn;
+
+        /**
+         * Constructor.
+         *
+         * @param defaultWriter The default writer.
+         * @param toReturn The result to return.
+         */
+        public MockModelBody(Writer defaultWriter, String toReturn) {
+            super(defaultWriter);
+            this.toReturn = toReturn;
+        }
+
+        @Override
+        public void evaluate(Writer writer) throws IOException {
+            writer.write(toReturn);
+        }
+
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core-runtime/src/test/java/org/apache/tiles/autotag/core/runtime/util/NullWriterTest.java b/tiles-autotag/tiles-autotag-core-runtime/src/test/java/org/apache/tiles/autotag/core/runtime/util/NullWriterTest.java
new file mode 100644
index 0000000..f8a2ba2
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core-runtime/src/test/java/org/apache/tiles/autotag/core/runtime/util/NullWriterTest.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.autotag.core.runtime.util;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link NullWriter}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NullWriterTest {
+
+    /**
+     * A dummy size.
+     */
+    private static final int DUMMY_SIZE = 15;
+    /**
+     * The object to test.
+     */
+    private NullWriter writer;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        writer = new NullWriter();
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.core.runtime.util.NullWriter#write(char[], int, int)}.
+     */
+    @Test
+    public void testWriteCharArrayIntInt() {
+        writer.write("Hello there".toCharArray(), 0, DUMMY_SIZE);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.core.runtime.util.NullWriter#flush()}.
+     */
+    @Test
+    public void testFlush() {
+        writer.flush();
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.core.runtime.util.NullWriter#close()}.
+     */
+    @Test
+    public void testClose() {
+        writer.close();
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/pom.xml b/tiles-autotag/tiles-autotag-core/pom.xml
new file mode 100644
index 0000000..b9fe1ed
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<artifactId>tiles-autotag</artifactId>
+		<groupId>org.apache.tiles</groupId>
+		<version>1.0-SNAPSHOT</version>
+	</parent>
+	<groupId>org.apache.tiles</groupId>
+	<artifactId>tiles-autotag-core</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<name>Autotag - Core</name>
+	<description>Core classes for Autotag.</description>
+	<build>
+		<resources>
+		</resources>
+		<testResources>
+			<testResource>
+				<directory>src/test/java</directory>
+				<includes>
+					<include>org/apache/tiles/autotag/core/internal/*.java</include>
+				</includes>
+			</testResource>
+			<testResource>
+				<directory>src/test/resources</directory>
+			</testResource>
+		</testResources>
+	</build>
+	<dependencies>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.thoughtworks.qdox</groupId>
+			<artifactId>qdox</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.tiles</groupId>
+			<artifactId>tiles-request-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.easymock</groupId>
+			<artifactId>easymock</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.tiles</groupId>
+			<artifactId>tiles-autotag-core-runtime</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.velocity</groupId>
+			<artifactId>velocity</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>commons-io</groupId>
+			<artifactId>commons-io</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/AutotagRuntimeException.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/AutotagRuntimeException.java
new file mode 100644
index 0000000..c4d05f9
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/AutotagRuntimeException.java
@@ -0,0 +1,64 @@
+/*
+ * $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;
+
+/**
+ * Generic exception for Autotag.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AutotagRuntimeException extends RuntimeException {
+
+    /**
+     * Constructor.
+     */
+    public AutotagRuntimeException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message of the exception.
+     */
+    public AutotagRuntimeException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param cause The cause.
+     */
+    public AutotagRuntimeException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message of the exception.
+     * @param cause The cause.
+     */
+    public AutotagRuntimeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/ClassParseException.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/ClassParseException.java
new file mode 100644
index 0000000..c2ef82b
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/ClassParseException.java
@@ -0,0 +1,64 @@
+/*
+ * $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;
+
+/**
+ * Thrown if there is a problem when parsing a class source.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ClassParseException extends AutotagRuntimeException {
+
+    /**
+     * Constructor.
+     */
+    public ClassParseException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message of the exception.
+     */
+    public ClassParseException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param cause The cause.
+     */
+    public ClassParseException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message of the exception.
+     * @param cause The cause.
+     */
+    public ClassParseException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/QDoxTemplateSuiteFactory.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/QDoxTemplateSuiteFactory.java
new file mode 100644
index 0000000..cfc903b
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/QDoxTemplateSuiteFactory.java
@@ -0,0 +1,259 @@
+/*
+ * $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;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.core.runtime.annotation.Parameter;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateMethod;
+import org.apache.tiles.autotag.model.TemplateParameter;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.tiles.autotag.model.TemplateSuiteFactory;
+import org.apache.tiles.request.Request;
+
+import com.thoughtworks.qdox.JavaDocBuilder;
+import com.thoughtworks.qdox.model.Annotation;
+import com.thoughtworks.qdox.model.DocletTag;
+import com.thoughtworks.qdox.model.JavaClass;
+import com.thoughtworks.qdox.model.JavaMethod;
+import com.thoughtworks.qdox.model.JavaParameter;
+import com.thoughtworks.qdox.model.Type;
+
+/**
+ * Creates a template suite using QDox.
+ *
+ * @version $Rev$ $Date$
+ */
+public class QDoxTemplateSuiteFactory implements TemplateSuiteFactory {
+
+    /**
+     * The suffix of parsed classes.
+     */
+    private static final String TEMPLATE_SUFFIX = "Model";
+
+    /**
+     * The Javadoc builder.
+     */
+    private JavaDocBuilder builder;
+
+    /**
+     * The name of the suite.
+     */
+    private String suiteName;
+
+    /**
+     * The documentation of the suite.
+     */
+    private String suiteDocumentation;
+
+    /**
+     * Constructor.
+     *
+     * @param sourceFiles All the source files to parse.
+     */
+    public QDoxTemplateSuiteFactory(File... sourceFiles) {
+        builder = new JavaDocBuilder();
+        try {
+            for (File file : sourceFiles) {
+                builder.addSource(file);
+            }
+        } catch (IOException e) {
+            throw new ClassParseException(
+                    "I/O Exception when adding source files", e);
+        }
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param urls All the URLs of source files to parse.
+     */
+    public QDoxTemplateSuiteFactory(URL... urls) {
+        builder = new JavaDocBuilder();
+        try {
+            for (URL url : urls) {
+                builder.addSource(url);
+            }
+        } catch (IOException e) {
+            throw new ClassParseException(
+                    "I/O Exception when adding source files", e);
+        }
+    }
+
+    /**
+     * Sets the suite name to assign to the created suite.
+     *
+     * @param suiteName The suite name.
+     */
+    public void setSuiteName(String suiteName) {
+        this.suiteName = suiteName;
+    }
+
+    /**
+     * Sets the suite documentation to assign to the suite.
+     *
+     * @param suiteDocumentation The suite documentation.
+     */
+    public void setSuiteDocumentation(String suiteDocumentation) {
+        this.suiteDocumentation = suiteDocumentation;
+    }
+
+    @Override
+    public TemplateSuite createTemplateSuite() {
+        List<TemplateClass> classes = new ArrayList<TemplateClass>();
+        for (JavaClass clazz : builder.getClasses()) {
+            String tagClassPrefix = getTagClassPrefix(clazz);
+            if (tagClassPrefix != null) {
+                String tagName = tagClassPrefix.substring(0, 1).toLowerCase()
+                        + tagClassPrefix.substring(1);
+                TemplateMethod executeMethod = null;
+                for (JavaMethod method : clazz.getMethods()) {
+                    if (isFeasible(method)) {
+                        executeMethod = createMethod(method);
+                    }
+                }
+                if (executeMethod != null) {
+                    TemplateClass templateClass = new TemplateClass(clazz
+                            .getFullyQualifiedName(), tagName, tagClassPrefix,
+                            executeMethod);
+                    templateClass.setDocumentation(clazz.getComment());
+                    classes.add(templateClass);
+                }
+            }
+        }
+        return new TemplateSuite(suiteName, suiteDocumentation, classes);
+    }
+
+    /**
+     * Computes the tag class prefix.
+     *
+     * @param clazz The parsed class.
+     * @return The tag class prefix.
+     */
+    private String getTagClassPrefix(JavaClass clazz) {
+        String tagName;
+        String simpleClassName = clazz.getName();
+        if (simpleClassName.endsWith(TEMPLATE_SUFFIX)
+                && simpleClassName.length() > TEMPLATE_SUFFIX.length()) {
+            tagName = simpleClassName.substring(0, 1).toUpperCase()
+                    + simpleClassName.substring(1, simpleClassName.length()
+                            - TEMPLATE_SUFFIX.length());
+        } else {
+            tagName = null;
+        }
+        return tagName;
+    }
+
+    /**
+     * Creates a template method descriptor from a parsed method.
+     *
+     * @param method The parsed method.
+     * @return The template method descriptor.
+     */
+    private TemplateMethod createMethod(JavaMethod method) {
+        List<TemplateParameter> params = new ArrayList<TemplateParameter>();
+        for (JavaParameter parameter : method.getParameters()) {
+            String exportedName = parameter.getName();
+            boolean required = false;
+            String defaultValue = null;
+            Annotation[] annotations = parameter.getAnnotations();
+            if (annotations != null && annotations.length > 0) {
+                boolean found = false;
+                for (int i = 0; i < annotations.length && !found; i++) {
+                    if (Parameter.class.getName().equals(annotations[i].getType().getFullyQualifiedName())) {
+                        found = true;
+                        String candidateName = (String) annotations[i].getNamedParameter("name");
+                        if (candidateName != null && candidateName.length() > 2) {
+                            exportedName = candidateName.substring(1, candidateName.length() - 1);
+                        }
+                        required = "true".equals(annotations[i].getNamedParameter("required"));
+                        candidateName = (String) annotations[i].getNamedParameter("defaultValue");
+                        if (candidateName != null && candidateName.length() > 2) {
+                            defaultValue = candidateName.substring(1, candidateName.length() - 1);
+                        }
+                    }
+                }
+            }
+            TemplateParameter templateParameter = new TemplateParameter(
+                    parameter.getName(), exportedName, parameter.getType()
+                            .getFullyQualifiedName(), defaultValue, required);
+            params.add(templateParameter);
+        }
+        TemplateMethod templateMethod = new TemplateMethod(method.getName(),
+                params);
+        templateMethod.setDocumentation(method.getComment());
+        DocletTag[] tags = method.getTagsByName("param");
+        for (DocletTag tag : tags) {
+            String[] tagParams = tag.getParameters();
+            if (tagParams.length > 0) {
+                TemplateParameter templateParameter = templateMethod
+                        .getParameterByName(tagParams[0]);
+                if (templateParameter != null) {
+                    String tagValue = tag.getValue();
+                    int pos = tagValue.indexOf(" ");
+                    templateParameter.setDocumentation(tagValue.substring(pos)
+                            .trim());
+                }
+            }
+        }
+        return templateMethod;
+    }
+
+    /**
+     * Verifies if the method can be used as an "execute" method.
+     *
+     * @param method The parsed method.
+     * @return <code>true</code> if it is an execute method.
+     */
+    private boolean isFeasible(JavaMethod method) {
+        Type returns = method.getReturns();
+        if ("execute".equals(method.getName()) && returns != null
+                && "void".equals(returns.getFullyQualifiedName())
+                && method.isPublic() && !method.isStatic()
+                && !method.isAbstract() && !method.isConstructor()) {
+            JavaParameter[] params = method.getParameters();
+            if (params.length > 0) {
+                JavaParameter param = params[params.length - 1];
+                if (Request.class.getName().equals(
+                        param.getType().getFullyQualifiedName())) {
+                    return true;
+                }
+            }
+            if (params.length >= 2) {
+                JavaParameter param1 = params[params.length - 2];
+                JavaParameter param2 = params[params.length - 1];
+                if (Request.class.getName().equals(
+                        param1.getType().getFullyQualifiedName())
+                        && ModelBody.class.getName().equals(
+                                param2.getType().getFullyQualifiedName())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/package-info.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/package-info.java
new file mode 100644
index 0000000..d7ed8d7
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/core/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * The Autotag core parsing code.
+ */
+package org.apache.tiles.autotag.core;
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateClassGenerator.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateClassGenerator.java
new file mode 100644
index 0000000..dd40607
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateClassGenerator.java
@@ -0,0 +1,142 @@
+/*
+ * $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.generate;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+
+import org.apache.tiles.autotag.core.AutotagRuntimeException;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.tiles.autotag.tool.StringTool;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+
+/**
+ * A base template class generator.
+ *
+ * @version $Rev$ $Date$
+ */
+public abstract class AbstractTemplateClassGenerator implements
+        TemplateClassGenerator {
+
+    /**
+     * The Velocity engine to use.
+     */
+    private VelocityEngine velocityEngine;
+
+    /**
+     * Constructor.
+     *
+     * @param velocityEngine The Velocity engine.
+     */
+    public AbstractTemplateClassGenerator(VelocityEngine velocityEngine) {
+        this.velocityEngine = velocityEngine;
+    }
+
+    @Override
+    public void generate(File directory, String packageName,
+            TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters,
+            String runtimeClass) {
+        File dir = new File(directory, getDirectoryName(directory, packageName,
+                suite, clazz, parameters, runtimeClass));
+        dir.mkdirs();
+        File file = new File(dir, getFilename(dir, packageName, suite, clazz, parameters, runtimeClass));
+        VelocityContext context = new VelocityContext();
+        context.put("packageName", packageName);
+        context.put("suite", suite);
+        context.put("clazz", clazz);
+        context.put("stringTool", new StringTool());
+        context.put("parameters", parameters);
+        context.put("runtimeClass", runtimeClass);
+        try {
+            file.createNewFile();
+            Template template = velocityEngine.getTemplate(getTemplatePath(dir,
+                    packageName, suite, clazz, parameters, runtimeClass));
+            Writer writer = new FileWriter(file);
+            try {
+                template.merge(context, writer);
+            } finally {
+                writer.close();
+            }
+        } catch (ResourceNotFoundException e) {
+            throw new AutotagRuntimeException("Cannot find template resource",
+                    e);
+        } catch (ParseErrorException e) {
+            throw new AutotagRuntimeException(
+                    "The template resource is not parseable", e);
+        } catch (IOException e) {
+            throw new AutotagRuntimeException(
+                    "I/O Exception when generating file", e);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new AutotagRuntimeException(
+                    "Another generic exception while parsing the template resource",
+                    e);
+        }
+    }
+
+    /**
+     * Calculates and returns the template path.
+     *
+     * @param directory The directory where the file will be written.
+     * @param packageName The name of the package.
+     * @param suite The template suite.
+     * @param clazz The template class.
+     * @param parameters The map of parameters.
+     * @return The template path.
+     */
+    protected abstract String getTemplatePath(File directory,
+            String packageName, TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters, String runtimeClass);
+
+    /**
+     * Calculates and returns the filename of the generated file.
+     *
+     * @param directory The directory where the file will be written.
+     * @param packageName The name of the package.
+     * @param suite The template suite.
+     * @param clazz The template class.
+     * @param parameters The map of parameters.
+     * @return The template path.
+     */
+    protected abstract String getFilename(File directory, String packageName,
+            TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters, String runtimeClass);
+
+    /**
+     * Calculates and returns the directory where the file will be written..
+     *
+     * @param directory The directory where the file will be written.
+     * @param packageName The name of the package.
+     * @param suite The template suite.
+     * @param clazz The template class.
+     * @param parameters The map of parameters.
+     * @return The template path.
+     */
+    protected abstract String getDirectoryName(File directory,
+            String packageName, TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters, String runtimeClass);
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateSuiteGenerator.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateSuiteGenerator.java
new file mode 100644
index 0000000..51f94b5
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/AbstractTemplateSuiteGenerator.java
@@ -0,0 +1,132 @@
+/*
+ * $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.generate;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+
+import org.apache.tiles.autotag.core.AutotagRuntimeException;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.tiles.autotag.tool.StringTool;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+
+/**
+ * A base template suite generator.
+ *
+ * @version $Rev$ $Date$
+ */
+public abstract class AbstractTemplateSuiteGenerator implements TemplateSuiteGenerator {
+
+    /**
+     * The velocity engine.
+     */
+    private VelocityEngine velocityEngine;
+
+    /**
+     * Constructor.
+     *
+     * @param velocityEngine The Velocity engine.
+     */
+    public AbstractTemplateSuiteGenerator(VelocityEngine velocityEngine) {
+        this.velocityEngine = velocityEngine;
+    }
+
+    @Override
+    public void generate(File directory, String packageName, TemplateSuite suite, Map<String, String> parameters) {
+        File dir = new File(directory, getDirectoryName(directory, packageName, suite, parameters));
+        dir.mkdirs();
+        File file = new File(dir, getFilename(dir, packageName, suite, parameters));
+        VelocityContext context = new VelocityContext();
+        context.put("packageName", packageName);
+        context.put("suite", suite);
+        context.put("stringTool", new StringTool());
+        context.put("parameters", parameters);
+        try {
+            file.createNewFile();
+            Template template = velocityEngine.getTemplate(getTemplatePath(dir,
+                    packageName, suite, parameters));
+            Writer writer = new FileWriter(file);
+            try {
+                template.merge(context, writer);
+            } finally {
+                writer.close();
+            }
+        } catch (ResourceNotFoundException e) {
+            throw new AutotagRuntimeException("Cannot find template resource", e);
+        } catch (ParseErrorException e) {
+            throw new AutotagRuntimeException("The template resource is not parseable", e);
+        } catch (IOException e) {
+            throw new AutotagRuntimeException(
+                    "I/O Exception when generating file", e);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new AutotagRuntimeException(
+                    "Another generic exception while parsing the template resource",
+                    e);
+        }
+    }
+
+    /**
+     * Calculates and returns the template path.
+     *
+     * @param directory The directory where the file will be written.
+     * @param packageName The name of the package.
+     * @param suite The template suite.
+     * @param parameters The map of parameters.
+     * @return The template path.
+     */
+    protected abstract String getTemplatePath(File directory,
+            String packageName, TemplateSuite suite,
+            Map<String, String> parameters);
+
+    /**
+     * Calculates and returns the filename of the generated file.
+     *
+     * @param directory The directory where the file will be written.
+     * @param packageName The name of the package.
+     * @param suite The template suite.
+     * @param parameters The map of parameters.
+     * @return The template path.
+     */
+    protected abstract String getFilename(File directory, String packageName,
+            TemplateSuite suite, Map<String, String> parameters);
+
+    /**
+     * Calculates and returns the directory where the file will be written..
+     *
+     * @param directory The directory where the file will be written.
+     * @param packageName The name of the package.
+     * @param suite The template suite.
+     * @param parameters The map of parameters.
+     * @return The template path.
+     */
+    protected abstract String getDirectoryName(File directory,
+            String packageName, TemplateSuite suite,
+            Map<String, String> parameters);
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/BasicTemplateGenerator.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/BasicTemplateGenerator.java
new file mode 100644
index 0000000..7fe7ffd
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/BasicTemplateGenerator.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.generate;
+
+import java.io.File;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateSuite;
+
+/**
+ * The basic template generator. Use {@link TemplateGeneratorBuilder} to
+ * create instances of this class.
+ *
+ * @version $Rev$ $Date$
+ */
+class BasicTemplateGenerator implements TemplateGenerator {
+
+    /**
+     * The template suite generators.
+     */
+    private List<TSGeneratorDirectoryPair> templateSuiteGenerators;
+
+    /**
+     * The template class generators.
+     */
+    private List<TCGeneratorDirectoryPair> templateClassGenerators;
+
+    /**
+     * Indicates that this generator generates resources.
+     */
+    private boolean generatingResources = false;
+
+    /**
+     * Indicates that this generator generates classes.
+     */
+    private boolean generatingClasses = false;
+
+    /**
+     * Constructor.
+     *
+     * @param templateSuiteGenerators The template suite generators.
+     * @param templateClassGenerators The template class generators.
+     * @param generatingClasses Indicates that this generator generates classes.
+     * @param generatingResources Indicates that this generator generates resources.
+     */
+    BasicTemplateGenerator(
+            List<TSGeneratorDirectoryPair> templateSuiteGenerators,
+            List<TCGeneratorDirectoryPair> templateClassGenerators,
+            boolean generatingClasses, boolean generatingResources) {
+        this.templateSuiteGenerators = templateSuiteGenerators;
+        this.templateClassGenerators = templateClassGenerators;
+        this.generatingClasses = generatingClasses;
+        this.generatingResources = generatingResources;
+    }
+
+
+
+    @Override
+    public void generate(String packageName, TemplateSuite suite, Map<String, String> parameters, 
+        String runtimeClass) {
+        for (TSGeneratorDirectoryPair pair : templateSuiteGenerators) {
+            pair.getGenerator().generate(pair.getDirectory(), packageName, suite, parameters);
+        }
+        for (TemplateClass templateClass : suite.getTemplateClasses()) {
+            for (TCGeneratorDirectoryPair pair : templateClassGenerators) {
+                pair.getGenerator().generate(pair.getDirectory(), packageName,
+                        suite, templateClass, parameters, runtimeClass);
+            }
+        }
+    }
+
+    /**
+     * A pair of a template suite generator and a directory.
+     *
+     * @version $Rev$ $Date$
+     */
+    static class TSGeneratorDirectoryPair {
+        /**
+         * The directory where files are generated.
+         */
+        private File directory;
+
+        /**
+         * The generator.
+         */
+        private TemplateSuiteGenerator generator;
+
+        /**
+         * Constructor.
+         *
+         * @param directory The directory where files are generated.
+         * @param generator The generator.
+         */
+        public TSGeneratorDirectoryPair(File directory,
+                TemplateSuiteGenerator generator) {
+            this.directory = directory;
+            this.generator = generator;
+        }
+
+        /**
+         * Returns the directory where files are generated.
+         *
+         * @return The directory where files are generated.
+         */
+        public File getDirectory() {
+            return directory;
+        }
+
+        /**
+         * Returns the generator.
+         *
+         * @return The generator.
+         */
+        public TemplateSuiteGenerator getGenerator() {
+            return generator;
+        }
+    }
+
+    /**
+     * A pair of a template class generator and a directory.
+     *
+     * @version $Rev$ $Date$
+     */
+    static class TCGeneratorDirectoryPair {
+        /**
+         * The directory where files are generated.
+         */
+        private File directory;
+
+        /**
+         * The generator.
+         */
+        private TemplateClassGenerator generator;
+
+        /**
+         * Constructor.
+         *
+         * @param directory The directory where files are generated.
+         * @param generator The generator.
+         */
+        public TCGeneratorDirectoryPair(File directory,
+                TemplateClassGenerator generator) {
+            this.directory = directory;
+            this.generator = generator;
+        }
+
+        /**
+         * Returns the directory where files are generated.
+         *
+         * @return The directory where files are generated.
+         */
+        public File getDirectory() {
+            return directory;
+        }
+
+        /**
+         * Returns the generator.
+         *
+         * @return The generator.
+         */
+        public TemplateClassGenerator getGenerator() {
+            return generator;
+        }
+    }
+
+    @Override
+    public boolean isGeneratingResources() {
+        return generatingResources;
+    }
+
+    @Override
+    public boolean isGeneratingClasses() {
+        return generatingClasses;
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateClassGenerator.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateClassGenerator.java
new file mode 100644
index 0000000..c6fe9e0
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateClassGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * $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.generate;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateSuite;
+
+/**
+ * Generates code from a parsed class.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface TemplateClassGenerator {
+
+    /**
+     * Generates the code.
+     *
+     * @param directory The base directory where the code will be put.
+     * @param packageName The package name.
+     * @param suite The template suite.
+     * @param clazz The template class.
+     * @param parameters Configuration parameters.
+     * @param runtimeClass The RequestBuilder implementation.
+     */
+    void generate(File directory, String packageName, TemplateSuite suite,
+            TemplateClass clazz, Map<String, String> parameters,
+            String runtimeClass);
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateGenerator.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateGenerator.java
new file mode 100644
index 0000000..db67873
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateGenerator.java
@@ -0,0 +1,59 @@
+/*
+ * $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.generate;
+
+
+import java.util.Map;
+
+import org.apache.tiles.autotag.model.TemplateSuite;
+
+/**
+ * Generates all the code for a template suite.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface TemplateGenerator {
+
+    /**
+     * Generates the code.
+     *
+     * @param packageName The package name.
+     * @param suite The template suite.
+     * @param parameters Configuration parameters.
+     * @param runtimeClass The RequestBuilder implementation.
+     */
+    void generate(String packageName, TemplateSuite suite, Map<String, String> parameters, 
+        String runtimeClass);
+
+    /**
+     * Indicates that this generator generates resources.
+     *
+     * @return <code>true</code> if the generator generates resources.
+     */
+    boolean isGeneratingResources();
+
+    /**
+     * Indicates that this generator generates classes.
+     *
+     * @return <code>true</code> if the generator generates classes.
+     */
+    boolean isGeneratingClasses();
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateGeneratorBuilder.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateGeneratorBuilder.java
new file mode 100644
index 0000000..3f29934
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateGeneratorBuilder.java
@@ -0,0 +1,184 @@
+/*
+ * $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.generate;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tiles.autotag.generate.BasicTemplateGenerator.TCGeneratorDirectoryPair;
+import org.apache.tiles.autotag.generate.BasicTemplateGenerator.TSGeneratorDirectoryPair;
+
+/**
+ * Builds a {@link TemplateGenerator}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TemplateGeneratorBuilder {
+
+    /**
+     * The template suite generators.
+     */
+    private List<TSGeneratorDirectoryPair> templateSuiteGenerators;
+
+    /**
+     * The template class generators.
+     */
+    private List<TCGeneratorDirectoryPair> templateClassGenerators;
+
+    /**
+     * Indicates that this generator generates resources.
+     */
+    private boolean generatingResources = false;
+
+    /**
+     * Indicates that this generator generates classes.
+     */
+    private boolean generatingClasses = false;
+
+    /**
+     * The classes output directory.
+     */
+    private File classesOutputDirectory;
+
+    /**
+     * The resources output directory.
+     */
+    private File resourcesOutputDirectory;
+
+    /**
+     * Constructor.
+     */
+    private TemplateGeneratorBuilder() {
+        templateSuiteGenerators = new ArrayList<BasicTemplateGenerator.TSGeneratorDirectoryPair>();
+        templateClassGenerators = new ArrayList<BasicTemplateGenerator.TCGeneratorDirectoryPair>();
+    }
+
+    /**
+     * Creates a new instance of the builder.
+     *
+     * @return A new instance of the builder.
+     */
+    public static TemplateGeneratorBuilder createNewInstance() {
+        return new TemplateGeneratorBuilder();
+    }
+
+    /**
+     * Sets the classes output directory.
+     *
+     * @param classesOutputDirectory The classes output directory.
+     * @return This instance.
+     */
+    public TemplateGeneratorBuilder setClassesOutputDirectory(File classesOutputDirectory) {
+        this.classesOutputDirectory = classesOutputDirectory;
+        return this;
+    }
+
+    /**
+     * Sets the resources output directory.
+     *
+     * @param resourcesOutputDirectory The resources output directory.
+     * @return This instance.
+     */
+    public TemplateGeneratorBuilder setResourcesOutputDirectory(File resourcesOutputDirectory) {
+        this.resourcesOutputDirectory = resourcesOutputDirectory;
+        return this;
+    }
+
+    /**
+     * Adds a new template suite generator to generate classes.
+     *
+     * @param generator The generator to add.
+     * @return This instance.
+     */
+    public TemplateGeneratorBuilder addClassesTemplateSuiteGenerator(TemplateSuiteGenerator generator) {
+        if (classesOutputDirectory == null) {
+            throw new NullPointerException(
+                    "Classes output directory not specified, call 'setClassesOutputDirectory' first");
+        }
+        templateSuiteGenerators.add(new TSGeneratorDirectoryPair(
+                classesOutputDirectory, generator));
+        generatingClasses = true;
+        return this;
+    }
+
+    /**
+     * Adds a new template class generator to generate classes.
+     *
+     * @param generator The generator to add.
+     * @return This instance.
+     */
+    public TemplateGeneratorBuilder addClassesTemplateClassGenerator(TemplateClassGenerator generator) {
+        if (classesOutputDirectory == null) {
+            throw new NullPointerException(
+                    "Classes output directory not specified, call 'setClassesOutputDirectory' first");
+        }
+        templateClassGenerators.add(new TCGeneratorDirectoryPair(
+                classesOutputDirectory, generator));
+        generatingClasses = true;
+        return this;
+    }
+
+    /**
+     * Adds a new template suite generator to generate resources.
+     *
+     * @param generator The generator to add.
+     * @return This instance.
+     */
+    public TemplateGeneratorBuilder addResourcesTemplateSuiteGenerator(TemplateSuiteGenerator generator) {
+        if (resourcesOutputDirectory == null) {
+            throw new NullPointerException(
+                    "Resources output directory not specified, call 'setClassesOutputDirectory' first");
+        }
+        templateSuiteGenerators.add(new TSGeneratorDirectoryPair(
+                resourcesOutputDirectory, generator));
+        generatingResources = true;
+        return this;
+    }
+
+    /**
+     * Adds a new template class generator to generate resources.
+     *
+     * @param generator The generator to add.
+     * @return This instance.
+     */
+    public TemplateGeneratorBuilder addResourcesTemplateClassGenerator(TemplateClassGenerator generator) {
+        if (resourcesOutputDirectory == null) {
+            throw new NullPointerException(
+                    "Resources output directory not specified, call 'setClassesOutputDirectory' first");
+        }
+        templateClassGenerators.add(new TCGeneratorDirectoryPair(
+                resourcesOutputDirectory, generator));
+        generatingResources = true;
+        return this;
+    }
+
+    /**
+     * Builds and returns a new template generator.
+     *
+     * @return The new template generator.
+     */
+    public TemplateGenerator build() {
+        return new BasicTemplateGenerator(templateSuiteGenerators,
+                templateClassGenerators, generatingClasses, generatingResources);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateGeneratorFactory.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateGeneratorFactory.java
new file mode 100644
index 0000000..76b40d7
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateGeneratorFactory.java
@@ -0,0 +1,36 @@
+/*
+ * $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.generate;
+
+/**
+ * Creates a new template generator.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface TemplateGeneratorFactory {
+
+    /**
+     * Creates a template generator.
+     *
+     * @return The newly created template generator.
+     */
+    TemplateGenerator createTemplateGenerator();
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateSuiteGenerator.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateSuiteGenerator.java
new file mode 100644
index 0000000..1d7df00
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/TemplateSuiteGenerator.java
@@ -0,0 +1,44 @@
+/*
+ * $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.generate;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.tiles.autotag.model.TemplateSuite;
+
+/**
+ * Generates code from a template suite.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface TemplateSuiteGenerator {
+
+    /**
+     * Generates the code.
+     *
+     * @param directory The base directory where the code will be put.
+     * @param packageName The package name.
+     * @param suite The template suite.
+     * @param parameters Configuration parameters.
+     */
+    void generate(File directory, String packageName, TemplateSuite suite, Map<String, String> parameters);
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/package-info.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/package-info.java
new file mode 100644
index 0000000..6256497
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/generate/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * The Autotag generation classes.
+ */
+package org.apache.tiles.autotag.generate;
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateClass.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateClass.java
new file mode 100644
index 0000000..f869c95
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/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/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateMethod.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateMethod.java
new file mode 100644
index 0000000..d5703c5
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/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/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateParameter.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateParameter.java
new file mode 100644
index 0000000..2755fcc
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateParameter.java
@@ -0,0 +1,181 @@
+/*
+ * $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;
+import org.apache.tiles.request.Request;
+
+/**
+ * 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 prameter.
+     */
+    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;
+
+    /**
+     * 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) {
+        this.name = name;
+        this.exportedName = exportedName;
+        this.type = type;
+        this.defaultValue = defaultValue;
+        this.required = required;
+    }
+
+    /**
+     * 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.class.getName().equals(type);
+    }
+
+    /**
+     * 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 + "]";
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateSuite.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateSuite.java
new file mode 100644
index 0000000..25361c8
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/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/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateSuiteFactory.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateSuiteFactory.java
new file mode 100644
index 0000000..4c45ebc
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/TemplateSuiteFactory.java
@@ -0,0 +1,37 @@
+/*
+ * $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;
+
+
+/**
+ * Creates a new template suite.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface TemplateSuiteFactory {
+
+    /**
+     * Creates a template suite.
+     *
+     * @return The created template suite.
+     */
+    TemplateSuite createTemplateSuite();
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/package-info.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/package-info.java
new file mode 100644
index 0000000..a1130d2
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/model/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Domain model classes representing a parsed template suite.
+ */
+package org.apache.tiles.autotag.model;
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/tool/StringTool.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/tool/StringTool.java
new file mode 100644
index 0000000..7341feb
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/tool/StringTool.java
@@ -0,0 +1,143 @@
+/*
+ * $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.tool;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tiles.autotag.core.AutotagRuntimeException;
+
+/**
+ * A Velocity tools to manipulate strings.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StringTool {
+
+    /**
+     * Maps a primitive type to its default value as a string.
+     */
+    private Map<String, String> type2default;
+
+    /**
+     * Maps a primitive type to its boxed version.
+     */
+    private Map<String, String> primitive2wrapped;
+
+    /**
+     * Constructor.
+     */
+    public StringTool() {
+        type2default = new HashMap<String, String>();
+        type2default.put("byte", "0");
+        type2default.put("short", "0");
+        type2default.put("int", "0");
+        type2default.put("long", "0L");
+        type2default.put("float", "0.0f");
+        type2default.put("double", "0.0d");
+        type2default.put("char", "'\\u0000'");
+        type2default.put("boolean", "false");
+
+        primitive2wrapped = new HashMap<String, String>();
+        primitive2wrapped.put("byte", Byte.class.getName());
+        primitive2wrapped.put("short", Short.class.getName());
+        primitive2wrapped.put("int", Integer.class.getName());
+        primitive2wrapped.put("long", Long.class.getName());
+        primitive2wrapped.put("float", Float.class.getName());
+        primitive2wrapped.put("double", Double.class.getName());
+        primitive2wrapped.put("char", Character.class.getName());
+        primitive2wrapped.put("boolean", Boolean.class.getName());
+    }
+
+    /**
+     * Creates a list of strings, separating a string when a newline is encountered.
+     *
+     * @param toSplit The string to split.
+     * @return The list of splitted strings.
+     */
+    public List<String> splitOnNewlines(String toSplit) {
+        List<String> retValue = new ArrayList<String>();
+        if (toSplit == null) {
+            return retValue;
+        }
+        Reader reader = new StringReader(toSplit);
+        BufferedReader bufReader = new BufferedReader(reader);
+        try {
+            String line;
+            while ((line = bufReader.readLine()) != null) {
+                retValue.add(line);
+            }
+        } catch (IOException e) {
+            throw new AutotagRuntimeException(
+                    "Cannot read the string completely", e);
+        }
+        return retValue;
+    }
+
+    /**
+     * Creates a string in which the first character is capitalized.
+     *
+     * @param string The string to use.
+     * @return The same string with the first character capitalized.
+     */
+    public String capitalizeFirstLetter(String string) {
+        return string.substring(0, 1).toUpperCase() + string.substring(1);
+    }
+
+    /**
+     * Returns the default value for a type.
+     *
+     * @param type The type.
+     * @param overriddenDefaultValue The default value, as specified by developers.
+     * @return The default value to use.
+     */
+    public String getDefaultValue(String type, String overriddenDefaultValue) {
+        if (overriddenDefaultValue != null) {
+            return overriddenDefaultValue;
+        }
+
+        String retValue = type2default.get(type);
+        if (retValue == null) {
+            retValue = "null";
+        }
+        return retValue;
+    }
+
+    /**
+     * Returns the class to be used to cast an Object.
+     *
+     * @param type The type to use, even a primitive type.
+     * @return The class to be used in casts.
+     */
+    public String getClassToCast(String type) {
+        String retValue = primitive2wrapped.get(type);
+        if (retValue == null) {
+            retValue = type;
+        }
+        return retValue;
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/tool/package-info.java b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/tool/package-info.java
new file mode 100644
index 0000000..558c330
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/java/org/apache/tiles/autotag/tool/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Velocity tools to be used in Velocity templates.
+ */
+package org.apache.tiles.autotag.tool;
diff --git a/tiles-autotag/tiles-autotag-core/src/main/resources/org/apache/tiles/autotag/velocity.properties b/tiles-autotag/tiles-autotag-core/src/main/resources/org/apache/tiles/autotag/velocity.properties
new file mode 100644
index 0000000..0a9f2a5
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/main/resources/org/apache/tiles/autotag/velocity.properties
@@ -0,0 +1,115 @@
+# 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.
+
+# ----------------------------------------------------------------------------
+# These are the default properties for the
+# Velocity Runtime. These values are used when
+# Runtime.init() is called, and when Runtime.init(properties)
+# fails to find the specificed properties file.
+# ----------------------------------------------------------------------------
+
+
+# ----------------------------------------------------------------------------
+# R U N T I M E  L O G
+# ----------------------------------------------------------------------------
+# Velocity uses the Servlet APIs logging facilites.
+
+# ----------------------------------------------------------------------------
+# This controls if Runtime.error(), info() and warn() messages include the
+# whole stack trace. The last property controls whether invalid references
+# are logged.
+# ----------------------------------------------------------------------------
+
+runtime.log.invalid.reference = true
+
+
+# ----------------------------------------------------------------------------
+# T E M P L A T E  E N C O D I N G
+# ----------------------------------------------------------------------------
+
+input.encoding=ISO-8859-1
+output.encoding=ISO-8859-1
+
+
+# ----------------------------------------------------------------------------
+# F O R E A C H  P R O P E R T I E S
+# ----------------------------------------------------------------------------
+# These properties control how the counter is accessed in the #foreach
+# directive. By default the reference $velocityCount will be available
+# in the body of the #foreach directive. The default starting value
+# for this reference is 1.
+# ----------------------------------------------------------------------------
+
+directive.foreach.counter.name = velocityCount
+directive.foreach.counter.initial.value = 1
+
+
+# ----------------------------------------------------------------------------
+# I N C L U D E  P R O P E R T I E S
+# ----------------------------------------------------------------------------
+# These are the properties that governed the way #include'd content
+# is governed.
+# ----------------------------------------------------------------------------
+
+directive.include.output.errormsg.start = <!-- include error :
+directive.include.output.errormsg.end   =  see error log -->
+
+
+# ----------------------------------------------------------------------------
+# P A R S E  P R O P E R T I E S
+# ----------------------------------------------------------------------------
+
+directive.parse.max.depth = 10
+
+
+# ----------------------------------------------------------------------------
+# VELOCIMACRO PROPERTIES
+# ----------------------------------------------------------------------------
+# global : name of default global library.  It is expected to be in the regular
+# template path.  You may remove it (either the file or this property) if
+# you wish with no harm.
+# ----------------------------------------------------------------------------
+# dev-changes by Marino
+resource.loader=class, string
+
+class.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
+
+velocimacro.library.autoreload = false
+
+velocimacro.permissions.allow.inline = true
+velocimacro.permissions.allow.inline.to.replace.global = false
+velocimacro.permissions.allow.inline.local.scope = false
+
+velocimacro.context.localscope = false
+
+# ----------------------------------------------------------------------------
+# INTERPOLATION
+# ----------------------------------------------------------------------------
+# turn off and on interpolation of references and directives in string
+# literals.  ON by default :)
+# ----------------------------------------------------------------------------
+runtime.interpolate.string.literals = true
+
+
+# ----------------------------------------------------------------------------
+# RESOURCE MANAGEMENT
+# ----------------------------------------------------------------------------
+# Allows alternative ResourceManager and ResourceCache implementations
+# to be plugged in.
+# ----------------------------------------------------------------------------
+resource.manager.class = org.apache.velocity.runtime.resource.ResourceManagerImpl
+resource.manager.cache.class = org.apache.velocity.runtime.resource.ResourceCacheImpl
diff --git a/tiles-autotag/tiles-autotag-core/src/site/site.xml b/tiles-autotag/tiles-autotag-core/src/site/site.xml
new file mode 100644
index 0000000..0a7d00d
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Tiles Autotags">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Tiles Autotag"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/AutotagRuntimeExceptionTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/AutotagRuntimeExceptionTest.java
new file mode 100644
index 0000000..bf3338a
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/AutotagRuntimeExceptionTest.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.autotag.core;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link AutotagRuntimeException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AutotagRuntimeExceptionTest {
+
+    /**
+     * Test method for {@link AutotagRuntimeException#AutotagRuntimeException()}.
+     */
+    @Test
+    public void testAutotagRuntimeException() {
+        AutotagRuntimeException exception = new AutotagRuntimeException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link AutotagRuntimeException#AutotagRuntimeException(java.lang.String)}.
+     */
+    @Test
+    public void testAutotagRuntimeExceptionString() {
+        AutotagRuntimeException exception = new AutotagRuntimeException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link AutotagRuntimeException#AutotagRuntimeException(java.lang.Throwable)}.
+     */
+    @Test
+    public void testAutotagRuntimeExceptionThrowable() {
+        Throwable cause = new Throwable();
+        AutotagRuntimeException exception = new AutotagRuntimeException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for {@link AutotagRuntimeException#AutotagRuntimeException(java.lang.String, java.lang.Throwable)}.
+     */
+    @Test
+    public void testAutotagRuntimeExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        AutotagRuntimeException exception = new AutotagRuntimeException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/ClassParseExceptionTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/ClassParseExceptionTest.java
new file mode 100644
index 0000000..c3b2ae6
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/ClassParseExceptionTest.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.autotag.core;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link ClassParseException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ClassParseExceptionTest {
+
+    /**
+     * Test method for {@link ClassParseException#ClassParseException()}.
+     */
+    @Test
+    public void testClassParseException() {
+        ClassParseException exception = new ClassParseException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link ClassParseException#ClassParseException(java.lang.String)}.
+     */
+    @Test
+    public void testClassParseExceptionString() {
+        ClassParseException exception = new ClassParseException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link ClassParseException#ClassParseException(java.lang.Throwable)}.
+     */
+    @Test
+    public void testClassParseExceptionThrowable() {
+        Throwable cause = new Throwable();
+        ClassParseException exception = new ClassParseException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for {@link ClassParseException#ClassParseException(java.lang.String, java.lang.Throwable)}.
+     */
+    @Test
+    public void testClassParseExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        ClassParseException exception = new ClassParseException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/QDoxTemplateSuiteFactoryTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/QDoxTemplateSuiteFactoryTest.java
new file mode 100644
index 0000000..d417d7b
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/QDoxTemplateSuiteFactoryTest.java
@@ -0,0 +1,148 @@
+/*
+ * $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;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tiles.autotag.core.internal.AnnotatedExampleModel;
+import org.apache.tiles.autotag.core.internal.ExampleExecutableModel;
+import org.apache.tiles.autotag.core.internal.ExampleModel;
+import org.apache.tiles.autotag.core.internal.NotFeasibleExampleModel;
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateMethod;
+import org.apache.tiles.autotag.model.TemplateParameter;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.tiles.request.Request;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link QDoxTemplateSuiteFactory}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class QDoxTemplateSuiteFactoryTest {
+
+    /**
+     * The factory to test.
+     */
+    private QDoxTemplateSuiteFactory factory;
+
+    /**
+     * @throws java.lang.Exception
+     */
+    @Before
+    public void setUp() {
+        factory = new QDoxTemplateSuiteFactory(
+                getClass().getResource("/org/apache/tiles/autotag/core/internal/ExampleModel.java"),
+                getClass().getResource("/org/apache/tiles/autotag/core/internal/AnnotatedExampleModel.java"),
+                getClass().getResource("/org/apache/tiles/autotag/core/internal/ExampleExcluded.java"),
+                getClass().getResource("/org/apache/tiles/autotag/core/internal/ExampleExecutableModel.java"),
+                getClass().getResource("/org/apache/tiles/autotag/core/internal/NotFeasibleExampleModel.java"));
+        factory.setSuiteName("The suite name");
+        factory.setSuiteDocumentation("This are the docs");
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.core.DefaultTemplateSuiteFactory#createTemplateSuite()}.
+     */
+    @Test
+    public void testCreateTemplateSuite() {
+        TemplateSuite suite = factory.createTemplateSuite();
+        assertEquals("The suite name", suite.getName());
+        assertEquals("This are the docs", suite.getDocumentation());
+        assertEquals(3, suite.getTemplateClasses().size());
+
+        TemplateClass templateClass = suite.getTemplateClassByName(ExampleModel.class.getName());
+        assertNotNull(templateClass);
+        assertEquals(ExampleModel.class.getName(), templateClass.getName());
+        assertEquals("Example start/stop template.", templateClass.getDocumentation());
+        TemplateMethod templateMethod = templateClass.getExecuteMethod();
+        assertNotNull(templateMethod);
+        assertTrue(templateMethod.hasBody());
+        assertTrue(templateClass.hasBody());
+        assertEquals("execute", templateMethod.getName());
+        assertEquals("It starts.", templateMethod.getDocumentation());
+        List<TemplateParameter> parameters = new ArrayList<TemplateParameter>(templateMethod.getParameters());
+        assertEquals(4, parameters.size());
+        TemplateParameter parameter = parameters.get(0);
+        assertEquals("one", parameter.getName());
+        assertEquals("java.lang.String", parameter.getType());
+        assertEquals("Parameter one.", parameter.getDocumentation());
+        parameter = parameters.get(1);
+        assertEquals("two", parameter.getName());
+        assertEquals("int", parameter.getType());
+        assertEquals("Parameter two.", parameter.getDocumentation());
+        parameter = parameters.get(2);
+        assertEquals("request", parameter.getName());
+        assertEquals(Request.class.getName(), parameter.getType());
+        assertEquals("The request.", parameter.getDocumentation());
+        parameter = parameters.get(3);
+        assertEquals("modelBody", parameter.getName());
+        assertEquals(ModelBody.class.getName(), parameter.getType());
+        assertEquals("The model body.", parameter.getDocumentation());
+
+        templateClass = suite.getTemplateClassByName(AnnotatedExampleModel.class.getName());
+        assertNotNull(templateClass);
+        assertEquals(AnnotatedExampleModel.class.getName(), templateClass.getName());
+        templateMethod = templateClass.getExecuteMethod();
+        assertNotNull(templateMethod);
+        assertEquals("execute", templateMethod.getName());
+        parameters = new ArrayList<TemplateParameter>(templateMethod.getParameters());
+        assertEquals(4, parameters.size());
+        parameter = parameters.get(0);
+        assertEquals("one", parameter.getName());
+        assertEquals("alternateOne", parameter.getExportedName());
+        assertEquals("java.lang.String", parameter.getType());
+        assertEquals("Parameter one.", parameter.getDocumentation());
+        assertEquals("hello", parameter.getDefaultValue());
+        assertTrue(parameter.isRequired());
+
+        templateClass = suite.getTemplateClassByName(ExampleExecutableModel.class.getName());
+        assertNotNull(templateClass);
+        assertEquals(ExampleExecutableModel.class.getName(), templateClass.getName());
+        assertEquals("Example executable template.", templateClass.getDocumentation());
+        templateMethod = templateClass.getExecuteMethod();
+        assertNotNull(templateMethod);
+        assertEquals("execute", templateMethod.getName());
+        assertEquals("It executes.", templateMethod.getDocumentation());
+        parameters = new ArrayList<TemplateParameter>(templateMethod.getParameters());
+        assertEquals(3, parameters.size());
+        parameter = parameters.get(0);
+        assertEquals("one", parameter.getName());
+        assertEquals("java.lang.String", parameter.getType());
+        assertEquals("Parameter one.", parameter.getDocumentation());
+        parameter = parameters.get(1);
+        assertEquals("two", parameter.getName());
+        assertEquals("int", parameter.getType());
+        assertEquals("Parameter two.", parameter.getDocumentation());
+        parameter = parameters.get(2);
+        assertEquals("request", parameter.getName());
+        assertEquals(Request.class.getName(), parameter.getType());
+        assertEquals("The request.", parameter.getDocumentation());
+
+        assertNull(suite.getTemplateClassByName(NotFeasibleExampleModel.class.getName()));
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/AnnotatedExampleModel.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/AnnotatedExampleModel.java
new file mode 100644
index 0000000..f2ca684
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/AnnotatedExampleModel.java
@@ -0,0 +1,47 @@
+/*
+ * $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.internal;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.core.runtime.annotation.Parameter;
+import org.apache.tiles.request.Request;
+
+/**
+ * Example start/stop template.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AnnotatedExampleModel {
+
+    /**
+     * It starts.
+     *
+     * @param one Parameter one.
+     * @param two Parameter two.
+     * @param request The request.
+     * @param modelBody The model body.
+     */
+    public void execute(
+            @Parameter(defaultValue = "hello", name = "alternateOne", required = true) String one,
+            int two, Request request, ModelBody modelBody) {
+        // Does nothing.
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/ExampleExcluded.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/ExampleExcluded.java
new file mode 100644
index 0000000..6693700
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/ExampleExcluded.java
@@ -0,0 +1,44 @@
+/*
+ * $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.internal;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.request.Request;
+
+/**
+ * Example start/stop template.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ExampleExcluded {
+
+    /**
+     * It starts.
+     *
+     * @param one Parameter one.
+     * @param two Parameter two.
+     * @param request The request.
+     * @param modelBody The model body.
+     */
+    public void execute(String one, int two, Request request, ModelBody modelBody) {
+        // Does nothing.
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/ExampleExecutableModel.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/ExampleExecutableModel.java
new file mode 100644
index 0000000..487e201
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/ExampleExecutableModel.java
@@ -0,0 +1,42 @@
+/*
+ * $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.internal;
+
+import org.apache.tiles.request.Request;
+
+/**
+ * Example executable template.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ExampleExecutableModel {
+
+    /**
+     * It executes.
+     *
+     * @param one Parameter one.
+     * @param two Parameter two.
+     * @param request The request.
+     */
+    public void execute(String one, int two, Request request) {
+        // Does nothing.
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/ExampleModel.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/ExampleModel.java
new file mode 100644
index 0000000..3ed332d
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/ExampleModel.java
@@ -0,0 +1,44 @@
+/*
+ * $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.internal;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.request.Request;
+
+/**
+ * Example start/stop template.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ExampleModel {
+
+    /**
+     * It starts.
+     *
+     * @param one Parameter one.
+     * @param two Parameter two.
+     * @param request The request.
+     * @param modelBody The model body.
+     */
+    public void execute(String one, int two, Request request, ModelBody modelBody) {
+        // Does nothing.
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/NotFeasibleExampleModel.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/NotFeasibleExampleModel.java
new file mode 100644
index 0000000..44dc258
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/core/internal/NotFeasibleExampleModel.java
@@ -0,0 +1,38 @@
+/*
+ * $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.internal;
+
+/**
+ * This won't be registered.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NotFeasibleExampleModel {
+
+    /**
+     * It starts.
+     *
+     * @param whatever Doesn't matter.
+     */
+    public void start(String whatever) {
+        // Does nothing.
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/AbstractTemplateClassGeneratorTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/AbstractTemplateClassGeneratorTest.java
new file mode 100644
index 0000000..35bdd5b
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/AbstractTemplateClassGeneratorTest.java
@@ -0,0 +1,265 @@
+/*
+ * $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.generate;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.tiles.autotag.core.AutotagRuntimeException;
+import org.apache.tiles.autotag.core.ClassParseException;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link AbstractTemplateClassGenerator}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AbstractTemplateClassGeneratorTest {
+
+    /**
+     * The velocity engine.
+     */
+    private VelocityEngine velocityEngine;
+
+    /**
+     * The temporary directory.
+     */
+    private File directory;
+
+    /**
+     * The generator to test.
+     */
+    private AbstractTemplateClassGenerator generator;
+
+    /**
+     * Sets up the test.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    @Before
+    public void setUp() throws IOException {
+        velocityEngine = createMock(VelocityEngine.class);
+        generator = createMockBuilder(AbstractTemplateClassGenerator.class)
+                .withConstructor(velocityEngine).createMock();
+        directory = File.createTempFile("autotag", null);
+    }
+
+    /**
+     * Tears down the test.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void tearDown() throws IOException {
+        FileUtils.deleteDirectory(directory);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateClassGenerator#generate(java.io.File,
+     * String, TemplateSuite, TemplateClass, Map)}.
+     * @throws Exception If something goes wrong.
+     */
+    @Test
+    public void testGenerate() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        TemplateClass clazz = createMock(TemplateClass.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+        String runtimeClass = "org.apache.tiles.autotag.test.DoStuffRuntime";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, clazz, parameters, runtimeClass)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, clazz, parameters, runtimeClass)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, clazz, parameters, runtimeClass)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andReturn(template);
+        template.merge(isA(VelocityContext.class), isA(FileWriter.class));
+
+        replay(velocityEngine, generator, suite, clazz, template, parameters);
+        generator.generate(directory, packageName, suite, clazz, parameters, runtimeClass);
+        verify(velocityEngine, generator, suite, clazz, template, parameters);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateClassGenerator#generate(File, String, TemplateSuite, TemplateClass, Map)}.
+     * @throws Exception If something goes wrong.
+     */
+    @Test(expected = AutotagRuntimeException.class)
+    public void testGenerateException1() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        TemplateClass clazz = createMock(TemplateClass.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+        String runtimeClass = "org.apache.tiles.autotag.test.DoStuffRuntime";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, clazz, parameters, runtimeClass)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, clazz, parameters, runtimeClass)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, clazz, parameters, runtimeClass)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andThrow(new ResourceNotFoundException("hello"));
+
+        replay(velocityEngine, generator, suite, clazz, template, parameters);
+        generator.generate(directory, packageName, suite, clazz, parameters, runtimeClass);
+        verify(velocityEngine, generator, suite, clazz, template, parameters);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateClassGenerator#generate(File, String, TemplateSuite, TemplateClass, Map)}.
+     * @throws Exception If something goes wrong.
+     */
+    @Test(expected = AutotagRuntimeException.class)
+    public void testGenerateException2() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        TemplateClass clazz = createMock(TemplateClass.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+        String runtimeClass = "org.apache.tiles.autotag.test.DoStuffRuntime";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, clazz, parameters, runtimeClass)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, clazz, parameters, runtimeClass)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, clazz, parameters, runtimeClass)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andThrow(new ParseErrorException("hello"));
+
+        replay(velocityEngine, generator, suite, clazz, template, parameters);
+        generator.generate(directory, packageName, suite, clazz, parameters, runtimeClass);
+        verify(velocityEngine, generator, suite, clazz, template, parameters);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateClassGenerator#generate(File, String, TemplateSuite, TemplateClass, Map)}.
+     * @throws Exception If something goes wrong.
+     */
+    @Test(expected = AutotagRuntimeException.class)
+    public void testGenerateException3() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        TemplateClass clazz = createMock(TemplateClass.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+        String runtimeClass = "org.apache.tiles.autotag.test.DoStuffRuntime";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, clazz, parameters, runtimeClass)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, clazz, parameters, runtimeClass)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, clazz, parameters, runtimeClass)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andThrow(new Exception());
+
+        replay(velocityEngine, generator, suite, clazz, template, parameters);
+        generator.generate(directory, packageName, suite, clazz, parameters, runtimeClass);
+        verify(velocityEngine, generator, suite, clazz, template, parameters);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateClassGenerator#generate(File, String, TemplateSuite, TemplateClass, Map)}.
+     * @throws Exception If something goes wrong.
+     * @throws ParseErrorException If something goes wrong.
+     * @throws ResourceNotFoundException If something goes wrong.
+     */
+    @Test(expected = AutotagRuntimeException.class)
+    public void testGenerateException4() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        TemplateClass clazz = createMock(TemplateClass.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+        String runtimeClass = "org.apache.tiles.autotag.test.DoStuffRuntime";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, clazz, parameters, runtimeClass)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, clazz, parameters, runtimeClass)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, clazz, parameters, runtimeClass)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andReturn(template);
+        template.merge(isA(VelocityContext.class), isA(FileWriter.class));
+        expectLastCall().andThrow(new IOException());
+
+        replay(velocityEngine, generator, suite, clazz, template, parameters);
+        generator.generate(directory, packageName, suite, clazz, parameters, runtimeClass);
+        verify(velocityEngine, generator, suite, clazz, template, parameters);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateClassGenerator#generate(File, String, TemplateSuite, TemplateClass, Map)}.
+     * @throws Exception If something goes wrong.
+     * @throws ParseErrorException If something goes wrong.
+     * @throws ResourceNotFoundException If something goes wrong.
+     */
+    @Test(expected = ClassParseException.class)
+    public void testGenerateException5() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        TemplateClass clazz = createMock(TemplateClass.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+        String runtimeClass = "org.apache.tiles.autotag.test.DoStuffRuntime";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, clazz, parameters, runtimeClass)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, clazz, parameters, runtimeClass)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, clazz, parameters, runtimeClass)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andReturn(template);
+        template.merge(isA(VelocityContext.class), isA(FileWriter.class));
+        expectLastCall().andThrow(new ClassParseException());
+
+        replay(velocityEngine, generator, suite, clazz, template, parameters);
+        generator.generate(directory, packageName, suite, clazz, parameters, runtimeClass);
+        verify(velocityEngine, generator, suite, clazz, template, parameters);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/AbstractTemplateSuiteGeneratorTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/AbstractTemplateSuiteGeneratorTest.java
new file mode 100644
index 0000000..b98da2c
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/AbstractTemplateSuiteGeneratorTest.java
@@ -0,0 +1,259 @@
+/*
+ * $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.generate;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.tiles.autotag.core.AutotagRuntimeException;
+import org.apache.tiles.autotag.core.ClassParseException;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link AbstractTemplateSuiteGenerator}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AbstractTemplateSuiteGeneratorTest {
+
+    /**
+     * The velocity engine.
+     */
+    private VelocityEngine velocityEngine;
+
+    /**
+     * The temporary directory.
+     */
+    private File directory;
+
+    /**
+     * The generator to test.
+     */
+    private AbstractTemplateSuiteGenerator generator;
+
+    /**
+     * Sets up the test.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    @Before
+    public void setUp() throws IOException {
+        velocityEngine = createMock(VelocityEngine.class);
+        generator = createMockBuilder(AbstractTemplateSuiteGenerator.class)
+                .withConstructor(velocityEngine).createMock();
+        directory = File.createTempFile("autotag", null);
+    }
+
+    /**
+     * Tears down the test.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void tearDown() throws IOException {
+        FileUtils.deleteDirectory(directory);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateSuiteGenerator#generate(File, String, TemplateSuite, Map)}.
+     * @throws Exception If something goes wrong.
+     * @throws ParseErrorException If something goes wrong.
+     * @throws ResourceNotFoundException If something goes wrong.
+     */
+    @Test
+    public void testGenerate() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, parameters)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, parameters)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, parameters)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andReturn(template);
+        template.merge(isA(VelocityContext.class), isA(FileWriter.class));
+
+        replay(velocityEngine, generator, suite, template, parameters);
+        generator.generate(directory, packageName, suite, parameters);
+        verify(velocityEngine, generator, suite, template, parameters);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateSuiteGenerator#generate(File, String, TemplateSuite, Map)}.
+     * @throws Exception If something goes wrong.
+     * @throws ParseErrorException If something goes wrong.
+     * @throws ResourceNotFoundException If something goes wrong.
+     */
+    @Test(expected = AutotagRuntimeException.class)
+    public void testGenerateException1() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, parameters)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, parameters)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, parameters)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andThrow(new ResourceNotFoundException("hello"));
+
+        replay(velocityEngine, generator, suite, template, parameters);
+        generator.generate(directory, packageName, suite, parameters);
+        verify(velocityEngine, generator, suite, template, parameters);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateSuiteGenerator#generate(File, String, TemplateSuite, Map)}.
+     * @throws Exception If something goes wrong.
+     * @throws ParseErrorException If something goes wrong.
+     * @throws ResourceNotFoundException If something goes wrong.
+     */
+    @Test(expected = AutotagRuntimeException.class)
+    public void testGenerateException2() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, parameters)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, parameters)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, parameters)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andThrow(new ParseErrorException("hello"));
+
+        replay(velocityEngine, generator, suite, template, parameters);
+        generator.generate(directory, packageName, suite, parameters);
+        verify(velocityEngine, generator, suite, template, parameters);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateSuiteGenerator#generate(File, String, TemplateSuite, Map)}.
+     * @throws Exception If something goes wrong.
+     * @throws ParseErrorException If something goes wrong.
+     * @throws ResourceNotFoundException If something goes wrong.
+     */
+    @Test(expected = AutotagRuntimeException.class)
+    public void testGenerateException3() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, parameters)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, parameters)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, parameters)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andThrow(new Exception());
+
+        replay(velocityEngine, generator, suite, template, parameters);
+        generator.generate(directory, packageName, suite, parameters);
+        verify(velocityEngine, generator, suite, template, parameters);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateSuiteGenerator#generate(File, String, TemplateSuite, Map)}.
+     * @throws Exception If something goes wrong.
+     * @throws ParseErrorException If something goes wrong.
+     * @throws ResourceNotFoundException If something goes wrong.
+     */
+    @Test(expected = AutotagRuntimeException.class)
+    public void testGenerateException4() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, parameters)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, parameters)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, parameters)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andReturn(template);
+        template.merge(isA(VelocityContext.class), isA(FileWriter.class));
+        expectLastCall().andThrow(new IOException());
+
+        replay(velocityEngine, generator, suite, template, parameters);
+        generator.generate(directory, packageName, suite, parameters);
+        verify(velocityEngine, generator, suite, template, parameters);
+    }
+
+    /**
+     * Test method for {@link AbstractTemplateSuiteGenerator#generate(File, String, TemplateSuite, Map)}.
+     * @throws Exception If something goes wrong.
+     * @throws ParseErrorException If something goes wrong.
+     * @throws ResourceNotFoundException If something goes wrong.
+     */
+    @Test(expected = ClassParseException.class)
+    public void testGenerateException5() throws Exception {
+        directory.delete();
+        directory.mkdir();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        Template template = createMock(Template.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        String packageName = "org.apache.tiles.autotag.test";
+
+        expect(generator.getDirectoryName(directory, packageName, suite, parameters)).andReturn("mydir");
+        File mydir = new File(directory, "mydir");
+        expect(generator.getFilename(mydir , packageName, suite, parameters)).andReturn("myfile.txt");
+        String sampleVmPath = "/sample.vm";
+        expect(generator.getTemplatePath(mydir, packageName, suite, parameters)).andReturn(sampleVmPath);
+        expect(velocityEngine.getTemplate("/sample.vm")).andReturn(template);
+        template.merge(isA(VelocityContext.class), isA(FileWriter.class));
+        expectLastCall().andThrow(new ClassParseException());
+
+        replay(velocityEngine, generator, suite, template, parameters);
+        generator.generate(directory, packageName, suite, parameters);
+        verify(velocityEngine, generator, suite, template, parameters);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/BasicTemplateGeneratorTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/BasicTemplateGeneratorTest.java
new file mode 100644
index 0000000..15463d3
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/BasicTemplateGeneratorTest.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.autotag.generate;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tiles.autotag.generate.BasicTemplateGenerator.TCGeneratorDirectoryPair;
+import org.apache.tiles.autotag.generate.BasicTemplateGenerator.TSGeneratorDirectoryPair;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.junit.Test;
+
+/**
+ * Tests {@link BasicTemplateGenerator}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class BasicTemplateGeneratorTest {
+
+    /**
+     * Test method for {@link BasicTemplateGenerator#generate(String, TemplateSuite, Map)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGenerate() throws IOException {
+        File file = File.createTempFile("tiles", "template");
+        file.deleteOnExit();
+        TemplateSuite suite = createMock(TemplateSuite.class);
+        TemplateClass templateClass = createMock(TemplateClass.class);
+        TemplateSuiteGenerator templateSuiteGenerator = createMock(TemplateSuiteGenerator.class);
+        TemplateClassGenerator templateClassGenerator = createMock(TemplateClassGenerator.class);
+        @SuppressWarnings("unchecked")
+        Map<String, String> parameters = createMock(Map.class);
+        List<TemplateClass> templateClasses = new ArrayList<TemplateClass>();
+
+        templateClasses.add(templateClass);
+
+        expect(suite.getTemplateClasses()).andReturn(templateClasses);
+        templateSuiteGenerator.generate(file, "my.package", suite, parameters);
+        templateClassGenerator.generate(file, "my.package", suite, templateClass, parameters, "my.Runtime");
+
+        replay(suite, templateClass, templateSuiteGenerator, templateClassGenerator, parameters);
+        TSGeneratorDirectoryPair tsPair = new TSGeneratorDirectoryPair(file, templateSuiteGenerator);
+        TCGeneratorDirectoryPair tcPair = new TCGeneratorDirectoryPair(file, templateClassGenerator);
+        List<TSGeneratorDirectoryPair> tsList = new ArrayList<BasicTemplateGenerator.TSGeneratorDirectoryPair>();
+        tsList.add(tsPair);
+        List<TCGeneratorDirectoryPair> tcList = new ArrayList<BasicTemplateGenerator.TCGeneratorDirectoryPair>();
+        tcList.add(tcPair);
+        BasicTemplateGenerator generator = new BasicTemplateGenerator(tsList, tcList, true, false);
+        assertTrue(generator.isGeneratingClasses());
+        assertFalse(generator.isGeneratingResources());
+        generator.generate("my.package", suite, parameters, "my.Runtime");
+        verify(suite, templateClass, templateSuiteGenerator, templateClassGenerator, parameters);
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/TemplateGeneratorBuilderTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/TemplateGeneratorBuilderTest.java
new file mode 100644
index 0000000..998bd6d
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/generate/TemplateGeneratorBuilderTest.java
@@ -0,0 +1,169 @@
+/*
+ * $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.generate;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.File;
+
+import org.junit.Test;
+
+/**
+ * @author antonio
+ *
+ * @version $Rev$ $Date$
+ */
+public class TemplateGeneratorBuilderTest {
+
+    /**
+     * Test method for {@link TemplateGeneratorBuilder#addClassesTemplateSuiteGenerator(TemplateSuiteGenerator)}.
+     */
+    @Test
+    public void testAddClassesTemplateSuiteGenerator() {
+        File dir = createMock(File.class);
+        TemplateSuiteGenerator generator = createMock(TemplateSuiteGenerator.class);
+
+        replay(dir, generator);
+        TemplateGenerator templateGenerator = TemplateGeneratorBuilder
+                .createNewInstance().setClassesOutputDirectory(dir)
+                .addClassesTemplateSuiteGenerator(generator).build();
+        assertTrue(templateGenerator.isGeneratingClasses());
+        assertFalse(templateGenerator.isGeneratingResources());
+        verify(dir, generator);
+    }
+
+    /**
+     * Test method for {@link TemplateGeneratorBuilder#addClassesTemplateSuiteGenerator(TemplateSuiteGenerator)}.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testAddClassesTemplateSuiteGeneratorException() {
+        TemplateSuiteGenerator generator = createMock(TemplateSuiteGenerator.class);
+
+        replay(generator);
+        try {
+            TemplateGeneratorBuilder.createNewInstance()
+                    .addClassesTemplateSuiteGenerator(generator);
+        } finally {
+            verify(generator);
+        }
+    }
+
+    /**
+     * Test method for {@link TemplateGeneratorBuilder#addClassesTemplateClassGenerator(TemplateClassGenerator)}.
+     */
+    @Test
+    public void testAddClassesTemplateClassGenerator() {
+        File dir = createMock(File.class);
+        TemplateClassGenerator generator = createMock(TemplateClassGenerator.class);
+
+        replay(dir, generator);
+        TemplateGenerator templateGenerator = TemplateGeneratorBuilder
+                .createNewInstance().setClassesOutputDirectory(dir)
+                .addClassesTemplateClassGenerator(generator).build();
+        assertTrue(templateGenerator.isGeneratingClasses());
+        assertFalse(templateGenerator.isGeneratingResources());
+        verify(dir, generator);
+    }
+
+    /**
+     * Test method for {@link TemplateGeneratorBuilder#addClassesTemplateClassGenerator(TemplateClassGenerator)}.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testAddClassesTemplateClassGeneratorException() {
+        TemplateClassGenerator generator = createMock(TemplateClassGenerator.class);
+
+        replay(generator);
+        try {
+            TemplateGeneratorBuilder.createNewInstance()
+                    .addClassesTemplateClassGenerator(generator);
+        } finally {
+            verify(generator);
+        }
+    }
+
+    /**
+     * Test method for {@link TemplateGeneratorBuilder#addResourcesTemplateSuiteGenerator(TemplateSuiteGenerator)}.
+     */
+    @Test
+    public void testAddResourcesTemplateSuiteGenerator() {
+        File dir = createMock(File.class);
+        TemplateSuiteGenerator generator = createMock(TemplateSuiteGenerator.class);
+
+        replay(dir, generator);
+        TemplateGenerator templateGenerator = TemplateGeneratorBuilder
+                .createNewInstance().setResourcesOutputDirectory(dir)
+                .addResourcesTemplateSuiteGenerator(generator).build();
+        assertFalse(templateGenerator.isGeneratingClasses());
+        assertTrue(templateGenerator.isGeneratingResources());
+        verify(dir, generator);
+    }
+
+    /**
+     * Test method for {@link TemplateGeneratorBuilder#addResourcesTemplateSuiteGenerator(TemplateSuiteGenerator)}.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testAddResourcesTemplateSuiteGeneratorException() {
+        TemplateSuiteGenerator generator = createMock(TemplateSuiteGenerator.class);
+
+        replay(generator);
+        try {
+            TemplateGeneratorBuilder.createNewInstance()
+                    .addResourcesTemplateSuiteGenerator(generator);
+        } finally {
+            verify(generator);
+        }
+    }
+
+    /**
+     * Test method for {@link TemplateGeneratorBuilder#addResourcesTemplateClassGenerator(TemplateClassGenerator)}.
+     */
+    @Test
+    public void testAddResourcesTemplateClassGenerator() {
+        File dir = createMock(File.class);
+        TemplateClassGenerator generator = createMock(TemplateClassGenerator.class);
+
+        replay(dir, generator);
+        TemplateGenerator templateGenerator = TemplateGeneratorBuilder
+                .createNewInstance().setResourcesOutputDirectory(dir)
+                .addResourcesTemplateClassGenerator(generator).build();
+        assertFalse(templateGenerator.isGeneratingClasses());
+        assertTrue(templateGenerator.isGeneratingResources());
+        verify(dir, generator);
+    }
+
+    /**
+     * Test method for {@link TemplateGeneratorBuilder#addResourcesTemplateClassGenerator(TemplateClassGenerator)}.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testAddResourcesTemplateClassGeneratorException() {
+        TemplateClassGenerator generator = createMock(TemplateClassGenerator.class);
+
+        replay(generator);
+        try {
+            TemplateGeneratorBuilder.createNewInstance()
+                    .addResourcesTemplateClassGenerator(generator);
+        } finally {
+            verify(generator);
+        }
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateClassTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateClassTest.java
new file mode 100644
index 0000000..e2acdb8
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateClassTest.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.autotag.model;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * @author antonio
+ *
+ * @version $Rev$ $Date$
+ */
+public class TemplateClassTest {
+
+    /**
+     * Test method for {@link TemplateClass#TemplateClass(String)}.
+     */
+    @Test
+    public void testTemplateConstructor1() {
+        TemplateClass templateClass = new TemplateClass("name");
+        assertEquals("name", templateClass.getName());
+        assertNull(templateClass.getTagName());
+        assertNull(templateClass.getTagClassPrefix());
+        assertNull(templateClass.getExecuteMethod());
+        Collection<TemplateParameter> params = templateClass.getParameters();
+        assertTrue(params.isEmpty());
+    }
+
+    /**
+     * Test method for {@link TemplateClass#TemplateClass(String, String, String, TemplateMethod)}.
+     */
+    @Test
+    public void testTemplateConstructor2() {
+        TemplateMethod method = createMock(TemplateMethod.class);
+
+        replay(method);
+        TemplateClass templateClass = new TemplateClass("name", "tagName", "tagClassPrefix", method);
+        assertEquals("name", templateClass.getName());
+        assertEquals("tagName", templateClass.getTagName());
+        assertEquals("tagClassPrefix", templateClass.getTagClassPrefix());
+        assertEquals(method, templateClass.getExecuteMethod());
+        verify(method);
+    }
+
+    /**
+     * Test method for {@link TemplateClass#getSimpleName()}.
+     */
+    @Test
+    public void testGetSimpleName() {
+        TemplateClass templateClass = new TemplateClass("name");
+        assertEquals("name", templateClass.getSimpleName());
+        templateClass = new TemplateClass("org.whatever.Hello");
+        assertEquals("Hello", templateClass.getSimpleName());
+    }
+
+    /**
+     * Test method for {@link TemplateClass#setDocumentation(String)}.
+     */
+    @Test
+    public void testSetDocumentation() {
+        TemplateClass templateClass = new TemplateClass("name");
+        templateClass.setDocumentation("docs");
+        assertEquals("docs", templateClass.getDocumentation());
+    }
+
+    /**
+     * Test method for {@link TemplateClass#getParameters()}.
+     */
+    @Test
+    public void testGetParameters() {
+        TemplateParameter param1 = createMock(TemplateParameter.class);
+        TemplateParameter param2 = createMock(TemplateParameter.class);
+        TemplateParameter param3 = createMock(TemplateParameter.class);
+        TemplateParameter param4 = createMock(TemplateParameter.class);
+        TemplateMethod method = createMock(TemplateMethod.class);
+        List<TemplateParameter> params = new ArrayList<TemplateParameter>();
+
+        expect(method.getParameters()).andReturn(params);
+        expect(param1.isRequest()).andReturn(true);
+        expect(param2.isRequest()).andReturn(false);
+        expect(param2.isBody()).andReturn(true);
+        expect(param3.isRequest()).andReturn(false);
+        expect(param3.isBody()).andReturn(false);
+        expect(param4.isRequest()).andReturn(false);
+        expect(param4.isBody()).andReturn(false);
+        expect(param3.getName()).andReturn("param1");
+        expect(param4.getName()).andReturn("param2");
+
+        replay(param1, param2, param3, param4, method);
+        params.add(param1);
+        params.add(param2);
+        params.add(param3);
+        params.add(param4);
+
+        TemplateClass templateClass = new TemplateClass("name", "tagName", "tagClassPrefix", method);
+        Collection<TemplateParameter> returnedParams = templateClass.getParameters();
+        Iterator<TemplateParameter> paramIt = returnedParams.iterator();
+        assertSame(param3, paramIt.next());
+        assertSame(param4, paramIt.next());
+        assertFalse(paramIt.hasNext());
+        verify(param1, param2, param3, param4, method);
+    }
+
+    /**
+     * Test method for {@link TemplateClass#hasBody()}.
+     */
+    @Test
+    public void testHasBody() {
+        TemplateMethod method = createMock(TemplateMethod.class);
+        expect(method.hasBody()).andReturn(true);
+
+        replay(method);
+        TemplateClass templateClass = new TemplateClass("name", "tagName", "tagClassPrefix", method);
+        assertTrue(templateClass.hasBody());
+        verify(method);
+    }
+
+    /**
+     * Test method for {@link TemplateClass#toString()}.
+     */
+    @Test
+    public void testToString() {
+        TemplateMethod method = new TemplateMethod("method", new ArrayList<TemplateParameter>());
+        TemplateClass templateClass = new TemplateClass("name", "tagName", "tagClassPrefix", method);
+        assertEquals(
+                "TemplateClass [name=name, tagName=tagName, tagClassPrefix=tagClassPrefix, "
+                        + "documentation=null, executeMethod=TemplateMethod "
+                        + "[name=method, documentation=null, parameters={}]]",
+                templateClass.toString());
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateMethodTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateMethodTest.java
new file mode 100644
index 0000000..c868109
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateMethodTest.java
@@ -0,0 +1,130 @@
+/*
+ * $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 static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link TemplateMethod}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TemplateMethodTest {
+
+    /**
+     * Tests {@link TemplateMethod#TemplateMethod(String, Iterable)}.
+     */
+    @Test
+    public void testTemplateMethod() {
+        TemplateParameter param1 = createMock(TemplateParameter.class);
+        TemplateParameter param2 = createMock(TemplateParameter.class);
+
+        expect(param1.getName()).andReturn("param1");
+        expect(param2.getName()).andReturn("param2");
+
+        replay(param1, param2);
+        List<TemplateParameter> parameters = new ArrayList<TemplateParameter>();
+        parameters.add(param1);
+        parameters.add(param2);
+
+        TemplateMethod method = new TemplateMethod("method", parameters);
+        assertEquals("method", method.getName());
+        Iterator<TemplateParameter> params = method.getParameters().iterator();
+        assertSame(param1, params.next());
+        assertSame(param2, params.next());
+        assertFalse(params.hasNext());
+        assertSame(param1, method.getParameterByName("param1"));
+        assertSame(param2, method.getParameterByName("param2"));
+        verify(param1, param2);
+    }
+
+    /**
+     * Tests {@link TemplateMethod#setDocumentation(String)}.
+     */
+    @Test
+    public void testSetDocumentation() {
+        TemplateMethod method = new TemplateMethod("method", new ArrayList<TemplateParameter>());
+        method.setDocumentation("docs");
+        assertEquals("docs", method.getDocumentation());
+    }
+
+    /**
+     * Tests {@link TemplateMethod#hasBody()}.
+     */
+    @Test
+    public void testHasBody() {
+        TemplateParameter param1 = createMock(TemplateParameter.class);
+        TemplateParameter param2 = createMock(TemplateParameter.class);
+
+        expect(param1.getName()).andReturn("param1");
+        expect(param2.getName()).andReturn("param2");
+        expect(param1.isBody()).andReturn(true);
+
+        replay(param1, param2);
+        List<TemplateParameter> parameters = new ArrayList<TemplateParameter>();
+        parameters.add(param1);
+        parameters.add(param2);
+
+        TemplateMethod method = new TemplateMethod("method", parameters);
+        assertTrue(method.hasBody());
+        verify(param1, param2);
+    }
+
+    /**
+     * Tests {@link TemplateMethod#hasBody()}.
+     */
+    @Test
+    public void testHasBody2() {
+        TemplateParameter param1 = createMock(TemplateParameter.class);
+        TemplateParameter param2 = createMock(TemplateParameter.class);
+
+        expect(param1.getName()).andReturn("param1");
+        expect(param2.getName()).andReturn("param2");
+        expect(param1.isBody()).andReturn(false);
+        expect(param2.isBody()).andReturn(false);
+
+        replay(param1, param2);
+        List<TemplateParameter> parameters = new ArrayList<TemplateParameter>();
+        parameters.add(param1);
+        parameters.add(param2);
+
+        TemplateMethod method = new TemplateMethod("method", parameters);
+        assertFalse(method.hasBody());
+        verify(param1, param2);
+    }
+
+    /**
+     * Tests {@link TemplateMethod#toString()}.
+     */
+    @Test
+    public void testToString() {
+        TemplateMethod method = new TemplateMethod("method", new ArrayList<TemplateParameter>());
+        assertEquals("TemplateMethod [name=method, documentation=null, parameters={}]", method.toString());
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateParameterTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateParameterTest.java
new file mode 100644
index 0000000..c0a100d
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateParameterTest.java
@@ -0,0 +1,94 @@
+/*
+ * $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 static org.junit.Assert.*;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.request.Request;
+import org.junit.Test;
+
+/**
+ * Tests {@link TemplateParameter}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TemplateParameterTest {
+
+    /**
+     * Tests {@link TemplateParameter#TemplateParameter(String, String, String, String, boolean)}.
+     */
+    @Test
+    public void testTemplateParameter() {
+        TemplateParameter parameter = new TemplateParameter("name", "exportedName", "type", "defaultValue", true);
+        assertEquals("name", parameter.getName());
+        assertEquals("exportedName", parameter.getExportedName());
+        assertEquals("type", parameter.getType());
+        assertEquals("defaultValue", parameter.getDefaultValue());
+        assertTrue(parameter.isRequired());
+        assertEquals("ExportedName", parameter.getGetterSetterSuffix());
+        assertFalse(parameter.isBody());
+        assertFalse(parameter.isRequest());
+
+        parameter = new TemplateParameter("name", "exportedName", Request.class.getName(), "defaultValue", false);
+        assertEquals("name", parameter.getName());
+        assertEquals("exportedName", parameter.getExportedName());
+        assertEquals(Request.class.getName(), parameter.getType());
+        assertEquals("defaultValue", parameter.getDefaultValue());
+        assertFalse(parameter.isRequired());
+        assertEquals("ExportedName", parameter.getGetterSetterSuffix());
+        assertFalse(parameter.isBody());
+        assertTrue(parameter.isRequest());
+
+        parameter = new TemplateParameter("name", "exportedName", ModelBody.class.getName(), "defaultValue", false);
+        assertEquals("name", parameter.getName());
+        assertEquals("exportedName", parameter.getExportedName());
+        assertEquals(ModelBody.class.getName(), parameter.getType());
+        assertEquals("defaultValue", parameter.getDefaultValue());
+        assertFalse(parameter.isRequired());
+        assertEquals("ExportedName", parameter.getGetterSetterSuffix());
+        assertTrue(parameter.isBody());
+        assertFalse(parameter.isRequest());
+    }
+
+    /**
+     * Tests {@link TemplateParameter#setDocumentation(String)}.
+     */
+    @Test
+    public void testSetDocumentation() {
+        TemplateParameter parameter = new TemplateParameter("name", "exportedName", "type", "defaultValue", true);
+        parameter.setDocumentation("docs");
+        assertEquals("docs", parameter.getDocumentation());
+    }
+
+    /**
+     * Tests {@link TemplateParameter#toString()}.
+     */
+    @Test
+    public void testToString() {
+        TemplateParameter parameter = new TemplateParameter("name", "exportedName", "type", "defaultValue", true);
+        assertEquals(
+                "TemplateParameter [name=name, exportedName=exportedName, "
+                        + "documentation=null, type=type, defaultValue=defaultValue, required=true]",
+                parameter.toString());
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateSuiteTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateSuiteTest.java
new file mode 100644
index 0000000..535ee98
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/model/TemplateSuiteTest.java
@@ -0,0 +1,113 @@
+/*
+ * $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 static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link TemplateSuite}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TemplateSuiteTest {
+
+    /**
+     * Test method for {@link TemplateSuite#TemplateSuite(java.lang.String, java.lang.String)}.
+     */
+    @Test
+    public void testTemplateSuiteCostructor1() {
+        TemplateSuite suite = new TemplateSuite("name", "docs");
+        assertEquals("name", suite.getName());
+        assertEquals("docs", suite.getDocumentation());
+        assertTrue(suite.getTemplateClasses().isEmpty());
+    }
+
+    /**
+     * Test method for {@link TemplateSuite#TemplateSuite(java.lang.String, java.lang.String, java.lang.Iterable)}.
+     */
+    @Test
+    public void testTemplateSuiteConstructor2() {
+        TemplateClass class1 = createMock(TemplateClass.class);
+        TemplateClass class2 = createMock(TemplateClass.class);
+        expect(class1.getName()).andReturn("class1");
+        expect(class2.getName()).andReturn("class2");
+
+        replay(class1, class2);
+        List<TemplateClass> classes = new ArrayList<TemplateClass>();
+        classes.add(class1);
+        classes.add(class2);
+        TemplateSuite suite = new TemplateSuite("name", "docs", classes);
+        assertEquals("name", suite.getName());
+        assertEquals("docs", suite.getDocumentation());
+        Iterator<TemplateClass> clazzes = suite.getTemplateClasses().iterator();
+        assertSame(class1, clazzes.next());
+        assertSame(class2, clazzes.next());
+        assertFalse(clazzes.hasNext());
+        assertSame(class1, suite.getTemplateClassByName("class1"));
+        assertSame(class2, suite.getTemplateClassByName("class2"));
+        verify(class1, class2);
+    }
+
+    /**
+     * Test method for {@link TemplateSuite#addTemplateClass(org.apache.tiles.autotag.model.TemplateClass)}.
+     */
+    @Test
+    public void testAddTemplateClass() {
+        TemplateClass class1 = createMock(TemplateClass.class);
+        TemplateClass class2 = createMock(TemplateClass.class);
+        expect(class1.getName()).andReturn("class1");
+        expect(class2.getName()).andReturn("class2");
+
+        replay(class1, class2);
+        List<TemplateClass> classes = new ArrayList<TemplateClass>();
+        classes.add(class1);
+        classes.add(class2);
+        TemplateSuite suite = new TemplateSuite("name", "docs");
+        assertEquals("name", suite.getName());
+        assertEquals("docs", suite.getDocumentation());
+        assertTrue(suite.getTemplateClasses().isEmpty());
+        suite.addTemplateClass(class1);
+        suite.addTemplateClass(class2);
+        Iterator<TemplateClass> clazzes = suite.getTemplateClasses().iterator();
+        assertSame(class1, clazzes.next());
+        assertSame(class2, clazzes.next());
+        assertFalse(clazzes.hasNext());
+        assertSame(class1, suite.getTemplateClassByName("class1"));
+        assertSame(class2, suite.getTemplateClassByName("class2"));
+        verify(class1, class2);
+    }
+
+    /**
+     * Test method for {@link TemplateSuite#toString()}.
+     */
+    @Test
+    public void testToString() {
+        TemplateSuite suite = new TemplateSuite("name", "docs");
+        assertEquals("TemplateSuite [name=name, documentation=docs, templateClasses={}]", suite.toString());
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/tool/StringToolTest.java b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/tool/StringToolTest.java
new file mode 100644
index 0000000..7fd6023
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-core/src/test/java/org/apache/tiles/autotag/tool/StringToolTest.java
@@ -0,0 +1,82 @@
+/*
+ * $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.tool;
+
+import static org.junit.Assert.*;
+
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link StringTool}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StringToolTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.tool.StringTool#splitOnNewlines(java.lang.String)}.
+     */
+    @Test
+    public void testSplitOnNewlines() {
+        StringTool tool = new StringTool();
+        List<String> splitted = tool.splitOnNewlines("time\nto\nsplit");
+        assertEquals(3, splitted.size());
+        assertEquals("time", splitted.get(0));
+        assertEquals("to", splitted.get(1));
+        assertEquals("split", splitted.get(2));
+        splitted = tool.splitOnNewlines(null);
+        assertTrue(splitted.isEmpty());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.tool.StringTool#capitalizeFirstLetter(java.lang.String)}.
+     */
+    @Test
+    public void testCapitalizeFirstLetter() {
+        StringTool tool = new StringTool();
+        assertEquals("Whatever", tool.capitalizeFirstLetter("whatever"));
+    }
+
+    /**
+     * Test method for {@link StringTool#getDefaultValue(java.lang.String, java.lang.String)}.
+     */
+    @Test
+    public void testGetDefaultValue() {
+        StringTool tool = new StringTool();
+        assertEquals("0", tool.getDefaultValue("byte", null));
+        assertEquals("1", tool.getDefaultValue("byte", "1"));
+        assertEquals("null", tool.getDefaultValue("Whatever", null));
+        assertEquals("thatsit", tool.getDefaultValue("Whatever", "thatsit"));
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.tool.StringTool#getClassToCast(java.lang.String)}.
+     */
+    @Test
+    public void testGetClassToCast() {
+        StringTool tool = new StringTool();
+        assertEquals(Byte.class.getName(), tool.getClassToCast("byte"));
+        assertEquals("Whatever", tool.getClassToCast("Whatever"));
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-freemarker/pom.xml b/tiles-autotag/tiles-autotag-freemarker/pom.xml
new file mode 100644
index 0000000..481c718
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>tiles-autotag</artifactId>
+    <groupId>org.apache.tiles</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.tiles</groupId>
+  <artifactId>tiles-autotag-freemarker</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Autotag - Freemarker support</name>
+  <description>Generates code for Freemarker support.</description>
+  <dependencies>
+  	<dependency>
+  		<groupId>org.apache.tiles</groupId>
+  		<artifactId>tiles-autotag-core</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>junit</groupId>
+  		<artifactId>junit</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>commons-io</groupId>
+  		<artifactId>commons-io</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.easymock</groupId>
+  		<artifactId>easymock</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  </dependencies>
+</project>
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/FMModelGenerator.java b/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/FMModelGenerator.java
new file mode 100644
index 0000000..ad08ea7
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/FMModelGenerator.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.autotag.freemarker;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.tiles.autotag.generate.AbstractTemplateClassGenerator;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * Generates a single Freemarker directive model, given a template class.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FMModelGenerator extends AbstractTemplateClassGenerator {
+
+    /**
+     * Constructor.
+     *
+     * @param velocityEngine The Velocity engine.
+     */
+    public FMModelGenerator(VelocityEngine velocityEngine) {
+        super(velocityEngine);
+    }
+
+    @Override
+    protected String getDirectoryName(File directory, String packageName,
+            TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters,
+            String runtimeClass) {
+        return packageName.replaceAll("\\.", "/");
+    }
+
+    @Override
+    protected String getFilename(File directory, String packageName,
+            TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters,
+            String runtimeClass) {
+        return clazz.getTagClassPrefix() + "FMModel.java";
+    }
+
+    @Override
+    protected String getTemplatePath(File directory, String packageName,
+            TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters,
+            String runtimeClass) {
+        return "/org/apache/tiles/autotag/freemarker/fmModel.vm";
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/FMModelRepositoryGenerator.java b/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/FMModelRepositoryGenerator.java
new file mode 100644
index 0000000..23bc4fc
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/FMModelRepositoryGenerator.java
@@ -0,0 +1,65 @@
+/*
+ * $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.freemarker;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.tiles.autotag.generate.AbstractTemplateSuiteGenerator;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * Generates the model repository, given the template suite.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FMModelRepositoryGenerator extends AbstractTemplateSuiteGenerator {
+
+    /**
+     * Constructor.
+     *
+     * @param velocityEngine The Velocity engine.
+     */
+    public FMModelRepositoryGenerator(VelocityEngine velocityEngine) {
+        super(velocityEngine);
+    }
+
+    @Override
+    protected String getTemplatePath(File directory, String packageName,
+            TemplateSuite suite, Map<String, String> parameters) {
+        return "/org/apache/tiles/autotag/freemarker/repository.vm";
+    }
+
+    @Override
+    protected String getFilename(File directory, String packageName,
+            TemplateSuite suite, Map<String, String> parameters) {
+        String name = suite.getName();
+        return name.substring(0, 1).toUpperCase() + name.substring(1) + "FMModelRepository.java";
+    }
+
+    @Override
+    protected String getDirectoryName(File directory, String packageName,
+            TemplateSuite suite, Map<String, String> parameters) {
+        return packageName.replaceAll("\\.", "/");
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/FMTemplateGeneratorFactory.java b/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/FMTemplateGeneratorFactory.java
new file mode 100644
index 0000000..00bf96a
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/FMTemplateGeneratorFactory.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.autotag.freemarker;
+
+import java.io.File;
+
+import org.apache.tiles.autotag.generate.TemplateGenerator;
+import org.apache.tiles.autotag.generate.TemplateGeneratorBuilder;
+import org.apache.tiles.autotag.generate.TemplateGeneratorFactory;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * Creates a template generator that generates code for Freemarker.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FMTemplateGeneratorFactory implements TemplateGeneratorFactory {
+
+    /**
+     * Location of the file.
+     *
+     * @parameter expression="${project.build.directory}/autotag-jsp-classes"
+     * @required
+     */
+    private File classesOutputDirectory;
+
+    /**
+     * The Velocity engine.
+     */
+    private VelocityEngine velocityEngine;
+
+    /**
+     * The template generator builder.
+     */
+    private TemplateGeneratorBuilder templateGeneratorBuilder;
+
+    /**
+     * Constructor.
+     *
+     * @param classesOutputDirectory Directory where code will be placed.
+     * @param velocityEngine The Velocity engine.
+     * @param templateGeneratorBuilder The template generator builder.
+     */
+    public FMTemplateGeneratorFactory(File classesOutputDirectory,
+            VelocityEngine velocityEngine, TemplateGeneratorBuilder templateGeneratorBuilder) {
+        this.classesOutputDirectory = classesOutputDirectory;
+        this.velocityEngine = velocityEngine;
+        this.templateGeneratorBuilder = templateGeneratorBuilder;
+    }
+
+    @Override
+    public TemplateGenerator createTemplateGenerator() {
+        return templateGeneratorBuilder
+                .setClassesOutputDirectory(classesOutputDirectory)
+                .addClassesTemplateSuiteGenerator(
+                        new FMModelRepositoryGenerator(velocityEngine))
+                .addClassesTemplateClassGenerator(
+                        new FMModelGenerator(velocityEngine)).build();
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/package-info.java b/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/package-info.java
new file mode 100644
index 0000000..c230fed
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/main/java/org/apache/tiles/autotag/freemarker/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Autotag support for Freemarker.
+ */
+package org.apache.tiles.autotag.freemarker;
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/main/resources/org/apache/tiles/autotag/freemarker/fmModel.vm b/tiles-autotag/tiles-autotag-freemarker/src/main/resources/org/apache/tiles/autotag/freemarker/fmModel.vm
new file mode 100644
index 0000000..ef8bdcf
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/main/resources/org/apache/tiles/autotag/freemarker/fmModel.vm
@@ -0,0 +1,83 @@
+#*
+ * $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.
+ *#/*
+ * This file was automatically generated by Apache Tiles Autotag.
+ */
+package ${packageName};
+
+import java.io.IOException;
+import java.util.Map;
+
+#if(${clazz.hasBody()})
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+#end
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+
+import freemarker.core.Environment;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+#foreach($line in $stringTool.splitOnNewlines(${clazz.documentation}))
+ * ${line}
+#end
+ */
+public class ${clazz.tagClassPrefix}FMModel implements TemplateDirectiveModel {
+
+    /**
+     * The template model.
+     */
+    private ${clazz.name} model;
+
+    /**
+     * Constructor.
+     *
+     * @param model
+     *            The template model.
+     */
+    public ${clazz.tagClassPrefix}FMModel(${clazz.name} model) {
+        this.model = model;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void execute(Environment env, @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+        AutotagRuntime runtime = new ${runtimeClass}();
+        if (runtime instanceof TemplateDirectiveModel) {
+            ((TemplateDirectiveModel) runtime).execute(env, params, loopVars, body);
+        }
+        Request request = runtime.createRequest();
+#if(${clazz.hasBody()})
+        ModelBody modelBody = runtime.createModelBody();
+#end
+        model.execute(
+#foreach($parameter in ${clazz.parameters})
+            ($stringTool.getClassToCast(${parameter.type})) runtime.getParameter("${parameter.exportedName}", $stringTool.getDefaultValue(${parameter.type}, ${parameter.defaultValue})),
+#end
+                request#if(${clazz.hasBody()}), modelBody#end
+
+        );
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/main/resources/org/apache/tiles/autotag/freemarker/repository.vm b/tiles-autotag/tiles-autotag-freemarker/src/main/resources/org/apache/tiles/autotag/freemarker/repository.vm
new file mode 100644
index 0000000..20724c6
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/main/resources/org/apache/tiles/autotag/freemarker/repository.vm
@@ -0,0 +1,58 @@
+#*
+ * $Id: tiles-jsp.tld 836180 2009-11-14 14:00:02Z apetrelli $
+ *
+ * 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.
+ *#/*
+ * This file was automatically generated by Apache Tiles Autotag.
+ */
+package ${packageName};
+
+/**
+#foreach($line in $stringTool.splitOnNewlines(${suite.documentation}))
+ * $line
+#end
+ */
+public class $stringTool.capitalizeFirstLetter(${suite.name})FMModelRepository {
+
+#foreach($clazz in ${suite.getTemplateClasses()})
+    /**
+     * The "${clazz.tagName}" directive.
+     */
+    private ${clazz.tagClassPrefix}FMModel ${clazz.tagName};
+
+#end
+    /**
+     * Constructor.
+     */
+    public TilesFMModelRepository() {
+#foreach($clazz in ${suite.getTemplateClasses()})
+        ${clazz.tagName} = new ${clazz.tagClassPrefix}FMModel(new ${clazz.name}());
+#end
+    }
+#foreach($clazz in ${suite.getTemplateClasses()})
+
+    /**
+     * Returns the "${clazz.tagName}" directive.
+     *
+     * @return The "${clazz.tagName}" directive.
+     */
+    public ${clazz.tagClassPrefix}FMModel get$stringTool.capitalizeFirstLetter(${clazz.tagName})() {
+        return ${clazz.tagName};
+    }
+#end
+}
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/site/site.xml b/tiles-autotag/tiles-autotag-freemarker/src/site/site.xml
new file mode 100644
index 0000000..0a7d00d
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Tiles Autotags">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Tiles Autotag"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/test/java/org/apache/tiles/autotag/freemarker/FMModelGeneratorTest.java b/tiles-autotag/tiles-autotag-freemarker/src/test/java/org/apache/tiles/autotag/freemarker/FMModelGeneratorTest.java
new file mode 100644
index 0000000..6c41678
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/test/java/org/apache/tiles/autotag/freemarker/FMModelGeneratorTest.java
@@ -0,0 +1,142 @@
+/*
+ * $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.freemarker;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateMethod;
+import org.apache.tiles.autotag.model.TemplateParameter;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.tiles.request.Request;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.Test;
+
+/**
+ * Tests {@link TagClassGenerator}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FMModelGeneratorTest {
+
+    /**
+     * Test method for {@link TagClassGenerator#generate(File, String, TemplateSuite, TemplateClass, java.util.Map)}.
+     * @throws Exception If something goes wrong.
+     */
+    @Test
+    public void testGenerate() throws Exception {
+        Properties props = new Properties();
+        InputStream propsStream = getClass().getResourceAsStream("/org/apache/tiles/autotag/velocity.properties");
+        props.load(propsStream);
+        propsStream.close();
+        VelocityEngine velocityEngine = new VelocityEngine(props);
+
+        FMModelGenerator generator = new FMModelGenerator(velocityEngine);
+        File file = File.createTempFile("autotag", null);
+        file.delete();
+        file.mkdir();
+        file.deleteOnExit();
+        TemplateSuite suite = new TemplateSuite("tldtest", "Test for TLD docs.");
+
+        List<TemplateParameter> params = new ArrayList<TemplateParameter>();
+        TemplateParameter param = new TemplateParameter("one", "one", "java.lang.String", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "int", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "boolean", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        param = new TemplateParameter("modelBody", "modelBody", ModelBody.class.getName(), null, false);
+        param.setDocumentation("The body.");
+        params.add(param);
+        TemplateMethod executeMethod = new TemplateMethod("execute", params);
+
+        TemplateClass clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffTemplate",
+                "doStuff", "DoStuff", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuff class.");
+
+        generator.generate(file, "org.apache.tiles.autotag.freemarker.test", suite, clazz, null,
+                           "org.apache.tiles.autotag.freemarker.test.Runtime");
+
+        InputStream expected = getClass()
+                .getResourceAsStream(
+                        "/org/apache/tiles/autotag/freemarker/test/DoStuffFMModel.javat");
+        File effectiveFile = new File(file, "/org/apache/tiles/autotag/freemarker/test/DoStuffFMModel.java");
+        assertTrue(effectiveFile.exists());
+        InputStream effective = new FileInputStream(effectiveFile);
+        assertTrue(IOUtils.contentEquals(effective, expected));
+        effective.close();
+        expected.close();
+
+        suite.addTemplateClass(clazz);
+        params = new ArrayList<TemplateParameter>();
+        param = new TemplateParameter("one", "one", "java.lang.Double", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "float", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "java.util.Date", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        executeMethod = new TemplateMethod("execute", params);
+
+        clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffNoBodyTemplate",
+                "doStuffNoBody", "DoStuffNoBody", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuffNoBody class.");
+
+        suite.addTemplateClass(clazz);
+
+        generator.generate(file, "org.apache.tiles.autotag.freemarker.test", suite, clazz, null,
+                           "org.apache.tiles.autotag.freemarker.test.Runtime");
+
+        expected = getClass()
+                .getResourceAsStream(
+                        "/org/apache/tiles/autotag/freemarker/test/DoStuffNoBodyFMModel.javat");
+        effectiveFile = new File(file, "/org/apache/tiles/autotag/freemarker/test/DoStuffNoBodyFMModel.java");
+        assertTrue(effectiveFile.exists());
+        effective = new FileInputStream(effectiveFile);
+        assertTrue(IOUtils.contentEquals(effective, expected));
+        effective.close();
+        expected.close();
+
+        FileUtils.deleteDirectory(file);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/test/java/org/apache/tiles/autotag/freemarker/FMModelRepositoryGeneratorTest.java b/tiles-autotag/tiles-autotag-freemarker/src/test/java/org/apache/tiles/autotag/freemarker/FMModelRepositoryGeneratorTest.java
new file mode 100644
index 0000000..20369b1
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/test/java/org/apache/tiles/autotag/freemarker/FMModelRepositoryGeneratorTest.java
@@ -0,0 +1,128 @@
+/*
+ * $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.freemarker;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateMethod;
+import org.apache.tiles.autotag.model.TemplateParameter;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.tiles.request.Request;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.Test;
+
+/**
+ * Tests {@link TLDGenerator}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FMModelRepositoryGeneratorTest {
+
+    /**
+     * Test method for {@link FMModelRepositoryGenerator#generate(File, String, TemplateSuite, java.util.Map)}.
+     * @throws Exception If something goes wrong.
+     */
+    @Test
+    public void testGenerate() throws Exception {
+        Properties props = new Properties();
+        InputStream propsStream = getClass().getResourceAsStream("/org/apache/tiles/autotag/velocity.properties");
+        props.load(propsStream);
+        propsStream.close();
+        VelocityEngine velocityEngine = new VelocityEngine(props);
+
+        FMModelRepositoryGenerator generator = new FMModelRepositoryGenerator(velocityEngine);
+        File file = File.createTempFile("autotag", null);
+        file.delete();
+        file.mkdir();
+        file.deleteOnExit();
+        TemplateSuite suite = new TemplateSuite("tldtest", "Test for TLD docs.");
+
+        List<TemplateParameter> params = new ArrayList<TemplateParameter>();
+        TemplateParameter param = new TemplateParameter("one", "one", "java.lang.String", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "int", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "long", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        param = new TemplateParameter("modelBody", "modelBody", ModelBody.class.getName(), null, false);
+        param.setDocumentation("The body.");
+        params.add(param);
+        TemplateMethod executeMethod = new TemplateMethod("execute", params);
+
+        TemplateClass clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffTemplate",
+                "doStuff", "DoStuff", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuff class");
+
+        suite.addTemplateClass(clazz);
+        params = new ArrayList<TemplateParameter>();
+        param = new TemplateParameter("one", "one", "java.lang.Double", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "float", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "java.util.Date", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        executeMethod = new TemplateMethod("execute", params);
+
+        clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffNoBodyTemplate",
+                "doStuffNoBody", "DoStuffNoBody", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuffNoBody class");
+
+        suite.addTemplateClass(clazz);
+
+        generator.generate(file, "org.apache.tiles.autotag.freemarker.test", suite, null);
+
+        InputStream expected = getClass()
+                .getResourceAsStream(
+                        "/org/apache/tiles/autotag/freemarker/test/TldtestFMModelRepository.javat");
+        File effectiveFile = new File(file, "/org/apache/tiles/autotag/freemarker/test/TldtestFMModelRepository.java");
+        assertTrue(effectiveFile.exists());
+        InputStream effective = new FileInputStream(effectiveFile);
+        assertTrue(IOUtils.contentEquals(effective, expected));
+        effective.close();
+        expected.close();
+
+        FileUtils.deleteDirectory(file);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/test/java/org/apache/tiles/autotag/freemarker/FMTemplateGeneratorFactoryTest.java b/tiles-autotag/tiles-autotag-freemarker/src/test/java/org/apache/tiles/autotag/freemarker/FMTemplateGeneratorFactoryTest.java
new file mode 100644
index 0000000..ea6b1ce
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/test/java/org/apache/tiles/autotag/freemarker/FMTemplateGeneratorFactoryTest.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.freemarker;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.File;
+
+import org.apache.tiles.autotag.generate.TemplateGenerator;
+import org.apache.tiles.autotag.generate.TemplateGeneratorBuilder;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.Test;
+
+/**
+ * Tests {@link FMTemplateGeneratorFactory}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FMTemplateGeneratorFactoryTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.autotag.freemarker.FMTemplateGeneratorFactory#createTemplateGenerator()}.
+     */
+    @Test
+    public void testCreateTemplateGenerator() {
+        File classesOutputDirectory = createMock(File.class);
+        VelocityEngine velocityEngine = createMock(VelocityEngine.class);
+        TemplateGeneratorBuilder builder = createMock(TemplateGeneratorBuilder.class);
+        TemplateGenerator generator = createMock(TemplateGenerator.class);
+
+        expect(builder.setClassesOutputDirectory(classesOutputDirectory)).andReturn(builder);
+        expect(builder.addClassesTemplateSuiteGenerator(isA(FMModelRepositoryGenerator.class))).andReturn(builder);
+        expect(builder.addClassesTemplateClassGenerator(isA(FMModelGenerator.class))).andReturn(builder);
+        expect(builder.build()).andReturn(generator);
+
+        replay(classesOutputDirectory, velocityEngine, builder, generator);
+        FMTemplateGeneratorFactory factory = new FMTemplateGeneratorFactory(
+                classesOutputDirectory, velocityEngine, builder);
+        assertSame(generator, factory.createTemplateGenerator());
+        verify(classesOutputDirectory, velocityEngine, builder, generator);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/test/resources/org/apache/tiles/autotag/freemarker/test/DoStuffFMModel.javat b/tiles-autotag/tiles-autotag-freemarker/src/test/resources/org/apache/tiles/autotag/freemarker/test/DoStuffFMModel.javat
new file mode 100644
index 0000000..73b7f8b
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/test/resources/org/apache/tiles/autotag/freemarker/test/DoStuffFMModel.javat
@@ -0,0 +1,57 @@
+/*
+ * This file was automatically generated by Apache Tiles Autotag.
+ */
+package org.apache.tiles.autotag.freemarker.test;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+
+import freemarker.core.Environment;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Documentation of the DoStuff class.
+ */
+public class DoStuffFMModel implements TemplateDirectiveModel {
+
+    /**
+     * The template model.
+     */
+    private org.apache.tiles.autotag.template.DoStuffTemplate model;
+
+    /**
+     * Constructor.
+     *
+     * @param model
+     *            The template model.
+     */
+    public DoStuffFMModel(org.apache.tiles.autotag.template.DoStuffTemplate model) {
+        this.model = model;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void execute(Environment env, @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+        AutotagRuntime runtime = new org.apache.tiles.autotag.freemarker.test.Runtime();
+        if (runtime instanceof TemplateDirectiveModel) {
+            ((TemplateDirectiveModel) runtime).execute(env, params, loopVars, body);
+        }
+        Request request = runtime.createRequest();
+        ModelBody modelBody = runtime.createModelBody();
+        model.execute(
+            (java.lang.String) runtime.getParameter("one", null),
+            (java.lang.Integer) runtime.getParameter("two", 0),
+            (java.lang.Boolean) runtime.getParameter("three", false),
+                request, modelBody
+        );
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/test/resources/org/apache/tiles/autotag/freemarker/test/DoStuffNoBodyFMModel.javat b/tiles-autotag/tiles-autotag-freemarker/src/test/resources/org/apache/tiles/autotag/freemarker/test/DoStuffNoBodyFMModel.javat
new file mode 100644
index 0000000..076eaab
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/test/resources/org/apache/tiles/autotag/freemarker/test/DoStuffNoBodyFMModel.javat
@@ -0,0 +1,55 @@
+/*
+ * This file was automatically generated by Apache Tiles Autotag.
+ */
+package org.apache.tiles.autotag.freemarker.test;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+
+import freemarker.core.Environment;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModel;
+
+/**
+ * Documentation of the DoStuffNoBody class.
+ */
+public class DoStuffNoBodyFMModel implements TemplateDirectiveModel {
+
+    /**
+     * The template model.
+     */
+    private org.apache.tiles.autotag.template.DoStuffNoBodyTemplate model;
+
+    /**
+     * Constructor.
+     *
+     * @param model
+     *            The template model.
+     */
+    public DoStuffNoBodyFMModel(org.apache.tiles.autotag.template.DoStuffNoBodyTemplate model) {
+        this.model = model;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void execute(Environment env, @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars,
+            TemplateDirectiveBody body) throws TemplateException, IOException {
+        AutotagRuntime runtime = new org.apache.tiles.autotag.freemarker.test.Runtime();
+        if (runtime instanceof TemplateDirectiveModel) {
+            ((TemplateDirectiveModel) runtime).execute(env, params, loopVars, body);
+        }
+        Request request = runtime.createRequest();
+        model.execute(
+            (java.lang.Double) runtime.getParameter("one", null),
+            (java.lang.Float) runtime.getParameter("two", 0.0f),
+            (java.util.Date) runtime.getParameter("three", null),
+                request
+        );
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-freemarker/src/test/resources/org/apache/tiles/autotag/freemarker/test/TldtestFMModelRepository.javat b/tiles-autotag/tiles-autotag-freemarker/src/test/resources/org/apache/tiles/autotag/freemarker/test/TldtestFMModelRepository.javat
new file mode 100644
index 0000000..4fe08ee
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-freemarker/src/test/resources/org/apache/tiles/autotag/freemarker/test/TldtestFMModelRepository.javat
@@ -0,0 +1,46 @@
+/*
+ * This file was automatically generated by Apache Tiles Autotag.
+ */
+package org.apache.tiles.autotag.freemarker.test;
+
+/**
+ * Test for TLD docs.
+ */
+public class TldtestFMModelRepository {
+
+    /**
+     * The "doStuff" directive.
+     */
+    private DoStuffFMModel doStuff;
+
+    /**
+     * The "doStuffNoBody" directive.
+     */
+    private DoStuffNoBodyFMModel doStuffNoBody;
+
+    /**
+     * Constructor.
+     */
+    public TilesFMModelRepository() {
+        doStuff = new DoStuffFMModel(new org.apache.tiles.autotag.template.DoStuffTemplate());
+        doStuffNoBody = new DoStuffNoBodyFMModel(new org.apache.tiles.autotag.template.DoStuffNoBodyTemplate());
+    }
+
+    /**
+     * Returns the "doStuff" directive.
+     *
+     * @return The "doStuff" directive.
+     */
+    public DoStuffFMModel getDoStuff() {
+        return doStuff;
+    }
+
+    /**
+     * Returns the "doStuffNoBody" directive.
+     *
+     * @return The "doStuffNoBody" directive.
+     */
+    public DoStuffNoBodyFMModel getDoStuffNoBody() {
+        return doStuffNoBody;
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-jsp/pom.xml b/tiles-autotag/tiles-autotag-jsp/pom.xml
new file mode 100644
index 0000000..f6e95a9
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>tiles-autotag</artifactId>
+    <groupId>org.apache.tiles</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.tiles</groupId>
+  <artifactId>tiles-autotag-jsp</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Tiles Autotag - JSP tags automatic generation</name>
+  <description>Generates JSP tags automatically from templates.</description>
+  <dependencies>
+    <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>commons-io</groupId>
+        <artifactId>commons-io</artifactId>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.tiles</groupId>
+    	<artifactId>tiles-autotag-core</artifactId>
+    </dependency>
+    <dependency>
+    	<groupId>org.easymock</groupId>
+    	<artifactId>easymock</artifactId>
+    	<scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/JspTemplateGeneratorFactory.java b/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/JspTemplateGeneratorFactory.java
new file mode 100644
index 0000000..166c376
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/JspTemplateGeneratorFactory.java
@@ -0,0 +1,85 @@
+/*
+ * $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.jsp;
+
+import java.io.File;
+
+import org.apache.tiles.autotag.generate.TemplateGenerator;
+import org.apache.tiles.autotag.generate.TemplateGeneratorBuilder;
+import org.apache.tiles.autotag.generate.TemplateGeneratorFactory;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * Creates a template generator to build JSP code around template models.
+ *
+ * @version $Rev$ $Date$
+ */
+public class JspTemplateGeneratorFactory implements TemplateGeneratorFactory {
+
+    /**
+     * Location of the file.
+     */
+    private File classesOutputDirectory;
+
+    /**
+     * Location of the file.
+     */
+    private File resourcesOutputDirectory;
+
+    /**
+     * The Velocity engine.
+     */
+    private VelocityEngine velocityEngine;
+
+    /**
+     * The template generator builder.
+     */
+    private TemplateGeneratorBuilder templateGeneratorBuilder;
+
+    /**
+     * Constructor.
+     *
+     * @param classesOutputDirectory The directory where classes will be generated.
+     * @param resourcesOutputDirectory The directory where the TLD file will be generated.
+     * @param velocityEngine The Velocity engine.
+     * @param templateGeneratorBuilder The template generator builder.
+     */
+    public JspTemplateGeneratorFactory(File classesOutputDirectory,
+            File resourcesOutputDirectory, VelocityEngine velocityEngine,
+            TemplateGeneratorBuilder templateGeneratorBuilder) {
+        this.classesOutputDirectory = classesOutputDirectory;
+        this.resourcesOutputDirectory = resourcesOutputDirectory;
+        this.velocityEngine = velocityEngine;
+        this.templateGeneratorBuilder = templateGeneratorBuilder;
+    }
+
+    @Override
+    public TemplateGenerator createTemplateGenerator() {
+        return templateGeneratorBuilder
+                .setClassesOutputDirectory(classesOutputDirectory)
+                .setResourcesOutputDirectory(resourcesOutputDirectory)
+                .addResourcesTemplateSuiteGenerator(
+                        new TLDGenerator(velocityEngine))
+                .addClassesTemplateClassGenerator(
+                        new TagClassGenerator(velocityEngine)).build();
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/TLDGenerator.java b/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/TLDGenerator.java
new file mode 100644
index 0000000..8ca3fad
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/TLDGenerator.java
@@ -0,0 +1,64 @@
+/*
+ * $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.jsp;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.tiles.autotag.generate.AbstractTemplateSuiteGenerator;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * Generates the TLD file, using a template suite.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TLDGenerator extends AbstractTemplateSuiteGenerator {
+
+    /**
+     * Constructor.
+     *
+     * @param velocityEngine The Velocity engine.
+     */
+    public TLDGenerator(VelocityEngine velocityEngine) {
+        super(velocityEngine);
+    }
+
+    @Override
+    protected String getTemplatePath(File directory, String packageName,
+            TemplateSuite suite, Map<String, String> parameters) {
+        return "/org/apache/tiles/autotag/jsp/tld.vm";
+    }
+
+    @Override
+    protected String getFilename(File directory, String packageName,
+            TemplateSuite suite, Map<String, String> parameters) {
+        return suite.getName() + "-jsp.tld";
+    }
+
+    @Override
+    protected String getDirectoryName(File directory, String packageName,
+            TemplateSuite suite, Map<String, String> parameters) {
+        return "META-INF/tld/";
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/TagClassGenerator.java b/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/TagClassGenerator.java
new file mode 100644
index 0000000..17b02bc
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/TagClassGenerator.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.autotag.jsp;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.tiles.autotag.generate.AbstractTemplateClassGenerator;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * Generates a tag class using a template class.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TagClassGenerator extends AbstractTemplateClassGenerator {
+
+    /**
+     * Constructor.
+     *
+     * @param velocityEngine The Velocity engine.
+     */
+    public TagClassGenerator(VelocityEngine velocityEngine) {
+        super(velocityEngine);
+    }
+
+    @Override
+    protected String getDirectoryName(File directory, String packageName,
+            TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters,
+            String runtimeClass) {
+        return packageName.replaceAll("\\.", "/");
+    }
+
+    @Override
+    protected String getFilename(File directory, String packageName,
+            TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters,
+            String runtimeClass) {
+        return clazz.getTagClassPrefix() + "Tag.java";
+    }
+
+    @Override
+    protected String getTemplatePath(File directory, String packageName,
+            TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters,
+            String runtimeClass) {
+        return "/org/apache/tiles/autotag/jsp/bodyTag.vm";
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/package-info.java b/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/package-info.java
new file mode 100644
index 0000000..e75b5c0
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/main/java/org/apache/tiles/autotag/jsp/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Autotag support for JavaServer Pages.
+ */
+package org.apache.tiles.autotag.jsp;
diff --git a/tiles-autotag/tiles-autotag-jsp/src/main/resources/org/apache/tiles/autotag/jsp/bodyTag.vm b/tiles-autotag/tiles-autotag-jsp/src/main/resources/org/apache/tiles/autotag/jsp/bodyTag.vm
new file mode 100644
index 0000000..b7d9ea1
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/main/resources/org/apache/tiles/autotag/jsp/bodyTag.vm
@@ -0,0 +1,106 @@
+#*
+ * $Id: tiles-jsp.tld 836180 2009-11-14 14:00:02Z apetrelli $
+ *
+ * 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.
+ *#/*
+ * This file was automatically generated by Apache Tiles Autotag.
+ */
+package ${packageName};
+
+import java.io.IOException;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.SimpleTagSupport;
+
+#if(${clazz.hasBody()})
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+#end
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+
+/**
+#foreach($line in $stringTool.splitOnNewlines(${clazz.documentation}))
+ * ${line}
+#end
+ */
+public class ${clazz.tagClassPrefix}Tag extends SimpleTagSupport {
+
+    /**
+     * The template model.
+     */
+    private ${clazz.name} model = new ${clazz.name}();
+
+#foreach($parameter in ${clazz.parameters})
+    /**
+#foreach($line in $stringTool.splitOnNewlines(${parameter.documentation}))
+     * ${line}
+#end
+     */
+    private ${parameter.type} ${parameter.name};
+
+#end
+#foreach($parameter in ${clazz.parameters})
+    /**
+     * Getter for ${parameter.exportedName} property.
+     *
+     * @return
+#foreach($line in $stringTool.splitOnNewlines(${parameter.documentation}))
+     * ${line}
+#end
+     */
+    public ${parameter.type} #if(${parameter.type} == 'boolean')is#{else}get#end${parameter.getterSetterSuffix}() {
+        return ${parameter.name};
+    }
+
+    /**
+     * Setter for ${parameter.exportedName} property.
+     *
+     * @param ${parameter.name}
+#foreach($line in $stringTool.splitOnNewlines(${parameter.documentation}))
+     * ${line}
+#end
+     */
+    public void set${parameter.getterSetterSuffix}(${parameter.type} ${parameter.name}) {
+        this.${parameter.name} = ${parameter.name};
+    }
+
+#end
+    /** {@inheritDoc} */
+    @Override
+    public void doTag() throws JspException, IOException {
+        AutotagRuntime runtime = new ${runtimeClass}();
+        if (runtime instanceof SimpleTagSupport) {
+            SimpleTagSupport tag = (SimpleTagSupport) runtime;
+            tag.setJspContext(getJspContext());
+            tag.setJspBody(getJspBody());
+            tag.setParent(getParent());
+            tag.doTag();
+        }
+        Request request = runtime.createRequest();        
+#if(${clazz.hasBody()})
+        ModelBody modelBody = runtime.createModelBody();
+#end
+        model.execute(
+#foreach($parameter in ${clazz.parameters})
+            ${parameter.name},
+#end
+            request#if(${clazz.hasBody()}), modelBody#end
+
+        );
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-jsp/src/main/resources/org/apache/tiles/autotag/jsp/tld.vm b/tiles-autotag/tiles-autotag-jsp/src/main/resources/org/apache/tiles/autotag/jsp/tld.vm
new file mode 100644
index 0000000..9d4c0e9
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/main/resources/org/apache/tiles/autotag/jsp/tld.vm
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+#*
+ * $Id: tiles-jsp.tld 836180 2009-11-14 14:00:02Z apetrelli $
+ *
+ * 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.
+ *#
+<!-- This file was automatically generated by Apache Tiles Autotag. -->
+<taglib
+  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
+  xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  version="2.1">
+   <description>
+   <![CDATA[
+#foreach($line in $stringTool.splitOnNewlines(${suite.documentation}))
+   $line
+#end
+   ]]>
+   </description>
+   <tlib-version>1.2</tlib-version>
+   <short-name>${suite.name}</short-name>
+   <uri>${parameters.taglibURI}</uri>
+#foreach($clazz in ${suite.getTemplateClasses()})
+   <tag>
+      <description>
+      <![CDATA[
+#foreach($line in $stringTool.splitOnNewlines(${clazz.documentation}))
+      $line
+#end
+      ]]>
+      </description>
+      <name>${clazz.tagName}</name>
+      <tag-class>${packageName}.${clazz.tagClassPrefix}Tag</tag-class>
+      <body-content>#if(${clazz.hasBody()})scriptless#{else}empty#end</body-content>
+#foreach($parameter in ${clazz.parameters})
+      <attribute>
+         <description>
+         <![CDATA[
+#foreach($line in $stringTool.splitOnNewlines(${parameter.documentation}))
+         $line
+#end
+         ]]>
+         </description>
+         <name>${parameter.exportedName}</name>
+         <required>${parameter.required}</required>
+         <rtexprvalue>true</rtexprvalue>
+         <type>${parameter.type}</type>
+      </attribute>
+#end
+   </tag>
+#end
+</taglib>
diff --git a/tiles-autotag/tiles-autotag-jsp/src/site/site.xml b/tiles-autotag/tiles-autotag-jsp/src/site/site.xml
new file mode 100644
index 0000000..0a7d00d
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Tiles Autotags">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Tiles Autotag"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-autotag/tiles-autotag-jsp/src/test/java/org/apache/tiles/autotag/jsp/JspTemplateGeneratorFactoryTest.java b/tiles-autotag/tiles-autotag-jsp/src/test/java/org/apache/tiles/autotag/jsp/JspTemplateGeneratorFactoryTest.java
new file mode 100644
index 0000000..8692c53
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/test/java/org/apache/tiles/autotag/jsp/JspTemplateGeneratorFactoryTest.java
@@ -0,0 +1,65 @@
+/*
+ * $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.jsp;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.File;
+
+import org.apache.tiles.autotag.generate.TemplateGenerator;
+import org.apache.tiles.autotag.generate.TemplateGeneratorBuilder;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.Test;
+
+/**
+ * Tests {@link JspTemplateGeneratorFactory}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class JspTemplateGeneratorFactoryTest {
+
+    /**
+     * Test method for {@link JspTemplateGeneratorFactory#createTemplateGenerator()}.
+     */
+    @Test
+    public void testCreateTemplateGenerator() {
+        File classesOutputDirectory = createMock(File.class);
+        File resourcesOutputDirectory = createMock(File.class);
+        VelocityEngine velocityEngine = createMock(VelocityEngine.class);
+        TemplateGeneratorBuilder builder = createMock(TemplateGeneratorBuilder.class);
+        TemplateGenerator generator = createMock(TemplateGenerator.class);
+
+        expect(builder.setClassesOutputDirectory(classesOutputDirectory)).andReturn(builder);
+        expect(builder.setResourcesOutputDirectory(resourcesOutputDirectory)).andReturn(builder);
+        expect(builder.addResourcesTemplateSuiteGenerator(isA(TLDGenerator.class))).andReturn(builder);
+        expect(builder.addClassesTemplateClassGenerator(isA(TagClassGenerator.class))).andReturn(builder);
+        expect(builder.build()).andReturn(generator);
+
+        replay(classesOutputDirectory, resourcesOutputDirectory, velocityEngine, builder, generator);
+        JspTemplateGeneratorFactory factory = new JspTemplateGeneratorFactory(
+                classesOutputDirectory, resourcesOutputDirectory,
+                velocityEngine, builder);
+        assertSame(generator, factory.createTemplateGenerator());
+        verify(classesOutputDirectory, resourcesOutputDirectory, velocityEngine, builder, generator);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-jsp/src/test/java/org/apache/tiles/autotag/jsp/TLDGeneratorTest.java b/tiles-autotag/tiles-autotag-jsp/src/test/java/org/apache/tiles/autotag/jsp/TLDGeneratorTest.java
new file mode 100644
index 0000000..2485c6f
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/test/java/org/apache/tiles/autotag/jsp/TLDGeneratorTest.java
@@ -0,0 +1,130 @@
+/*
+ * $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.jsp;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateMethod;
+import org.apache.tiles.autotag.model.TemplateParameter;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.tiles.request.Request;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.Test;
+
+/**
+ * Tests {@link TLDGenerator}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TLDGeneratorTest {
+
+    /**
+     * Test method for {@link TLDGenerator#generate(File, String, TemplateSuite, Map)}.
+     * @throws Exception If something goes wrong.
+     */
+    @Test
+    public void testGenerate() throws Exception {
+        Properties props = new Properties();
+        InputStream propsStream = getClass().getResourceAsStream("/org/apache/tiles/autotag/velocity.properties");
+        props.load(propsStream);
+        propsStream.close();
+        VelocityEngine velocityEngine = new VelocityEngine(props);
+
+        TLDGenerator generator = new TLDGenerator(velocityEngine);
+        File file = File.createTempFile("autotag", null);
+        file.delete();
+        file.mkdir();
+        file.deleteOnExit();
+        TemplateSuite suite = new TemplateSuite("tldtest", "Test for TLD docs.");
+        Map<String, String> parameters = new HashMap<String, String>();
+        parameters.put("taglibURI", "http://www.initrode.net/tags/test");
+
+        List<TemplateParameter> params = new ArrayList<TemplateParameter>();
+        TemplateParameter param = new TemplateParameter("one", "one", "java.lang.String", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "int", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "long", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        param = new TemplateParameter("modelBody", "modelBody", ModelBody.class.getName(), null, false);
+        param.setDocumentation("The body.");
+        params.add(param);
+        TemplateMethod executeMethod = new TemplateMethod("execute", params);
+
+        TemplateClass clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffTemplate",
+                "doStuff", "DoStuff", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuff class");
+
+        suite.addTemplateClass(clazz);
+        params = new ArrayList<TemplateParameter>();
+        param = new TemplateParameter("one", "one", "java.lang.Double", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "float", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "java.util.Date", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        executeMethod = new TemplateMethod("execute", params);
+
+        clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffNoBodyTemplate",
+                "doStuffNoBody", "DoStuffNoBody", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuffNoBody class");
+
+        suite.addTemplateClass(clazz);
+
+        generator.generate(file, "org.apache.tiles.autotag.jsp.test", suite, parameters);
+
+        InputStream expected = getClass().getResourceAsStream("/tldtest-jsp.tld");
+        File effectiveFile = new File(file, "META-INF/tld/tldtest-jsp.tld");
+        assertTrue(effectiveFile.exists());
+        InputStream effective = new FileInputStream(effectiveFile);
+        assertTrue(IOUtils.contentEquals(effective, expected));
+        effective.close();
+        expected.close();
+
+        FileUtils.deleteDirectory(file);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-jsp/src/test/java/org/apache/tiles/autotag/jsp/TagClassGeneratorTest.java b/tiles-autotag/tiles-autotag-jsp/src/test/java/org/apache/tiles/autotag/jsp/TagClassGeneratorTest.java
new file mode 100644
index 0000000..cbf2245
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/test/java/org/apache/tiles/autotag/jsp/TagClassGeneratorTest.java
@@ -0,0 +1,142 @@
+/*
+ * $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.jsp;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateMethod;
+import org.apache.tiles.autotag.model.TemplateParameter;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.tiles.request.Request;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.Test;
+
+/**
+ * Tests {@link TagClassGenerator}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class TagClassGeneratorTest {
+
+    /**
+     * Test method for {@link TagClassGenerator#generate(File, String, TemplateSuite, TemplateClass, Map)}.
+     * @throws Exception If something goes wrong.
+     */
+    @Test
+    public void testGenerate() throws Exception {
+        Properties props = new Properties();
+        InputStream propsStream = getClass().getResourceAsStream("/org/apache/tiles/autotag/velocity.properties");
+        props.load(propsStream);
+        propsStream.close();
+        VelocityEngine velocityEngine = new VelocityEngine(props);
+
+        TagClassGenerator generator = new TagClassGenerator(velocityEngine);
+        File file = File.createTempFile("autotag", null);
+        file.delete();
+        file.mkdir();
+        file.deleteOnExit();
+        TemplateSuite suite = new TemplateSuite("tldtest", "Test for TLD docs.");
+        Map<String, String> parameters = new HashMap<String, String>();
+        parameters.put("taglibURI", "http://www.initrode.net/tags/test");
+
+        List<TemplateParameter> params = new ArrayList<TemplateParameter>();
+        TemplateParameter param = new TemplateParameter("one", "one", "java.lang.String", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "int", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "boolean", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        param = new TemplateParameter("modelBody", "modelBody", ModelBody.class.getName(), null, false);
+        param.setDocumentation("The body.");
+        params.add(param);
+        TemplateMethod executeMethod = new TemplateMethod("execute", params);
+
+        TemplateClass clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffTemplate",
+                "doStuff", "DoStuff", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuff class.");
+
+        generator.generate(file, "org.apache.tiles.autotag.jsp.test", suite, clazz, parameters,
+                           "org.apache.tiles.autotag.jsp.test.Runtime");
+
+        InputStream expected = getClass().getResourceAsStream("/org/apache/tiles/autotag/jsp/test/DoStuffTag.java");
+        File effectiveFile = new File(file, "/org/apache/tiles/autotag/jsp/test/DoStuffTag.java");
+        assertTrue(effectiveFile.exists());
+        InputStream effective = new FileInputStream(effectiveFile);
+        assertTrue(IOUtils.contentEquals(effective, expected));
+        effective.close();
+        expected.close();
+
+        suite.addTemplateClass(clazz);
+        params = new ArrayList<TemplateParameter>();
+        param = new TemplateParameter("one", "one", "java.lang.Double", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "float", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "java.util.Date", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        executeMethod = new TemplateMethod("execute", params);
+
+        clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffNoBodyTemplate",
+                "doStuffNoBody", "DoStuffNoBody", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuffNoBody class.");
+
+        suite.addTemplateClass(clazz);
+
+        generator.generate(file, "org.apache.tiles.autotag.jsp.test", suite, clazz, parameters,
+                           "org.apache.tiles.autotag.jsp.test.Runtime");
+
+        expected = getClass().getResourceAsStream("/org/apache/tiles/autotag/jsp/test/DoStuffNoBodyTag.java");
+        effectiveFile = new File(file, "/org/apache/tiles/autotag/jsp/test/DoStuffNoBodyTag.java");
+        assertTrue(effectiveFile.exists());
+        effective = new FileInputStream(effectiveFile);
+        assertTrue(IOUtils.contentEquals(effective, expected));
+        effective.close();
+        expected.close();
+
+        FileUtils.deleteDirectory(file);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-jsp/src/test/resources/org/apache/tiles/autotag/jsp/test/DoStuffNoBodyTag.java b/tiles-autotag/tiles-autotag-jsp/src/test/resources/org/apache/tiles/autotag/jsp/test/DoStuffNoBodyTag.java
new file mode 100644
index 0000000..e5189c8
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/test/resources/org/apache/tiles/autotag/jsp/test/DoStuffNoBodyTag.java
@@ -0,0 +1,118 @@
+/*
+ * This file was automatically generated by Apache Tiles Autotag.
+ */
+package org.apache.tiles.autotag.jsp.test;
+
+import java.io.IOException;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.SimpleTagSupport;
+
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+
+/**
+ * Documentation of the DoStuffNoBody class.
+ */
+public class DoStuffNoBodyTag extends SimpleTagSupport {
+
+    /**
+     * The template model.
+     */
+    private org.apache.tiles.autotag.template.DoStuffNoBodyTemplate model = new org.apache.tiles.autotag.template.DoStuffNoBodyTemplate();
+
+    /**
+     * Parameter one.
+     */
+    private java.lang.Double one;
+
+    /**
+     * Parameter two.
+     */
+    private float two;
+
+    /**
+     * Parameter three.
+     */
+    private java.util.Date three;
+
+    /**
+     * Getter for one property.
+     *
+     * @return
+     * Parameter one.
+     */
+    public java.lang.Double getOne() {
+        return one;
+    }
+
+    /**
+     * Setter for one property.
+     *
+     * @param one
+     * Parameter one.
+     */
+    public void setOne(java.lang.Double one) {
+        this.one = one;
+    }
+
+    /**
+     * Getter for two property.
+     *
+     * @return
+     * Parameter two.
+     */
+    public float getTwo() {
+        return two;
+    }
+
+    /**
+     * Setter for two property.
+     *
+     * @param two
+     * Parameter two.
+     */
+    public void setTwo(float two) {
+        this.two = two;
+    }
+
+    /**
+     * Getter for three property.
+     *
+     * @return
+     * Parameter three.
+     */
+    public java.util.Date getThree() {
+        return three;
+    }
+
+    /**
+     * Setter for three property.
+     *
+     * @param three
+     * Parameter three.
+     */
+    public void setThree(java.util.Date three) {
+        this.three = three;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void doTag() throws JspException, IOException {
+        AutotagRuntime runtime = new org.apache.tiles.autotag.jsp.test.Runtime();
+        if (runtime instanceof SimpleTagSupport) {
+            SimpleTagSupport tag = (SimpleTagSupport) runtime;
+            tag.setJspContext(getJspContext());
+            tag.setJspBody(getJspBody());
+            tag.setParent(getParent());
+            tag.doTag();
+        }
+        Request request = runtime.createRequest();        
+        model.execute(
+            one,
+            two,
+            three,
+            request
+        );
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-jsp/src/test/resources/org/apache/tiles/autotag/jsp/test/DoStuffTag.java b/tiles-autotag/tiles-autotag-jsp/src/test/resources/org/apache/tiles/autotag/jsp/test/DoStuffTag.java
new file mode 100644
index 0000000..796b838
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/test/resources/org/apache/tiles/autotag/jsp/test/DoStuffTag.java
@@ -0,0 +1,120 @@
+/*
+ * This file was automatically generated by Apache Tiles Autotag.
+ */
+package org.apache.tiles.autotag.jsp.test;
+
+import java.io.IOException;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.SimpleTagSupport;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+
+/**
+ * Documentation of the DoStuff class.
+ */
+public class DoStuffTag extends SimpleTagSupport {
+
+    /**
+     * The template model.
+     */
+    private org.apache.tiles.autotag.template.DoStuffTemplate model = new org.apache.tiles.autotag.template.DoStuffTemplate();
+
+    /**
+     * Parameter one.
+     */
+    private java.lang.String one;
+
+    /**
+     * Parameter two.
+     */
+    private int two;
+
+    /**
+     * Parameter three.
+     */
+    private boolean three;
+
+    /**
+     * Getter for one property.
+     *
+     * @return
+     * Parameter one.
+     */
+    public java.lang.String getOne() {
+        return one;
+    }
+
+    /**
+     * Setter for one property.
+     *
+     * @param one
+     * Parameter one.
+     */
+    public void setOne(java.lang.String one) {
+        this.one = one;
+    }
+
+    /**
+     * Getter for two property.
+     *
+     * @return
+     * Parameter two.
+     */
+    public int getTwo() {
+        return two;
+    }
+
+    /**
+     * Setter for two property.
+     *
+     * @param two
+     * Parameter two.
+     */
+    public void setTwo(int two) {
+        this.two = two;
+    }
+
+    /**
+     * Getter for three property.
+     *
+     * @return
+     * Parameter three.
+     */
+    public boolean isThree() {
+        return three;
+    }
+
+    /**
+     * Setter for three property.
+     *
+     * @param three
+     * Parameter three.
+     */
+    public void setThree(boolean three) {
+        this.three = three;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void doTag() throws JspException, IOException {
+        AutotagRuntime runtime = new org.apache.tiles.autotag.jsp.test.Runtime();
+        if (runtime instanceof SimpleTagSupport) {
+            SimpleTagSupport tag = (SimpleTagSupport) runtime;
+            tag.setJspContext(getJspContext());
+            tag.setJspBody(getJspBody());
+            tag.setParent(getParent());
+            tag.doTag();
+        }
+        Request request = runtime.createRequest();        
+        ModelBody modelBody = runtime.createModelBody();
+        model.execute(
+            one,
+            two,
+            three,
+            request, modelBody
+        );
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-jsp/src/test/resources/tldtest-jsp.tld b/tiles-autotag/tiles-autotag-jsp/src/test/resources/tldtest-jsp.tld
new file mode 100644
index 0000000..2db4ddc
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-jsp/src/test/resources/tldtest-jsp.tld
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- This file was automatically generated by Apache Tiles Autotag. -->
+<taglib
+  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
+  xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  version="2.1">
+   <description>
+   <![CDATA[
+   Test for TLD docs.
+   ]]>
+   </description>
+   <tlib-version>1.2</tlib-version>
+   <short-name>tldtest</short-name>
+   <uri>http://www.initrode.net/tags/test</uri>
+   <tag>
+      <description>
+      <![CDATA[
+      Documentation of the DoStuff class
+      ]]>
+      </description>
+      <name>doStuff</name>
+      <tag-class>org.apache.tiles.autotag.jsp.test.DoStuffTag</tag-class>
+      <body-content>scriptless</body-content>
+      <attribute>
+         <description>
+         <![CDATA[
+         Parameter one.
+         ]]>
+         </description>
+         <name>one</name>
+         <required>true</required>
+         <rtexprvalue>true</rtexprvalue>
+         <type>java.lang.String</type>
+      </attribute>
+      <attribute>
+         <description>
+         <![CDATA[
+         Parameter two.
+         ]]>
+         </description>
+         <name>two</name>
+         <required>false</required>
+         <rtexprvalue>true</rtexprvalue>
+         <type>int</type>
+      </attribute>
+      <attribute>
+         <description>
+         <![CDATA[
+         Parameter three.
+         ]]>
+         </description>
+         <name>three</name>
+         <required>false</required>
+         <rtexprvalue>true</rtexprvalue>
+         <type>long</type>
+      </attribute>
+   </tag>
+   <tag>
+      <description>
+      <![CDATA[
+      Documentation of the DoStuffNoBody class
+      ]]>
+      </description>
+      <name>doStuffNoBody</name>
+      <tag-class>org.apache.tiles.autotag.jsp.test.DoStuffNoBodyTag</tag-class>
+      <body-content>empty</body-content>
+      <attribute>
+         <description>
+         <![CDATA[
+         Parameter one.
+         ]]>
+         </description>
+         <name>one</name>
+         <required>true</required>
+         <rtexprvalue>true</rtexprvalue>
+         <type>java.lang.Double</type>
+      </attribute>
+      <attribute>
+         <description>
+         <![CDATA[
+         Parameter two.
+         ]]>
+         </description>
+         <name>two</name>
+         <required>false</required>
+         <rtexprvalue>true</rtexprvalue>
+         <type>float</type>
+      </attribute>
+      <attribute>
+         <description>
+         <![CDATA[
+         Parameter three.
+         ]]>
+         </description>
+         <name>three</name>
+         <required>false</required>
+         <rtexprvalue>true</rtexprvalue>
+         <type>java.util.Date</type>
+      </attribute>
+   </tag>
+</taglib>
diff --git a/tiles-autotag/tiles-autotag-velocity/pom.xml b/tiles-autotag/tiles-autotag-velocity/pom.xml
new file mode 100644
index 0000000..d0399ca
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>tiles-autotag</artifactId>
+    <groupId>org.apache.tiles</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.tiles</groupId>
+  <artifactId>tiles-autotag-velocity</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Autotag - Velocity support</name>
+  <description>Module to generate Velocity code to use tag models.</description>
+  <dependencies>
+  	<dependency>
+  		<groupId>org.apache.tiles</groupId>
+  		<artifactId>tiles-autotag-core</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>junit</groupId>
+  		<artifactId>junit</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>commons-io</groupId>
+  		<artifactId>commons-io</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.easymock</groupId>
+  		<artifactId>easymock</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  </dependencies>
+</project>
diff --git a/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/VelocityDirectiveGenerator.java b/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/VelocityDirectiveGenerator.java
new file mode 100644
index 0000000..8493fc3
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/VelocityDirectiveGenerator.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.autotag.velocity;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.tiles.autotag.generate.AbstractTemplateClassGenerator;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * Generates a Velocity directive using a template class.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityDirectiveGenerator extends AbstractTemplateClassGenerator {
+
+    /**
+     * Constructor.
+     *
+     * @param velocityEngine The Velocity engine.
+     */
+    public VelocityDirectiveGenerator(VelocityEngine velocityEngine) {
+        super(velocityEngine);
+    }
+
+    @Override
+    protected String getDirectoryName(File directory, String packageName,
+            TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters,
+            String runtimeClass) {
+        return packageName.replaceAll("\\.", "/");
+    }
+
+    @Override
+    protected String getFilename(File directory, String packageName,
+            TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters,
+            String runtimeClass) {
+        return clazz.getTagClassPrefix() + "Directive.java";
+    }
+
+    @Override
+    protected String getTemplatePath(File directory, String packageName,
+            TemplateSuite suite, TemplateClass clazz, Map<String, String> parameters,
+            String runtimeClass) {
+        return "/org/apache/tiles/autotag/velocity/velocityDirective.vm";
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/VelocityPropertiesGenerator.java b/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/VelocityPropertiesGenerator.java
new file mode 100644
index 0000000..a9a8619
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/VelocityPropertiesGenerator.java
@@ -0,0 +1,64 @@
+/*
+ * $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.velocity;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.tiles.autotag.generate.AbstractTemplateSuiteGenerator;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * Generates a Velocity properties containing the list of generated user directives for future use.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityPropertiesGenerator extends AbstractTemplateSuiteGenerator {
+
+    /**
+     * Constructor.
+     *
+     * @param velocityEngine The Velocity engine.
+     */
+    public VelocityPropertiesGenerator(VelocityEngine velocityEngine) {
+        super(velocityEngine);
+    }
+
+    @Override
+    protected String getTemplatePath(File directory, String packageName,
+            TemplateSuite suite, Map<String, String> parameters) {
+        return "/org/apache/tiles/autotag/velocity/velocityProperties.vm";
+    }
+
+    @Override
+    protected String getFilename(File directory, String packageName,
+            TemplateSuite suite, Map<String, String> parameters) {
+        return "velocity.properties";
+    }
+
+    @Override
+    protected String getDirectoryName(File directory, String packageName,
+            TemplateSuite suite, Map<String, String> parameters) {
+        return "META-INF/";
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/VelocityTemplateGeneratorFactory.java b/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/VelocityTemplateGeneratorFactory.java
new file mode 100644
index 0000000..02f0f14
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/VelocityTemplateGeneratorFactory.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.velocity;
+
+import java.io.File;
+
+import org.apache.tiles.autotag.generate.TemplateGenerator;
+import org.apache.tiles.autotag.generate.TemplateGeneratorBuilder;
+import org.apache.tiles.autotag.generate.TemplateGeneratorFactory;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * Creates a template generator that generates code to build Velocity code
+ * around template classes.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityTemplateGeneratorFactory implements
+        TemplateGeneratorFactory {
+
+    /**
+     * Location of the file.
+     */
+    private File classesOutputDirectory;
+
+    /**
+     * Location of the file.
+     */
+    private File resourcesOutputDirectory;
+
+    /**
+     * The Velocity engine.
+     */
+    private VelocityEngine velocityEngine;
+
+    /**
+     * The template generator builder.
+     */
+    private TemplateGeneratorBuilder templateGeneratorBuilder;
+
+    /**
+     * Constructor.
+     *
+     * @param classesOutputDirectory The directory where classes will be generated.
+     * @param resourcesOutputDirectory The directory where velocity.properties will be written.
+     * @param velocityEngine The Velocity engine.
+     * @param templateGeneratorBuilder The template generator builder.
+     */
+    public VelocityTemplateGeneratorFactory(File classesOutputDirectory,
+            File resourcesOutputDirectory, VelocityEngine velocityEngine,
+            TemplateGeneratorBuilder templateGeneratorBuilder) {
+        this.classesOutputDirectory = classesOutputDirectory;
+        this.resourcesOutputDirectory = resourcesOutputDirectory;
+        this.velocityEngine = velocityEngine;
+        this.templateGeneratorBuilder = templateGeneratorBuilder;
+    }
+
+    @Override
+    public TemplateGenerator createTemplateGenerator() {
+        return templateGeneratorBuilder
+                .setClassesOutputDirectory(classesOutputDirectory)
+                .setResourcesOutputDirectory(resourcesOutputDirectory)
+                .addResourcesTemplateSuiteGenerator(
+                        new VelocityPropertiesGenerator(velocityEngine))
+                .addClassesTemplateClassGenerator(
+                        new VelocityDirectiveGenerator(velocityEngine)).build();
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/package-info.java b/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/package-info.java
new file mode 100644
index 0000000..a2c9d18
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/main/java/org/apache/tiles/autotag/velocity/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Autotag support for Velocity.
+ */
+package org.apache.tiles.autotag.velocity;
diff --git a/tiles-autotag/tiles-autotag-velocity/src/main/resources/org/apache/tiles/autotag/velocity/velocityDirective.vm b/tiles-autotag/tiles-autotag-velocity/src/main/resources/org/apache/tiles/autotag/velocity/velocityDirective.vm
new file mode 100644
index 0000000..f0bef91
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/main/resources/org/apache/tiles/autotag/velocity/velocityDirective.vm
@@ -0,0 +1,82 @@
+#*
+ * $Id: tiles-jsp.tld 836180 2009-11-14 14:00:02Z apetrelli $
+ *
+ * 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.
+ *#/*
+ * This file was automatically generated by Apache Tiles Autotag.
+ */
+package ${packageName};
+
+import java.io.IOException;
+import java.io.Writer;
+
+#if(${clazz.hasBody()})
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+#end
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.runtime.directive.Directive;
+import org.apache.velocity.runtime.parser.node.Node;
+
+/**
+#foreach($line in $stringTool.splitOnNewlines(${clazz.documentation}))
+ * ${line}
+#end
+ */
+public class ${clazz.tagClassPrefix}Directive extends Directive {
+
+    /**
+     * The template model.
+     */
+    private ${clazz.name} model = new ${clazz.name}();
+
+    /** {@inheritDoc} */
+    @Override
+    public String getName() {
+        return "${suite.name}_${clazz.tagName}";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getType() {
+        return #if(${clazz.hasBody()})BLOCK#{else}LINE#{end};
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean render(InternalContextAdapter context, Writer writer, Node node)
+            throws IOException {
+        AutotagRuntime runtime = new ${runtimeClass}();
+        if (runtime instanceof Directive) {
+            ((Directive) runtime).render(context, writer, node);
+        }
+        Request request = runtime.createRequest();
+#if(${clazz.hasBody()})
+        ModelBody modelBody = runtime.createModelBody();
+#end
+        model.execute(
+#foreach($parameter in ${clazz.parameters})
+            ($stringTool.getClassToCast(${parameter.type})) runtime.getParameter("${parameter.exportedName}", $stringTool.getDefaultValue(${parameter.type}, ${parameter.defaultValue})),
+#end
+            request#if(${clazz.hasBody()}), modelBody#end
+
+        );
+        return true;
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-velocity/src/main/resources/org/apache/tiles/autotag/velocity/velocityProperties.vm b/tiles-autotag/tiles-autotag-velocity/src/main/resources/org/apache/tiles/autotag/velocity/velocityProperties.vm
new file mode 100644
index 0000000..4fc1bb7
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/main/resources/org/apache/tiles/autotag/velocity/velocityProperties.vm
@@ -0,0 +1,22 @@
+#*
+ * $Id: tiles-jsp.tld 836180 2009-11-14 14:00:02Z apetrelli $
+ *
+ * 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.
+ *## This file was automatically generated by Apache Tiles Autotag.
+userdirective=#foreach($clazz in ${suite.getTemplateClasses()})#if($velocityCount > 1),\
+  #{end}${packageName}.${clazz.tagClassPrefix}Directive#end
diff --git a/tiles-autotag/tiles-autotag-velocity/src/site/site.xml b/tiles-autotag/tiles-autotag-velocity/src/site/site.xml
new file mode 100644
index 0000000..0a7d00d
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Tiles Autotags">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Tiles Autotag"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-autotag/tiles-autotag-velocity/src/test/java/org/apache/tiles/autotag/velocity/VelocityDirectiveGeneratorTest.java b/tiles-autotag/tiles-autotag-velocity/src/test/java/org/apache/tiles/autotag/velocity/VelocityDirectiveGeneratorTest.java
new file mode 100644
index 0000000..9806199
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/test/java/org/apache/tiles/autotag/velocity/VelocityDirectiveGeneratorTest.java
@@ -0,0 +1,143 @@
+/*
+ * $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.velocity;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateMethod;
+import org.apache.tiles.autotag.model.TemplateParameter;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.tiles.request.Request;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.Test;
+
+/**
+ * Tests {@link VelocityDirectiveGenerator}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityDirectiveGeneratorTest {
+
+    /**
+     * Test method for
+     * {@link VelocityDirectiveGenerator#generate(File, String, TemplateSuite, TemplateClass, java.util.Map)}.
+     * @throws Exception If something goes wrong.
+     */
+    @Test
+    public void testGenerate() throws Exception {
+        Properties props = new Properties();
+        InputStream propsStream = getClass().getResourceAsStream("/org/apache/tiles/autotag/velocity.properties");
+        props.load(propsStream);
+        propsStream.close();
+        VelocityEngine velocityEngine = new VelocityEngine(props);
+
+        VelocityDirectiveGenerator generator = new VelocityDirectiveGenerator(velocityEngine);
+        File file = File.createTempFile("autotag", null);
+        file.delete();
+        file.mkdir();
+        file.deleteOnExit();
+        TemplateSuite suite = new TemplateSuite("tldtest", "Test for TLD docs.");
+
+        List<TemplateParameter> params = new ArrayList<TemplateParameter>();
+        TemplateParameter param = new TemplateParameter("one", "one", "java.lang.String", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "int", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "boolean", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        param = new TemplateParameter("modelBody", "modelBody", ModelBody.class.getName(), null, false);
+        param.setDocumentation("The body.");
+        params.add(param);
+        TemplateMethod executeMethod = new TemplateMethod("execute", params);
+
+        TemplateClass clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffTemplate",
+                "doStuff", "DoStuff", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuff class.");
+
+        generator.generate(file, "org.apache.tiles.autotag.velocity.test", suite, clazz, null,
+                           "org.apache.tiles.autotag.velocity.test.Runtime");
+
+        InputStream expected = getClass()
+                .getResourceAsStream(
+                        "/org/apache/tiles/autotag/velocity/test/DoStuffDirective.javat");
+        File effectiveFile = new File(file, "/org/apache/tiles/autotag/velocity/test/DoStuffDirective.java");
+        assertTrue(effectiveFile.exists());
+        InputStream effective = new FileInputStream(effectiveFile);
+        assertTrue(IOUtils.contentEquals(effective, expected));
+        effective.close();
+        expected.close();
+
+        suite.addTemplateClass(clazz);
+        params = new ArrayList<TemplateParameter>();
+        param = new TemplateParameter("one", "one", "java.lang.Double", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "float", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "java.util.Date", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        executeMethod = new TemplateMethod("execute", params);
+
+        clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffNoBodyTemplate",
+                "doStuffNoBody", "DoStuffNoBody", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuffNoBody class.");
+
+        suite.addTemplateClass(clazz);
+
+        generator.generate(file, "org.apache.tiles.autotag.velocity.test", suite, clazz, null,
+                           "org.apache.tiles.autotag.velocity.test.Runtime");
+
+        expected = getClass()
+                .getResourceAsStream(
+                        "/org/apache/tiles/autotag/velocity/test/DoStuffNoBodyDirective.javat");
+        effectiveFile = new File(file, "/org/apache/tiles/autotag/velocity/test/DoStuffNoBodyDirective.java");
+        assertTrue(effectiveFile.exists());
+        effective = new FileInputStream(effectiveFile);
+        assertTrue(IOUtils.contentEquals(effective, expected));
+        effective.close();
+        expected.close();
+
+        FileUtils.deleteDirectory(file);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-velocity/src/test/java/org/apache/tiles/autotag/velocity/VelocityPropertiesGeneratorTest.java b/tiles-autotag/tiles-autotag-velocity/src/test/java/org/apache/tiles/autotag/velocity/VelocityPropertiesGeneratorTest.java
new file mode 100644
index 0000000..f8b5f92
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/test/java/org/apache/tiles/autotag/velocity/VelocityPropertiesGeneratorTest.java
@@ -0,0 +1,128 @@
+/*
+ * $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.velocity;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.model.TemplateClass;
+import org.apache.tiles.autotag.model.TemplateMethod;
+import org.apache.tiles.autotag.model.TemplateParameter;
+import org.apache.tiles.autotag.model.TemplateSuite;
+import org.apache.tiles.request.Request;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.Test;
+
+/**
+ * Tests {@link VelocityPropertiesGenerator}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityPropertiesGeneratorTest {
+
+    /**
+     * Test method for
+     * {@link org.apache.tiles.autotag.velocity.VelocityPropertiesGenerator
+     * #generate(File, String, TemplateSuite, java.util.Map)}.
+     * @throws Exception If something goes wrong.
+     */
+    @Test
+    public void testGenerate() throws Exception {
+        Properties props = new Properties();
+        InputStream propsStream = getClass().getResourceAsStream("/org/apache/tiles/autotag/velocity.properties");
+        props.load(propsStream);
+        propsStream.close();
+        VelocityEngine velocityEngine = new VelocityEngine(props);
+
+        VelocityPropertiesGenerator generator = new VelocityPropertiesGenerator(velocityEngine);
+        File file = File.createTempFile("autotag", null);
+        file.delete();
+        file.mkdir();
+        file.deleteOnExit();
+        TemplateSuite suite = new TemplateSuite("tldtest", "Test for TLD docs.");
+
+        List<TemplateParameter> params = new ArrayList<TemplateParameter>();
+        TemplateParameter param = new TemplateParameter("one", "one", "java.lang.String", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "int", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "long", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        param = new TemplateParameter("modelBody", "modelBody", ModelBody.class.getName(), null, false);
+        param.setDocumentation("The body.");
+        params.add(param);
+        TemplateMethod executeMethod = new TemplateMethod("execute", params);
+
+        TemplateClass clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffTemplate",
+                "doStuff", "DoStuff", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuff class");
+
+        suite.addTemplateClass(clazz);
+        params = new ArrayList<TemplateParameter>();
+        param = new TemplateParameter("one", "one", "java.lang.Double", null, true);
+        param.setDocumentation("Parameter one.");
+        params.add(param);
+        param = new TemplateParameter("two", "two", "float", null, false);
+        param.setDocumentation("Parameter two.");
+        params.add(param);
+        param = new TemplateParameter("three", "three", "java.util.Date", null, false);
+        param.setDocumentation("Parameter three.");
+        params.add(param);
+        param = new TemplateParameter("request", "request", Request.class.getName(), null, false);
+        param.setDocumentation("The request.");
+        params.add(param);
+        executeMethod = new TemplateMethod("execute", params);
+
+        clazz = new TemplateClass("org.apache.tiles.autotag.template.DoStuffNoBodyTemplate",
+                "doStuffNoBody", "DoStuffNoBody", executeMethod);
+        clazz.setDocumentation("Documentation of the DoStuffNoBody class");
+
+        suite.addTemplateClass(clazz);
+
+        generator.generate(file, "org.apache.tiles.autotag.velocity.test", suite, null);
+
+        InputStream expected = getClass().getResourceAsStream("/velocity.properties.test");
+        File effectiveFile = new File(file, "META-INF/velocity.properties");
+        assertTrue(effectiveFile.exists());
+        InputStream effective = new FileInputStream(effectiveFile);
+        assertTrue(IOUtils.contentEquals(effective, expected));
+        effective.close();
+        expected.close();
+
+        FileUtils.deleteDirectory(file);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-velocity/src/test/java/org/apache/tiles/autotag/velocity/VelocityTemplateGeneratorFactoryTest.java b/tiles-autotag/tiles-autotag-velocity/src/test/java/org/apache/tiles/autotag/velocity/VelocityTemplateGeneratorFactoryTest.java
new file mode 100644
index 0000000..a17ffd8
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/test/java/org/apache/tiles/autotag/velocity/VelocityTemplateGeneratorFactoryTest.java
@@ -0,0 +1,65 @@
+/*
+ * $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.velocity;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.File;
+
+import org.apache.tiles.autotag.generate.TemplateGenerator;
+import org.apache.tiles.autotag.generate.TemplateGeneratorBuilder;
+import org.apache.velocity.app.VelocityEngine;
+import org.junit.Test;
+
+/**
+ * Tests {@link JspTemplateGeneratorFactory}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityTemplateGeneratorFactoryTest {
+
+    /**
+     * Test method for {@link JspTemplateGeneratorFactory#createTemplateGenerator()}.
+     */
+    @Test
+    public void testCreateTemplateGenerator() {
+        File classesOutputDirectory = createMock(File.class);
+        File resourcesOutputDirectory = createMock(File.class);
+        VelocityEngine velocityEngine = createMock(VelocityEngine.class);
+        TemplateGeneratorBuilder builder = createMock(TemplateGeneratorBuilder.class);
+        TemplateGenerator generator = createMock(TemplateGenerator.class);
+
+        expect(builder.setClassesOutputDirectory(classesOutputDirectory)).andReturn(builder);
+        expect(builder.setResourcesOutputDirectory(resourcesOutputDirectory)).andReturn(builder);
+        expect(builder.addResourcesTemplateSuiteGenerator(isA(VelocityPropertiesGenerator.class))).andReturn(builder);
+        expect(builder.addClassesTemplateClassGenerator(isA(VelocityDirectiveGenerator.class))).andReturn(builder);
+        expect(builder.build()).andReturn(generator);
+
+        replay(classesOutputDirectory, resourcesOutputDirectory, velocityEngine, builder, generator);
+        VelocityTemplateGeneratorFactory factory = new VelocityTemplateGeneratorFactory(
+                classesOutputDirectory, resourcesOutputDirectory,
+                velocityEngine, builder);
+        assertSame(generator, factory.createTemplateGenerator());
+        verify(classesOutputDirectory, resourcesOutputDirectory, velocityEngine, builder, generator);
+    }
+
+}
diff --git a/tiles-autotag/tiles-autotag-velocity/src/test/resources/org/apache/tiles/autotag/velocity/test/DoStuffDirective.javat b/tiles-autotag/tiles-autotag-velocity/src/test/resources/org/apache/tiles/autotag/velocity/test/DoStuffDirective.javat
new file mode 100644
index 0000000..5caf980
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/test/resources/org/apache/tiles/autotag/velocity/test/DoStuffDirective.javat
@@ -0,0 +1,56 @@
+/*
+ * This file was automatically generated by Apache Tiles Autotag.
+ */
+package org.apache.tiles.autotag.velocity.test;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.runtime.directive.Directive;
+import org.apache.velocity.runtime.parser.node.Node;
+
+/**
+ * Documentation of the DoStuff class.
+ */
+public class DoStuffDirective extends Directive {
+
+    /**
+     * The template model.
+     */
+    private org.apache.tiles.autotag.template.DoStuffTemplate model = new org.apache.tiles.autotag.template.DoStuffTemplate();
+
+    /** {@inheritDoc} */
+    @Override
+    public String getName() {
+        return "tldtest_doStuff";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getType() {
+        return BLOCK;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean render(InternalContextAdapter context, Writer writer, Node node)
+            throws IOException {
+        AutotagRuntime runtime = new org.apache.tiles.autotag.velocity.test.Runtime();
+        if (runtime instanceof Directive) {
+            ((Directive) runtime).render(context, writer, node);
+        }
+        Request request = runtime.createRequest();
+        ModelBody modelBody = runtime.createModelBody();
+        model.execute(
+            (java.lang.String) runtime.getParameter("one", null),
+            (java.lang.Integer) runtime.getParameter("two", 0),
+            (java.lang.Boolean) runtime.getParameter("three", false),
+            request, modelBody
+        );
+        return true;
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-velocity/src/test/resources/org/apache/tiles/autotag/velocity/test/DoStuffNoBodyDirective.javat b/tiles-autotag/tiles-autotag-velocity/src/test/resources/org/apache/tiles/autotag/velocity/test/DoStuffNoBodyDirective.javat
new file mode 100644
index 0000000..88c9919
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/test/resources/org/apache/tiles/autotag/velocity/test/DoStuffNoBodyDirective.javat
@@ -0,0 +1,54 @@
+/*
+ * This file was automatically generated by Apache Tiles Autotag.
+ */
+package org.apache.tiles.autotag.velocity.test;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.runtime.directive.Directive;
+import org.apache.velocity.runtime.parser.node.Node;
+
+/**
+ * Documentation of the DoStuffNoBody class.
+ */
+public class DoStuffNoBodyDirective extends Directive {
+
+    /**
+     * The template model.
+     */
+    private org.apache.tiles.autotag.template.DoStuffNoBodyTemplate model = new org.apache.tiles.autotag.template.DoStuffNoBodyTemplate();
+
+    /** {@inheritDoc} */
+    @Override
+    public String getName() {
+        return "tldtest_doStuffNoBody";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getType() {
+        return LINE;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean render(InternalContextAdapter context, Writer writer, Node node)
+            throws IOException {
+        AutotagRuntime runtime = new org.apache.tiles.autotag.velocity.test.Runtime();
+        if (runtime instanceof Directive) {
+            ((Directive) runtime).render(context, writer, node);
+        }
+        Request request = runtime.createRequest();
+        model.execute(
+            (java.lang.Double) runtime.getParameter("one", null),
+            (java.lang.Float) runtime.getParameter("two", 0.0f),
+            (java.util.Date) runtime.getParameter("three", null),
+            request
+        );
+        return true;
+    }
+}
diff --git a/tiles-autotag/tiles-autotag-velocity/src/test/resources/velocity.properties.test b/tiles-autotag/tiles-autotag-velocity/src/test/resources/velocity.properties.test
new file mode 100644
index 0000000..4d4051a
--- /dev/null
+++ b/tiles-autotag/tiles-autotag-velocity/src/test/resources/velocity.properties.test
@@ -0,0 +1,3 @@
+# This file was automatically generated by Apache Tiles Autotag.
+userdirective=org.apache.tiles.autotag.velocity.test.DoStuffDirective,\
+  org.apache.tiles.autotag.velocity.test.DoStuffNoBodyDirective
\ No newline at end of file
diff --git a/tiles-request/pom.xml b/tiles-request/pom.xml
new file mode 100644
index 0000000..cb10f74
--- /dev/null
+++ b/tiles-request/pom.xml
@@ -0,0 +1,375 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>tiles-master</artifactId>
+        <groupId>org.apache.tiles</groupId>
+        <version>4</version>
+        <relativePath/>
+    </parent>
+    <groupId>org.apache.tiles</groupId>
+    <artifactId>tiles-request</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>Tiles Request Framework</name>
+    <description>Request abstraction for various request and template technologies</description>
+    <url>http://tiles.apache.org/tiles-request/</url>
+    <scm>
+        <connection>scm:svn:http://svn.eu.apache.org/repos/asf/tiles/framework/trunk/tiles-request/</connection>
+        <developerConnection>scm:svn:https://svn.eu.apache.org/repos/asf/tiles/framework/trunk/tiles-request/</developerConnection>
+        <url>http://svn.eu.apache.org/viewvc/tiles/framework/trunk/tiles-request/</url>
+    </scm>
+    <issueManagement>
+        <system>JIRA</system>
+        <url>https://issues.apache.org/jira/browse/TREQ</url>
+    </issueManagement>
+
+    <modules>
+        <module>tiles-request-api</module>
+        <module>tiles-request-servlet</module>
+        <module>tiles-request-servlet-wildcard</module>
+        <module>tiles-request-portlet</module>
+        <module>tiles-request-portlet-wildcard</module>
+        <module>tiles-request-jsp</module>
+        <module>tiles-request-freemarker</module>
+        <module>tiles-request-velocity</module>
+        <module>tiles-request-mustache</module>
+    </modules>
+    <distributionManagement>
+        <site>
+            <id>apache-site</id>
+            <url>scp://people.apache.org/www/tiles.apache.org/tiles-request</url>
+        </site>
+    </distributionManagement>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <configuration>
+                        <archive>
+                            <manifestFile>${tiles.manifestfile}</manifestFile>
+                            <manifest>
+                                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+                                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                            </manifest>
+                        </archive>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>maven-bundle-plugin</artifactId>
+                    <version>2.3.7</version>
+                    <inherited>true</inherited>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <excludeDependencies>true</excludeDependencies>
+                    <manifestLocation>target/osgi</manifestLocation>
+                    <instructions>
+                        <_nouses>true</_nouses>
+                        <Bundle-SymbolicName>${tiles.osgi.symbolicName}</Bundle-SymbolicName>
+                        <Export-Package>${tiles.osgi.export}</Export-Package>
+                        <Private-Package>${tiles.osgi.private}</Private-Package>
+                        <Import-Package>${tiles.osgi.import}</Import-Package>
+                        <DynamicImport-Package>${tiles.osgi.dynamicImport}</DynamicImport-Package>
+                        <Bundle-DocURL>${project.url}</Bundle-DocURL>
+                        <Specification-Title>${project.name}</Specification-Title>
+                        <Specification-Version>${project.version}</Specification-Version>
+                        <Specification-Vendor>${project.organization.name}</Specification-Vendor>
+                        <Implementation-Title>${project.name}</Implementation-Title>
+                        <Implementation-Version>${project.version}</Implementation-Version>
+                        <Implementation-Vendor>${project.organization.name}</Implementation-Vendor>
+                        <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <configuration>
+                    <releaseProfiles>apache-release</releaseProfiles><!-- xxx tiles still uses "release" instead of "apache-release" -->
+                    <goals>deploy site-deploy</goals>
+                </configuration>
+            </plugin>
+        </plugins>
+
+        <defaultGoal>install</defaultGoal>
+    </build>
+
+    <properties>
+        <tiles.osgi.symbolicName>org.apache.${project.artifactId}</tiles.osgi.symbolicName>
+        <tiles.osgi.export>org.apache.tiles.*;version=${project.version}</tiles.osgi.export>
+        <tiles.osgi.import>*</tiles.osgi.import>
+        <tiles.osgi.dynamicImport />
+        <tiles.osgi.private />
+        <tiles.manifestfile>target/osgi/MANIFEST.MF</tiles.manifestfile>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.apache.tiles</groupId>
+                <artifactId>tiles-request-api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.tiles</groupId>
+                <artifactId>tiles-request-servlet</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.tiles</groupId>
+                <artifactId>tiles-request-portlet</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.tiles</groupId>
+                <artifactId>tiles-autotag-core-runtime</artifactId>
+                <version>${project.version}</version>
+                <optional>true</optional>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-jdk14</artifactId>
+                <version>1.5.8</version>
+            </dependency>
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>4.7</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.easymock</groupId>
+                <artifactId>easymock</artifactId>
+                <version>3.0</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.easymock</groupId>
+                <artifactId>easymockclassextension</artifactId>
+                <version>3.0</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                    <groupId>javax.servlet</groupId>
+                    <artifactId>servlet-api</artifactId>
+                    <version>2.5</version>
+                    <scope>provided</scope>
+            </dependency>
+            <dependency>
+                <groupId>javax.portlet</groupId>
+                <artifactId>portlet-api</artifactId>
+                <version>2.0</version>
+                <scope>provided</scope>
+            </dependency>
+            <dependency>
+                    <groupId>org.apache.shale</groupId>
+                    <artifactId>shale-test</artifactId>
+                    <version>1.0.5</version>
+                    <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>jcl-over-slf4j</artifactId>
+                <version>1.5.8</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-web</artifactId>
+                <version>2.5.6</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>commons-logging</groupId>
+                        <artifactId>commons-logging</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-webmvc-portlet</artifactId>
+                <version>2.5.6</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.springframework</groupId>
+                        <artifactId>spring-webmvc</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>commons-logging</groupId>
+                        <artifactId>commons-logging</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>javax.servlet.jsp</groupId>
+                <artifactId>jsp-api</artifactId>
+                <version>2.1</version>
+                <scope>provided</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.velocity</groupId>
+                <artifactId>velocity-tools</artifactId>
+                <version>2.0</version>
+                <exclusions>
+                    <exclusion>
+                        <artifactId>struts-taglib</artifactId>
+                        <groupId>org.apache.struts</groupId>
+                    </exclusion>
+                    <exclusion>
+                        <artifactId>struts-tiles</artifactId>
+                        <groupId>org.apache.struts</groupId>
+                    </exclusion>
+                    <exclusion>
+                        <artifactId>struts-core</artifactId>
+                        <groupId>org.apache.struts</groupId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>commons-logging</groupId>
+                        <artifactId>commons-logging</artifactId>
+                    </exclusion>
+                    <exclusion>
+                        <artifactId>sslext</artifactId>
+                        <groupId>sslext</groupId>
+                    </exclusion>
+                    <exclusion>
+                        <artifactId>commons-chain</artifactId>
+                        <groupId>commons-chain</groupId>
+                    </exclusion>
+                    <exclusion>
+                        <artifactId>commons-validator</artifactId>
+                        <groupId>commons-validator</groupId>
+                    </exclusion>
+                    <exclusion>
+                        <artifactId>commons-digester</artifactId>
+                        <groupId>commons-digester</groupId>
+                    </exclusion>
+                    <exclusion>
+                        <artifactId>commons-beanutils</artifactId>
+                        <groupId>commons-beanutils</groupId>
+                    </exclusion>
+                    <exclusion>
+                        <artifactId>dom4j</artifactId>
+                        <groupId>dom4j</groupId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>org.freemarker</groupId>
+                <artifactId>freemarker</artifactId>
+                <version>2.3.15</version>
+            </dependency>
+            <dependency>
+                <groupId>com.github.spullara.mustache.java</groupId>
+                <artifactId>core</artifactId>
+                <version>0.6.2</version>
+            </dependency>
+            <dependency>
+                <groupId>com.github.spullara.mustache.java</groupId>
+                <artifactId>builder</artifactId>
+                <version>0.6.2</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+    <profiles>
+        <profile>
+            <id>apache-release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-install-plugin</artifactId>
+                        <configuration>
+                            <createChecksum>true</createChecksum>
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>rat-maven-plugin</artifactId>
+                        <version>1.0-alpha-3</version>
+                        <executions>
+                            <execution>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>check</goal>
+                                </goals>
+                                <configuration>
+                                    <addDefaultLicenseMatchers>false</addDefaultLicenseMatchers>
+                                    <licenseMatchers>
+                                        <classNames>
+                                            <className>rat.analysis.license.ApacheSoftwareLicense20</className>
+                                        </classNames>
+                                    </licenseMatchers>
+                                    <includes>
+                                        <include>pom.xml</include>
+                                        <include>src/**</include>
+                                    </includes>
+                                    <excludes>
+                                        <exclude>**/*LICENSE.txt</exclude>
+                                        <exclude>**/*MANIFEST.MF</exclude>
+                                    </excludes>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>linkcheck</id>
+            <reporting>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-linkcheck-plugin</artifactId>
+                        <version>1.1</version>
+                        <configuration>
+                            <excludedLinks>
+                                <excludedLink>**/index.html</excludedLink>
+                                <excludedLink>**/logo.png</excludedLink>
+                            </excludedLinks>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </reporting>
+
+        </profile>
+    </profiles>
+
+</project>
diff --git a/tiles-request/src/site/apt/dev/release.apt b/tiles-request/src/site/apt/dev/release.apt
new file mode 100644
index 0000000..ab9bd52
--- /dev/null
+++ b/tiles-request/src/site/apt/dev/release.apt
@@ -0,0 +1,351 @@
+~~ $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.
+~~
+         -----------
+         Release Process
+         -----------
+
+Tiles Release Process
+
+  Here you will find the steps to create releases for Tiles.
+
+Prerequisites
+
+  To create a release you have to install:
+
+  * {{{http://java.sun.com/javase/6}Java 6.0}}. If you are using a newer
+  version of Java, it is suggested to <<change JAVA_HOME environment variable>>
+  when calling Maven, so it points to an instance of Java 6.0;
+
+  * {{{http://maven.apache.org/}Maven 2.2 or 3}};
+
+  * {{{http://www.gnupg.org/}GnuPG}};
+
+  * {{{http://www.openssh.com/}OpenSSH}};
+
+  * {{{http://www.graphviz.org/}GraphViz}};
+
+One-time operations
+
+  These operations need to be performed only one time.
+
+* Create and publish your GPG key
+
+  To create a GPG key, follow the
+  {{{http://www.apache.org/dev/openpgp.html}guidelines}}.
+  Include it in:
+
+-------------------------------------
+https://svn.apache.org/repos/asf/tiles/site/KEYS
+-------------------------------------
+
+  Publish your GPG key in a PGP key server, such as
+  {{{http://pgp.mit.edu/} MIT Keyserver}}.
+
+* Create and upload yout SSH key
+
+  * Generate your SSH key (in this case we will use DSA encryption):
+
+---------------------------------
+ssh-keygen -t rsa
+---------------------------------
+
+  * Copy your public key to the server:
+
+--------------------------------------
+scp ~/.ssh/id_rsa.pub user@people.apache.org:.ssh/authorized_keys
+--------------------------------------
+
+  * Try to login:
+
+---------------------------------
+ssh user@people.apache.org
+---------------------------------
+
+  If it does not ask you a password, everything is ok.
+
+* Modify <<<settings.xml>>>
+
+  Your <<<settings.xml>>> must be modified to allow deployment.
+
+  This is the minimal configuration, obviously if you already have a <<<settings.xml>>> file,
+  you must edit it:
+
+---------------------------
+<settings xmlns="http://maven.apache.org/POM/4.0.0"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
+    <servers>
+        <server>
+            <id>apache-site</id>
+            <username>YOUR_APACHE_USERNAME</username>
+            <filePermissions>664</filePermissions>
+            <directoryPermissions>775</directoryPermissions>
+        </server>
+        <server>
+            <id>apache.snapshots.https</id>
+            <username>YOUR_APACHE_USERNAME</username>
+            <password>YOUR_APACHE_PASSWORD</password>
+        </server>
+        <server>
+            <id>apache.releases.https</id>
+            <username>YOUR_APACHE_USERNAME</username>
+            <password>YOUR_APACHE_PASSWORD</password>
+        </server>
+    </servers>
+    <profiles>
+        <profile>
+            <id>release</id>
+            <properties>
+                <gpg.passphrase>YOUR_SECRET_PHRASE</gpg.passphrase>
+            </properties>
+            </profile>
+        </profile>
+    </profiles>
+</settings>
+---------------------------
+
+Repeatable operations
+
+  These steps must be performed <<for each>> release.
+
+* Prepare the release tag
+
+  To prepare the release Subversion tag, check out the branch/trunk from where
+  you are preparing the release and type:
+
+-----------------------------------
+mvn release:prepare -Dusername=YOUR_SVN_USER -Dpassword=YOUR_SVN_PASSWORD
+-----------------------------------
+
+  The plugin interactively will ask you the version to release, the Subversion
+  tag to use and the next snapshot version. It is reccomended to use the tag:
+  <<tiles-X.X.X>>.
+
+* Perform the Release
+
+  To perform the release, i.e. creating and deploying Maven artifacts, use:
+
+-----------------------------------
+mvn release:perform
+-----------------------------------
+
+* Close the staging repository
+
+  Login to {{{https://repository.apache.org} Nexus repository}} using your Apache LDAP credentials.
+  Click on "Staging". Then click on "tiles" in the list of repositories.
+  In the panel below you should see an open repository that is linked to your username and ip.
+  Right click on this repository and select "Close".
+  This will close the repository from future deployments and make it available for others to view.
+  If you are staging multiple releases together, skip this step until you have staged everything.
+  Enter the name and version of the artifact being released in the "Description" field and then click "Close".
+  This will make it easier to identify it later.
+
+* Verify the staged artifacts
+
+  If you click on your repository, a tree view will appear below.
+  You can then browse the contents to ensure the artifacts are as you expect them.
+  Pay particular attention to the existence of *.asc (signature) files.
+  If the you don't like the content of the repository, right click your repository and choose "Drop".
+  You can then rollback your release and repeat the process.
+
+  Note the repository URL, you will need this in your vote email.
+
+* Digest and upload assemblies
+
+  * Go into the release assembly target directory:
+
+-----------------------------------
+cd target/checkout/assembly/target/assembly/out
+-----------------------------------
+
+  * Create MD5 and SHA1 files for each files (including ASC files). You can do
+  it with this simple shell script:
+
+-----------------------------------
+#!/bin/sh
+
+for fileitem in *
+do
+  openssl md5 < $fileitem > $fileitem.md5
+  openssl sha1 < $fileitem > $fileitem.sha1
+done
+-----------------------------------
+
+  * Upload everything to the build site:
+
+-----------------------------------
+scp * user@people.apache.org:/www/people.apache.org/builds/tiles/${version}
+-----------------------------------
+
+* Release the JIRA version
+
+  * In JIRA go to the version that you want to release and release it.
+
+  * Create a new version, if it has not been done before.
+
+  * Create the release notes and <<write down the link>> that it uses.
+
+* Send announcement for the test build
+
+  In <<developers mailing list>> send an announcement for the test build:
+
+-----------------------------------
+Subject: [ANNOUNCE] Tiles ${version} test build available
+
+The test build of Tiles ${version} is available.
+
+
+No determination as to the quality ('alpha,' 'beta,' or 'GA') of Tiles
+${version} has been made, and at this time it is simply a "test build". We
+welcome any comments you may have, and will take all feedback into
+account if a quality vote is called for this build.
+
+Release notes:
+
+* ${jira.release.notes}
+
+Distribution:
+
+ * http://people.apache.org/builds/tiles/${version}/
+
+Maven 2 staging repository:
+
+ * https://repository.apache.org/content/repositories/tiles-[YOUR REPOSITORY ID]/
+
+A vote regarding the quality of this test build will be initiated
+within the next couple of days.
+-----------------------------------
+
+* Call for a vote
+
+  A few days after the test build announcement, call for a vote in
+  <<developers mailing list>>.
+
+-----------------------------------
+Subject: [VOTE] ${version} Release Quality
+
+The Tiles ${version} test build has been available since ${testBuildDate}.
+
+Release notes:
+
+* ${jira.release.notes}
+
+Distribution:
+
+ * http://people.apache.org/builds/tiles/${version}/
+
+Maven 2 staging repository:
+
+ * https://repository.apache.org/content/repositories/tiles-[YOUR REPOSITORY ID]/
+
+If you have had a chance to review the test build, please respond with
+a vote on its quality:
+
+ [ ] Leave at test build
+ [ ] Alpha
+ [ ] Beta
+ [ ] General Availability (GA)
+
+
+Everyone who has tested the build is invited to vote. Votes by PMC
+members are considered binding. A vote passes if there are at least
+three binding +1s and more +1s than -1s.
+-----------------------------------
+
+* Post-vote operations
+
+  After a vote is finished, and it has been decided that is
+  <<at least of alpha quality>>, there is the need of a post-vote process.
+
+** Promote staged artifacts
+
+  Once the release is deemed fit for public consumption it can be transfered to a production repository where it will be available to all users.
+
+  Login to {{{https://repository.apache.org}Nexus repository}} again.
+  Click on "Staging" and then on the repository with id "tiles-staging".
+  Find your closed staging repository, right click on it and choose "Promote".
+  Select the "Releases" repository and click "Promote".
+
+  Next click on "Repositories", select the "Releases" repository
+  and validate that your artifacts exist as you expect them.
+
+** Move assemblies
+
+  * Move assemblies to the Apache distribution mirrors:
+
+-------------------------------------------
+ssh user@people.apache.org
+
+cd /www/people.apache.org/builds/tiles/${version}
+mkdir /www/www.apache.org/dist/tiles/v${version}/
+cp * /www/www.apache.org/dist/tiles/v${version}/
+-------------------------------------------
+
+** Update the site
+
+  * Wait 24 hours to let the mirror sync to the release and then update the
+  site. In particular you have to update the index and the download pages:
+
+------------------------------------------------
+https://svn.apache.org/repos/asf/tiles/site/src/site/xdoc/index.xml
+https://svn.apache.org/repos/asf/tiles/site/src/site/apt/download.apt
+------------------------------------------------
+
+  Build and publish the site:
+
+--------------------------------------
+mvn site
+mvn site:deploy
+--------------------------------------
+
+** Send announcement
+
+  Finally, send an an announcement to the <<users>> and <<developers mailing
+  list>>:
+
+--------------------------------------
+Subject: [ANNOUNCE] Tiles ${version} ${quality} released
+
+The Apache Tiles team is pleased to announce the release of Tiles ${version}
+${quality}.
+
+Tiles ${version} is available in a binary and a source distribution.
+
+http://tiles.apache.org/download.html
+
+It is also available in the central Maven repository under Group ID
+"org.apache.tiles".
+
+The 2.0.x series of the Apache Tiles framework has a minimum
+requirement of the following specification versions:
+
+* Java Servlet 2.4 and JavaServer Pages (JSP) 2.0
+* Java Standard Edition (Java SE) 1.5
+
+The release notes are available online at:
+
+* ${jira.release.notes}
+
+Please feel free to test the distribution and post your comments to
+the user list, or, if appropriate, file a ticket with JIRA.
+--------------------------------------
+
+  <<You have finished!>>
diff --git a/tiles-request/src/site/site.xml b/tiles-request/src/site/site.xml
new file mode 100644
index 0000000..15e8926
--- /dev/null
+++ b/tiles-request/src/site/site.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Request Microframework">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../index.html"/>
+            <item
+                   name="Tiles Request Microframework"
+                   href="./index.html"/>
+        </menu>
+        <menu name="Reference">
+            <item
+                    name="Javadoc"
+                    href="apidocs/index.html"/>
+        </menu>
+
+        <menu name="Developers">
+            <item
+                    name="Building"
+                    href="/dev/building.html"/>
+            <item
+                    name="Snapshots"
+                    href="/dev/snapshots.html"/>
+            <item
+                    name="Release Process"
+                    href="/dev/release.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-request/src/site/xdoc/dev/building.xml b/tiles-request/src/site/xdoc/dev/building.xml
new file mode 100644
index 0000000..92dceaa
--- /dev/null
+++ b/tiles-request/src/site/xdoc/dev/building.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<!--
+/*
+ * $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.
+ */
+-->
+<document>
+
+    <properties>
+        <title>Apache Request Microframework</title>
+    </properties>
+
+    <body>
+        <section name="Building the Request Microframework">
+          <subsection name="Prerequisites">
+            <p>For all the next instructions, we assume that you downloaded and
+            installed <a href="http://maven.apache.org/">Maven</a>.</p>
+            <p>To download packages from the source repository, you need to
+            download and install <a href="http://subversion.tigris.org/">
+            Subversion</a>.</p>
+            <p>If you want to build something including JavaDocs (assemblies,
+            sites and JavaDoc report itself) you need to install
+            <a href="http://www.graphviz.org/">GraphViz</a>, otherwise you will
+            notice missing pictures inside JavaDocs pages.</p>
+          </subsection>
+          <subsection name="Building main packages">
+            <p>To build the Request Microframework from source you need to:</p>
+            <ul>
+            <li><p><a href="../../download.html">download</a> the source
+            distribution, or checkout the latest version:</p>
+            <p><source>svn co http://svn.apache.org/repos/asf/tiles/framework/trunk/tiles-request</source></p></li>
+            <li><p>go into the source directory and type:</p>
+            <p><source>mvn package</source></p></li>
+            </ul>
+            <p>You will find the generated JARs under:</p>
+            <ul>
+            <li>{tiles-dir}/tiles-request-api/target/tiles-request-api-${version}.jar</li>
+            <li>{tiles-dir}/tiles-request-{implementation}/target/tiles-request-{implementation}-${version}.jar</li>
+            </ul>
+          </subsection>
+        </section>
+        <section name="Building the websites">
+          <p>There are four Tiles websites: the main website and the projects
+          websites (tiles-request, tiles-autotag and framework).</p>
+          <subsection name="Building the main website">
+            <p>To build the main website:</p>
+            <ul>
+            <li><p>checkout the site from the source repository:</p>
+            <p><source>svn co http://svn.apache.org/repos/asf/tiles/site/</source></p></li>
+            <li><p>go into the site directory and type:</p>
+            <p><source>mvn site</source></p></li>
+            </ul>
+            <p>You will find the generated distribution under
+            <code>{tiles-site-dir}/target/site</code>.</p>
+          </subsection>
+          <subsection name="Building the tiles-request website">
+            <p>To build a project's website:</p>
+            <ul>
+            <li><p><a href="../download.html">download</a> the source
+            distribution, or checkout the latest version:</p>
+            <p><source>svn co http://svn.apache.org/repos/asf/tiles/framework/trunk/tiles-request</source></p></li>
+            <li><p>go into the source directory and type:</p>
+            <p><source>mvn site site:stage</source></p></li>
+            </ul>
+            <p>You will find the generated website under:</p>
+            <ul>
+            <li>{tiles-request-dir}/target/staging</li>
+            </ul>
+          </subsection>
+        </section>
+    </body>
+
+</document>
diff --git a/tiles-request/src/site/xdoc/dev/snapshots.xml b/tiles-request/src/site/xdoc/dev/snapshots.xml
new file mode 100644
index 0000000..adfed4f
--- /dev/null
+++ b/tiles-request/src/site/xdoc/dev/snapshots.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<!--
+/*
+ * $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.
+ */
+-->
+<document>
+
+    <properties>
+        <title>Apache Tiles</title>
+    </properties>
+
+    <body>
+        <section name="Apache Tiles&#8482;">
+        <subsection name="Snapshots">
+
+        <p>Looking for snapshot builds of the latest version of Tiles?
+           Snapshots are occasionally published to Apache's Maven snapshot
+           repository, which can be accessed with the following configuration:</p>
+
+        <source>
+        <![CDATA[
+          <repository>
+              <id>apache.snapshots</id>
+              <name>Apache Maven Snapshot Repository</name>
+              <url>http://repository.apache.org/snapshots</url>
+          </repository>
+        ]]>
+        </source>
+
+        <p>After configuring the repository, declare a dependency on
+        Tiles Request:</p>
+
+        <source><![CDATA[
+          <dependency>
+              <groupId>org.apache.tiles</groupId>
+              <artifactId>tiles-request-api</artifactId>
+              <version>1.0-SNAPSHOT</version>
+          </dependency>
+        ]]>
+        </source>
+
+    </subsection>
+</section>
+</body>
+
+</document>
diff --git a/tiles-request/src/site/xdoc/index.xml b/tiles-request/src/site/xdoc/index.xml
new file mode 100644
index 0000000..178737c
--- /dev/null
+++ b/tiles-request/src/site/xdoc/index.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+/*
+ * $Id: index.xml 1162124 2011-08-26 14:16:13Z mck $
+ *
+ * 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.
+ */
+-->
+<document>
+
+    <properties>
+        <title>Home</title>
+    </properties>
+
+    <body>
+
+        <section name="Apache Request microframework project">
+
+            <p>Tiles has served the purpose of rendering views for the Model-View-Controller pattern in JavaEE for years.</p>
+            <p>Tiles-3 introduces a complete standalone and super simple abstraction around request/response objects.
+            <p>We've all seen that the various technologies like Servlet, Portlet, JSP, Velocity, FreeMarker seem to have a different abstraction of a common pattern.
+            They all have a concept of:</p>
+            <ul>
+                <li>request: the client request, or some sort of it;</li>
+                <li>response: the response to send to the client, or to the caller, with a writer/output stream to write into;</li>
+                <li>attributes: usually maps (or map-like structures) with string keys and object values;</li>
+                <li>scope: places where different attribute maps are stored (request, session, etc.)</li>
+            </ul>
+            <p>Now all of these scopes are concentrated into "Request" interfaces, that acts as a single point of reference for the developer.
+            In theory, everything you need for rendering a view can be obtained by an object that implements Request, and Request implementations can be built that do not rely on an application server environment.</p>
+            <p> </p>
+            <p>Currently there are bindings for Servlet, Portlet, JSP, Mustache, Velocity and FreeMarker.</p></p>
+
+        </section>
+    </body>
+
+</document>
diff --git a/tiles-request/tiles-request-api/pom.xml b/tiles-request/tiles-request-api/pom.xml
new file mode 100644
index 0000000..8da0917
--- /dev/null
+++ b/tiles-request/tiles-request-api/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <artifactId>tiles-request</artifactId>
+        <groupId>org.apache.tiles</groupId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <groupId>org.apache.tiles</groupId>
+    <artifactId>tiles-request-api</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <name>Tiles request - API</name>
+    <description>API for the Tiles Request framework.</description>
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-jdk14</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymockclassextension</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/AbstractClientRequest.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/AbstractClientRequest.java
new file mode 100644
index 0000000..ccec97b
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/AbstractClientRequest.java
@@ -0,0 +1,97 @@
+/*
+ * $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.request;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Base class for "client" requests, i.e. requests that come unchanged by the
+ * container, such as ServletRequest and PortletRequest.
+ *
+ * @version $Rev$ $Date$
+ */
+public abstract class AbstractClientRequest extends AbstractRequest {
+
+    /**
+     * The application context.
+     */
+    private ApplicationContext applicationContext;
+
+    /**
+     * Constructor.
+     *
+     * @param applicationContext The application context.
+     */
+    public AbstractClientRequest(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+    }
+
+
+    @Override
+    public void dispatch(String path) throws IOException {
+        if (isForceInclude()) {
+            doInclude(path);
+        } else {
+            setForceInclude(true);
+            doForward(path);
+        }
+    }
+
+    @Override
+    public void include(String path) throws IOException {
+        setForceInclude(true);
+        doInclude(path);
+    }
+
+    @Override
+    public ApplicationContext getApplicationContext() {
+        return applicationContext;
+    }
+
+    /**
+     * Returns the application scope.
+     *
+     * @return The application scope.
+     */
+    public Map<String, Object> getApplicationScope() {
+        return applicationContext.getApplicationScope();
+    }
+
+    /**
+     * Forwards to a path.
+     *
+     * @param path The path to forward to.
+     * @throws IOException If something goes wrong when forwarding.
+     */
+    protected abstract void doForward(String path) throws IOException;
+
+    /**
+     * Includes the result of a path.
+     *
+     * @param path The path to forward to.
+     * @throws IOException If something goes wrong when forwarding.
+     */
+    protected abstract void doInclude(String path) throws IOException;
+
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/AbstractRequest.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/AbstractRequest.java
new file mode 100644
index 0000000..a850f94
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/AbstractRequest.java
@@ -0,0 +1,61 @@
+/*
+ * $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.request;
+
+/**
+ * Base request.
+ *
+ * @version $Rev$ $Date: 2010-11-14 21:32:50 +0100 (dom, 14 nov 2010)$
+ */
+public abstract class AbstractRequest implements DispatchRequest {
+
+    /**
+     * Name of the attribute used to store the force-include option.
+     *
+     */
+    public static final String FORCE_INCLUDE_ATTRIBUTE_NAME = AbstractRequest.class
+            .getName() + ".FORCE_INCLUDE";
+
+    /**
+     * Sets the flag to force inclusion at next dispatch.
+     *
+     * @param forceInclude <code>true</code> means that, at the next dispatch, response
+     * will be included and never forwarded.
+     */
+    protected void setForceInclude(boolean forceInclude) {
+        getContext("request").put(FORCE_INCLUDE_ATTRIBUTE_NAME, forceInclude);
+    }
+
+    /**
+     * Checks if, when dispatching to a resource, the result must be included
+     * and not forwarded to.
+     *
+     * @return <code>true</code> if inclusion is forced.
+     */
+    protected boolean isForceInclude() {
+        Boolean forceInclude = (Boolean) getContext("request").get(
+                FORCE_INCLUDE_ATTRIBUTE_NAME);
+        if (forceInclude != null) {
+            return forceInclude;
+        }
+        return false;
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/AbstractViewRequest.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/AbstractViewRequest.java
new file mode 100644
index 0000000..b3282d9
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/AbstractViewRequest.java
@@ -0,0 +1,65 @@
+/*
+ * $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.request;
+
+import java.io.IOException;
+
+
+/**
+ * Base class for "view" requests, i.e. requests created into view technologies,
+ * such as JSP, Velocity and Freemarker. In particular, all calls to
+ * {@link #dispatch(String)} will cause an inclusion and never a forward.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AbstractViewRequest extends DispatchRequestWrapper {
+
+    /**
+     * Constructor.
+     *
+     * @param request The base request.
+     */
+    public AbstractViewRequest(DispatchRequest request) {
+        super(request);
+    }
+
+    @Override
+    public void dispatch(String path) throws IOException {
+        setForceInclude(true);
+        doInclude(path);
+    }
+
+    @Override
+    public void include(String path) throws IOException {
+        setForceInclude(true);
+        doInclude(path);
+    }
+
+    /**
+     * Includes the result. By default, uses the wrapped request for the inclusion.
+     *
+     * @param path The path whose result will be included.
+     * @throws IOException If something goes wrong.
+     */
+    protected void doInclude(String path) throws IOException {
+        getWrappedRequest().include(path);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationAccess.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationAccess.java
new file mode 100644
index 0000000..260e791
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationAccess.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.request;
+
+
+/**
+ * Entry point to get information about the application.
+ *
+ * @version $Rev: 1064782 $ $Date: 2011-01-28 18:08:52 +0100 (Fri, 28 Jan 2011) $
+ */
+public final class ApplicationAccess {
+
+    /**
+     * The attribute name that will be used to store the application context itself.
+     */
+    public static final String APPLICATION_CONTEXT_ATTRIBUTE =
+        ApplicationContext.class.getName() + ".ATTRIBUTE";
+
+    /**
+     * Constructor.
+     */
+    private ApplicationAccess() {
+    }
+
+    /**
+     * Registers an application context. It will be registered into itself as an
+     * attribute, using the {@link #APPLICATION_CONTEXT_ATTRIBUTE} name.
+     *
+     * @param applicationContext The application context to register.
+     */
+    public static void register(ApplicationContext applicationContext) {
+        applicationContext.getApplicationScope().put(
+                APPLICATION_CONTEXT_ATTRIBUTE, applicationContext);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationContext.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationContext.java
new file mode 100644
index 0000000..24c236f
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationContext.java
@@ -0,0 +1,83 @@
+/*
+ * $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.request;
+
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Defines a set of methods which tiles use to communicate to
+ * the tiles container and runtime environment.  There is only
+ * one application context per container.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface ApplicationContext {
+
+    /**
+     * Returns the original, technology-dependent, context.
+     *
+     * @return The original application context.
+     */
+    Object getContext();
+
+    /**
+     * Returns a mutable Map that maps application scope attribute names to
+     * their values.
+     *
+     * @return Map of key value pairs.
+     */
+    Map<String, Object> getApplicationScope();
+
+    /**
+     * Return an immutable Map that maps context application initialization
+     * parameters to their values.
+     *
+     * @return initialization parameters
+     */
+    Map<String, String> getInitParams();
+
+    /**
+     * Return the application resource mapped to the specified path.
+     *
+     * @param localePath path to the desired resource, including the Locale suffix.
+     * @return the first located resource which matches the given path or null if no such resource exists.
+     */
+    ApplicationResource getResource(String localePath);
+
+    /**
+     * Return a localized version of an ApplicationResource.
+     *
+     * @param base the ApplicationResource.
+     * @param locale the desired Locale.
+     * @return the first located resource which matches the given path or null if no such resource exists.
+     */
+    ApplicationResource getResource(ApplicationResource base, Locale locale);
+
+    /**
+     * Return the application resources mapped to the specified path.
+     *
+     * @param path to the desired resource.
+     * @return all resources which match the given path.
+     */
+    Collection<ApplicationResource> getResources(String path);
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationContextAware.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationContextAware.java
new file mode 100644
index 0000000..1526ffd
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationContextAware.java
@@ -0,0 +1,38 @@
+/*
+ * $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.request;
+
+
+/**
+ * It represents an object that can have a reference to the
+ * {@link ApplicationContext}.
+ *
+ * @version $Rev: 893303 $ $Date: 2009-12-22 21:18:35 +0100 (Tue, 22 Dec 2009) $
+ */
+public interface ApplicationContextAware {
+
+    /**
+     * Sets the Tiles application context.
+     *
+     * @param applicationContext The Tiles application context.
+     */
+    void setApplicationContext(ApplicationContext applicationContext);
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationContextWrapper.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationContextWrapper.java
new file mode 100644
index 0000000..3696f97
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationContextWrapper.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.request;
+
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+
+
+/**
+ * Delegate for ease of customization.
+ *
+ * @version $Rev: 933750 $ $Date: 2010-04-13 21:16:06 +0200 (Tue, 13 Apr 2010) $
+ */
+public class ApplicationContextWrapper implements ApplicationContext {
+
+    /**
+     * The original context.
+     */
+    private ApplicationContext context;
+
+    /**
+     * Constructor.
+     *
+     * @param context The original context.
+     */
+    public ApplicationContextWrapper(ApplicationContext context) {
+        this.context = context;
+    }
+
+    /**
+     * Returns the wrapped application context.
+     *
+     * @return The wrapped application context.
+     */
+    public ApplicationContext getWrappedApplicationContext() {
+        return context;
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, Object> getApplicationScope() {
+        return context.getApplicationScope();
+    }
+
+    /** {@inheritDoc} */
+    public Object getContext() {
+        return context.getContext();
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String> getInitParams() {
+        return context.getInitParams();
+    }
+
+    /** {@inheritDoc} */
+    public ApplicationResource getResource(String localePath) {
+        return context.getResource(localePath);
+    }
+
+    /** {@inheritDoc} */
+    public ApplicationResource getResource(ApplicationResource base, Locale locale) {
+        return context.getResource(base, locale);
+    }
+
+    /** {@inheritDoc} */
+    public Collection<ApplicationResource> getResources(String path) {
+        return context.getResources(path);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationResource.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationResource.java
new file mode 100644
index 0000000..6f5c05b
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/ApplicationResource.java
@@ -0,0 +1,84 @@
+/*
+ * $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.request;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+
+/**
+ * A (localized) resource accessible through the ApplicationContext.
+ * Typically this is a file inside the web application's war.
+ * 
+ * @version $Rev$ $Date$
+ */
+public interface ApplicationResource {
+
+    /**
+     * Get the path name for this resource.
+     * You can access this ressource by passing the path to 
+     * {@link ApplicationContext#getResource(String) getResource}.
+     * 
+     * @return the path including localization.
+     */
+    String getLocalePath();
+
+    /**
+     * Get the path name for this resource. Multiple versions of 
+     * a resource can share the same path if the locale part is different.
+     * 
+     * @return the path excluding localization.
+     */
+    String getPath();
+
+    /**
+     * Get the Locale for this resource.
+     * 
+     * @return the Locale.
+     */
+    Locale getLocale();
+
+    /**
+     * Get the path name of another version of the resource.
+     * 
+     * @param locale the Locale for the new version.
+     * @return the path including localization.
+     */
+    String getLocalePath(Locale locale);
+
+    /**
+     * Get a java.io.InputStream to read the contents of this resource.
+     * 
+     * @return the InputStream.
+     * @throws IOException if the contents cannot be read.
+     */
+    InputStream getInputStream() throws IOException;
+
+    /**
+     * Get the last modification date for this resource.
+     * 
+     * @return the difference, measured in milliseconds, between the current 
+     * time and midnight, January 1, 1970 UTC.
+     * @throws IOException if the last modification date cannot be found.
+     */
+    long getLastModified() throws IOException;
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/DefaultRequestWrapper.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/DefaultRequestWrapper.java
new file mode 100644
index 0000000..a1ff9f1
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/DefaultRequestWrapper.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.request;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.tiles.request.attribute.Addable;
+
+/**
+ * Delegate for ease of customization.
+ *
+ * @version $Rev: 1215009 $ $Date: 2011-12-16 01:32:31 +0100 (Fri, 16 Dec 2011) $
+ */
+public class DefaultRequestWrapper implements RequestWrapper {
+
+    /**
+     * The wrapper request context object.
+     */
+    private Request context;
+
+    /**
+     * Constructor.
+     *
+     * @param context
+     *            The request context to wrap.
+     */
+    public DefaultRequestWrapper(Request context) {
+        this.context = context;
+    }
+
+    /** {@inheritDoc} */
+    public Request getWrappedRequest() {
+        return context;
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String> getHeader() {
+        return context.getHeader();
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String[]> getHeaderValues() {
+        return context.getHeaderValues();
+    }
+
+    /** {@inheritDoc} */
+    public Addable<String> getResponseHeaders() {
+        return context.getResponseHeaders();
+    }
+
+    /** {@inheritDoc} */
+    public ApplicationContext getApplicationContext() {
+        return context.getApplicationContext();
+    }
+
+    /** {@inheritDoc} */
+    public OutputStream getOutputStream() throws IOException {
+        return context.getOutputStream();
+    }
+
+    /** {@inheritDoc} */
+    public Writer getWriter() throws IOException {
+        return context.getWriter();
+    }
+
+    /** {@inheritDoc} */
+    public PrintWriter getPrintWriter() throws IOException {
+        return context.getPrintWriter();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isResponseCommitted() {
+        return context.isResponseCommitted();
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String> getParam() {
+        return context.getParam();
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String[]> getParamValues() {
+        return context.getParamValues();
+    }
+
+    /** {@inheritDoc} */
+    public Locale getRequestLocale() {
+        return context.getRequestLocale();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isUserInRole(String role) {
+        return context.isUserInRole(role);
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, Object> getContext(String scope) {
+        return context.getContext(scope);
+    }
+
+    /** {@inheritDoc} */
+    public List<String> getAvailableScopes() {
+        return context.getAvailableScopes();
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/DispatchRequest.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/DispatchRequest.java
new file mode 100644
index 0000000..26141c6
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/DispatchRequest.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.request;
+
+import java.io.IOException;
+
+
+/**
+ * Encapsulation of request information.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface DispatchRequest extends Request {
+
+    /**
+     * Dispatches the request to a specified path.
+     *
+     * @param path The path to dispatch to.
+     * @throws IOException If something goes wrong during dispatching.
+     */
+    void dispatch(String path) throws IOException;
+
+    /**
+     * Includes the response from the specified URL in the current response output.
+     *
+     * @param path The path to include.
+     * @throws IOException If something goes wrong during inclusion.
+     */
+    void include(String path) throws IOException;
+
+    /**
+     * Sets the content type when rendering the result.
+     *
+     * @param contentType The content type. It should follow the specifications
+     * from W3C about content types.
+     */
+    void setContentType(String contentType);
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/DispatchRequestWrapper.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/DispatchRequestWrapper.java
new file mode 100644
index 0000000..e118f1f
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/DispatchRequestWrapper.java
@@ -0,0 +1,145 @@
+/*
+ * $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.request;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.tiles.request.attribute.Addable;
+
+/**
+ * Delegate for ease of customization.
+ *
+ * @version $Rev: 1215009 $ $Date: 2011-12-16 01:32:31 +0100 (Fri, 16 Dec 2011) $
+ */
+public class DispatchRequestWrapper extends AbstractRequest implements
+        RequestWrapper {
+
+    /**
+     * The wrapper request context object.
+     */
+    private DispatchRequest context;
+
+    /**
+     * Constructor.
+     *
+     * @param context The request context to wrap.
+     */
+    public DispatchRequestWrapper(DispatchRequest context) {
+        this.context = context;
+    }
+
+    /** {@inheritDoc} */
+    public DispatchRequest getWrappedRequest() {
+        return context;
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String> getHeader() {
+        return context.getHeader();
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String[]> getHeaderValues() {
+        return context.getHeaderValues();
+    }
+
+    /** {@inheritDoc} */
+    public Addable<String> getResponseHeaders() {
+        return context.getResponseHeaders();
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, Object> getContext(String scope) {
+        return context.getContext(scope);
+    }
+
+    /** {@inheritDoc} */
+    public List<String> getAvailableScopes() {
+        return context.getAvailableScopes();
+    }
+
+    /** {@inheritDoc} */
+    public ApplicationContext getApplicationContext() {
+        return context.getApplicationContext();
+    }
+
+    /** {@inheritDoc} */
+    public void dispatch(String path) throws IOException {
+        context.dispatch(path);
+    }
+
+    /** {@inheritDoc} */
+    public void include(String path) throws IOException {
+        context.include(path);
+    }
+
+    /** {@inheritDoc} */
+    public OutputStream getOutputStream() throws IOException {
+        return context.getOutputStream();
+    }
+
+    /** {@inheritDoc} */
+    public Writer getWriter() throws IOException {
+        return context.getWriter();
+    }
+
+    /** {@inheritDoc} */
+    public PrintWriter getPrintWriter() throws IOException {
+        return context.getPrintWriter();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isResponseCommitted() {
+        return context.isResponseCommitted();
+    }
+
+    /** {@inheritDoc} */
+    public void setContentType(String contentType) {
+        context.setContentType(contentType);
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String> getParam() {
+        return context.getParam();
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String[]> getParamValues() {
+        return context.getParamValues();
+    }
+
+    /** {@inheritDoc} */
+    public Locale getRequestLocale() {
+        return context.getRequestLocale();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isUserInRole(String role) {
+        return context.isUserInRole(role);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/NotAvailableFeatureException.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/NotAvailableFeatureException.java
new file mode 100644
index 0000000..3484c42
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/NotAvailableFeatureException.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.request;
+
+/**
+ * Exception that indicates that a feature could not be used since it is not
+ * available.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NotAvailableFeatureException extends RequestException {
+
+    /**
+     * Constructor.
+     *
+     */
+    public NotAvailableFeatureException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     */
+    public NotAvailableFeatureException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param e The cause to be wrapped.
+     */
+    public NotAvailableFeatureException(Throwable e) {
+        super(e);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @param e The cause to be wrapped.
+     */
+    public NotAvailableFeatureException(String message, Throwable e) {
+        super(message, e);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/Request.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/Request.java
new file mode 100644
index 0000000..08e4a59
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/Request.java
@@ -0,0 +1,153 @@
+/*
+ * $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.request;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.tiles.request.attribute.Addable;
+
+
+/**
+ * Encapsulation of request information.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface Request {
+
+    /**
+     * Return an immutable Map that maps header names to the first (or only)
+     * header value (as a String).
+     *
+     * @return The header map.
+     */
+    Map<String, String> getHeader();
+
+    /**
+     * Return an immutable Map that maps header names to the set of all values
+     * specified in the request (as a String array). Header names must be
+     * matched in a case-insensitive manner.
+     *
+     * @return The header values map.
+     */
+    Map<String, String[]> getHeaderValues();
+
+    /**
+     * Return an Addable object that can be used to write headers to the response.
+     *
+     * @return An Addable object.
+     */
+    Addable<String> getResponseHeaders();
+
+    /**
+     * Returns a context map, given the scope name.
+     *
+     * @param scope The name of the scope.
+     * @return The context.
+     */
+    Map<String, Object> getContext(String scope);
+
+    /**
+     * Returns all available scopes, that are the ones returned by
+     * {@link #getNativeScopes()} plus derivative scopes (e.g. flash scope).
+     *
+     * @return All the available scopes.
+     */
+    List<String> getAvailableScopes();
+
+    /**
+     * Returns the associated application context.
+     *
+     * @return The application context associated to this request.
+     */
+    ApplicationContext getApplicationContext();
+
+    /**
+     * Returns an output stream to be used to write directly in the response.
+     *
+     * @return The output stream that writes in the response.
+     * @throws IOException If something goes wrong when getting the output stream.
+     */
+    OutputStream getOutputStream() throws IOException;
+
+    /**
+     * Returns a writer to be used to write directly in the response.
+     *
+     * @return The writer that writes in the response.
+     * @throws IOException If something goes wrong when getting the writer.
+     */
+    Writer getWriter() throws IOException;
+
+    /**
+     * Returns a print writer to be used to write directly in the response.
+     *
+     * @return The print writer that writes in the response.
+     * @throws IOException If something goes wrong when getting the print
+     * writer.
+     */
+    PrintWriter getPrintWriter() throws IOException;
+
+    /**
+     * Checks if the response has been committed.
+     *
+     * @return <code>true</code> only if the response has been committed.
+     */
+    boolean isResponseCommitted();
+
+    /**
+     * Return an immutable Map that maps request parameter names to the first
+     * (or only) value (as a String).
+     *
+     * @return The parameter map.
+     */
+    Map<String, String> getParam();
+
+    /**
+     * Return an immutable Map that maps request parameter names to the set of
+     * all values (as a String array).
+     *
+     * @return The parameter values map.
+     */
+    Map<String, String[]> getParamValues();
+
+    /**
+     * Return the preferred Locale in which the client will accept content.
+     *
+     * @return The current request locale. It is the locale of the request
+     * object itself and it is NOT the locale that the user wants to use. See
+     * {@link org.apache.tiles.locale.LocaleResolver} to implement strategies to
+     * resolve locales.
+     */
+    Locale getRequestLocale();
+
+    /**
+     * Determine whether or not the specified user is in the given role.
+     * @param role the role to check against.
+     * @return <code>true</code> if the user is in the given role.
+     */
+    boolean isUserInRole(String role);
+
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/RequestException.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/RequestException.java
new file mode 100644
index 0000000..430937e
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/RequestException.java
@@ -0,0 +1,64 @@
+/*
+ * $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.request;
+
+/**
+ * Thrown when something related to a request fails.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RequestException extends RuntimeException {
+
+    /**
+     * Constructor.
+     */
+    public RequestException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message of the exception.
+     */
+    public RequestException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param cause The cause.
+     */
+    public RequestException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message of the exception.
+     * @param cause The cause.
+     */
+    public RequestException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/RequestWrapper.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/RequestWrapper.java
new file mode 100644
index 0000000..d82db8c
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/RequestWrapper.java
@@ -0,0 +1,38 @@
+/*
+ * $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.request;
+
+
+/**
+ * Delegate for ease of customization.
+ *
+ * @version $Rev: 1215002 $ $Date: 2011-12-16 01:27:17 +0100 (Fri, 16 Dec 2011) $
+ */
+public interface RequestWrapper extends Request {
+
+    /**
+     * Returns the wrapped Tiles request.
+     *
+     * @return The wrapped Tiles request.
+     */
+    Request getWrappedRequest();
+
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/Addable.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/Addable.java
new file mode 100644
index 0000000..5e8b9a9
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/Addable.java
@@ -0,0 +1,38 @@
+/*
+ * $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.request.attribute;
+
+/**
+ * Allows to get and set attributes.
+ *
+ * @version $Rev$ $Date$
+ * @param <V> The type of the value of the attribute.
+ */
+public interface Addable<V> {
+
+    /**
+     * Sets a value for the given key.
+     *
+     * @param key The key of the attribute.
+     * @param value The value of the attribute.
+     */
+    void setValue(String key, V value);
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/AttributeExtractor.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/AttributeExtractor.java
new file mode 100644
index 0000000..5c313ac
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/AttributeExtractor.java
@@ -0,0 +1,29 @@
+/*
+ * $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.request.attribute;
+
+/**
+ * Allows to get, put and remove Object-type attributes.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface AttributeExtractor extends HasRemovableKeys<Object>, HasAddableKeys<Object> {
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/EnumeratedValuesExtractor.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/EnumeratedValuesExtractor.java
new file mode 100644
index 0000000..88ae480
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/EnumeratedValuesExtractor.java
@@ -0,0 +1,39 @@
+/*
+ * $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.request.attribute;
+
+import java.util.Enumeration;
+
+/**
+ * Allows to get values from multi-valued attributes.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface EnumeratedValuesExtractor extends HasAddableKeys<String> {
+
+    /**
+     * Returns the values stored at the given key.
+     *
+     * @param key The key of the attribute.
+     * @return The values of the attribute.
+     */
+    Enumeration<String> getValues(String key);
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/HasAddableKeys.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/HasAddableKeys.java
new file mode 100644
index 0000000..115fd2b
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/HasAddableKeys.java
@@ -0,0 +1,30 @@
+/*
+ * $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.request.attribute;
+
+/**
+ * Allows to get and set attributes.
+ *
+ * @version $Rev$ $Date$
+ * @param <V> The type of the value of the attribute.
+ */
+public interface HasAddableKeys<V> extends HasKeys<V>, Addable<V> {
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/HasKeys.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/HasKeys.java
new file mode 100644
index 0000000..32ea92c
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/HasKeys.java
@@ -0,0 +1,47 @@
+/*
+ * $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.request.attribute;
+
+import java.util.Enumeration;
+
+/**
+ * Allows to get attributes.
+ *
+ * @version $Rev$ $Date$
+ * @param <V> The type of the value of the attribute.
+ */
+public interface HasKeys<V> {
+
+    /**
+     * The enumeration of the keys of the stored attributes.
+     *
+     * @return The keys.
+     */
+    Enumeration<String> getKeys();
+
+    /**
+     * Returns the value of the attribute with the given key.
+     *
+     * @param key The key of the attribute.
+     * @return The value.
+     */
+    V getValue(String key);
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/HasRemovableKeys.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/HasRemovableKeys.java
new file mode 100644
index 0000000..160737f
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/HasRemovableKeys.java
@@ -0,0 +1,37 @@
+/*
+ * $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.request.attribute;
+
+/**
+ * Allows to get and remove attributes.
+ *
+ * @version $Rev$ $Date$
+ * @param <V> The type of the value of the attribute.
+ */
+public interface HasRemovableKeys<V> extends HasKeys<V> {
+
+    /**
+     * Removes an attribute.
+     *
+     * @param key The key of the attribute to remove.
+     */
+    void removeValue(String key);
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/package-info.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/package-info.java
new file mode 100644
index 0000000..5b70eee
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/attribute/package-info.java
@@ -0,0 +1,28 @@
+/*
+ * $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.
+ */
+/**
+ * Methods to get, set and remove attributes from different sources (like HTTP requests 
+ * or sessions).
+ * By implementing {@link org.apache.tiles.request.attribute.AttributeExtractor},
+ * you can use a Map from {@link org.apache.tiles.request.collection} to manipulate the
+ * attributes of another class.
+ */
+package org.apache.tiles.request.attribute;
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/AddableParameterMap.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/AddableParameterMap.java
new file mode 100644
index 0000000..c6813fa
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/AddableParameterMap.java
@@ -0,0 +1,93 @@
+/*
+ * $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.request.collection;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tiles.request.attribute.HasAddableKeys;
+
+/**
+ * Exposes an {@link HasAddableKeys} object as a put/get (no remove) map.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AddableParameterMap extends ReadOnlyEnumerationMap<String> {
+
+    /**
+     * The request.
+     */
+    private HasAddableKeys<String> request;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request object to use.
+     */
+    public AddableParameterMap(HasAddableKeys<String> request) {
+        super(request);
+        this.request = request;
+    }
+
+    /** {@inheritDoc} */
+    public Set<Map.Entry<String, String>> entrySet() {
+        return new AddableParameterEntrySet();
+    }
+
+    /** {@inheritDoc} */
+    public String put(String key, String value) {
+        String oldValue = request.getValue(key);
+        request.setValue(key, value);
+        return oldValue;
+    }
+
+
+    /** {@inheritDoc} */
+    public void putAll(Map<? extends String, ? extends String> map) {
+        for (Map.Entry<? extends String, ? extends String> entry : map
+                .entrySet()) {
+            request.setValue(entry.getKey(), entry.getValue());
+        }
+    }
+
+
+    /**
+     * Entry set implementation for {@link AddableParameterMap}.
+     */
+    private class AddableParameterEntrySet extends ReadOnlyEnumerationMap<String>.ReadOnlyEnumerationMapEntrySet {
+
+        @Override
+        public boolean add(java.util.Map.Entry<String, String> e) {
+            request.setValue(e.getKey(), e.getValue());
+            return true;
+        }
+
+        @Override
+        public boolean addAll(
+                Collection<? extends java.util.Map.Entry<String, String>> c) {
+            for (Map.Entry<String, String> entry : c) {
+                request.setValue(entry.getKey(), entry.getValue());
+            }
+            return true;
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/CollectionUtil.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/CollectionUtil.java
new file mode 100644
index 0000000..5d68e23
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/CollectionUtil.java
@@ -0,0 +1,70 @@
+/*
+ * $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.request.collection;
+
+import java.util.Enumeration;
+
+/**
+ * Utilities for requests.
+ *
+ * @version $Rev: 1064782 $ $Date: 2011-01-28 18:08:52 +0100 (Fri, 28 Jan 2011) $
+ */
+public final class CollectionUtil {
+
+    /**
+     * Constructor.
+     */
+    private CollectionUtil() {
+
+    }
+
+    /**
+     * Returns the string representation of the key.
+     *
+     * @param key The key.
+     * @return The string representation of the key.
+     * @throws IllegalArgumentException If the key is <code>null</code>.
+     */
+    public static String key(Object key) {
+        if (key == null) {
+            throw new IllegalArgumentException();
+        } else if (key instanceof String) {
+            return ((String) key);
+        } else {
+            return (key.toString());
+        }
+    }
+
+    /**
+     * Returns the number of elements in an enumeration, by iterating it.
+     *
+     * @param keys The enumeration.
+     * @return The number of elements.
+     */
+    public static int enumerationSize(Enumeration<?> keys) {
+        int n = 0;
+        while (keys.hasMoreElements()) {
+            keys.nextElement();
+            n++;
+        }
+        return n;
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/HeaderValuesMap.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/HeaderValuesMap.java
new file mode 100644
index 0000000..3a0f8d2
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/HeaderValuesMap.java
@@ -0,0 +1,525 @@
+/*
+ * $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.request.collection;
+
+import static org.apache.tiles.request.collection.CollectionUtil.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tiles.request.attribute.EnumeratedValuesExtractor;
+
+
+/**
+ * Exposes an {@link EnumeratedValuesExtractor} object as a read-only map.
+ *
+ * @version $Rev$ $Date$
+ */
+public class HeaderValuesMap implements Map<String, String[]> {
+
+    /**
+     * The request.
+     */
+    private EnumeratedValuesExtractor request;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request object to use.
+     */
+    public HeaderValuesMap(EnumeratedValuesExtractor request) {
+        this.request = request;
+    }
+
+
+    /** {@inheritDoc} */
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /** {@inheritDoc} */
+    public boolean containsKey(Object key) {
+        return (request.getValue(key(key)) != null);
+    }
+
+
+    /** {@inheritDoc} */
+    public boolean containsValue(Object value) {
+        if (!(value instanceof String[])) {
+            return (false);
+        }
+        String[] test = (String[]) value;
+        Enumeration<String> names = request.getKeys();
+        while (names.hasMoreElements()) {
+            String name = names.nextElement();
+            if (compareHeaders(name, array2set(test))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /** {@inheritDoc} */
+    public Set<Map.Entry<String, String[]>> entrySet() {
+        return new HeadersEntrySet();
+    }
+
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object o) {
+        EnumeratedValuesExtractor otherRequest = ((HeaderValuesMap) o).request;
+        boolean retValue = true;
+        for (Enumeration<String> attribs = request.getKeys(); attribs
+                .hasMoreElements()
+                && retValue;) {
+            String parameterName = attribs.nextElement();
+            Set<String> valueSet = enumeration2set(otherRequest.getValues(parameterName));
+            retValue = compareHeaders(parameterName, valueSet);
+        }
+
+        return retValue;
+    }
+
+
+    /** {@inheritDoc} */
+    public String[] get(Object key) {
+        return getHeaderValues(key(key));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        int retValue = 0;
+        for (Enumeration<String> attribs = request.getKeys(); attribs
+                .hasMoreElements();) {
+            String parameterName = attribs.nextElement();
+            Enumeration<String> values = request.getValues(parameterName);
+            int valueHash = 0;
+            while (values.hasMoreElements()) {
+                valueHash += values.nextElement().hashCode();
+            }
+            retValue += parameterName.hashCode() ^ valueHash;
+        }
+        return retValue;
+    }
+
+
+    /** {@inheritDoc} */
+    public boolean isEmpty() {
+        return !request.getKeys().hasMoreElements();
+    }
+
+
+    /** {@inheritDoc} */
+    public Set<String> keySet() {
+        return new KeySet(request);
+    }
+
+
+    /** {@inheritDoc} */
+    public String[] put(String key, String[] value) {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /** {@inheritDoc} */
+    public void putAll(Map<? extends String, ? extends String[]> map) {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /** {@inheritDoc} */
+    public String[] remove(Object key) {
+        throw new UnsupportedOperationException();
+    }
+
+
+
+    /** {@inheritDoc} */
+    public int size() {
+        return enumerationSize(request.getKeys());
+    }
+
+
+    /** {@inheritDoc} */
+    public Collection<String[]> values() {
+        return new HeaderValuesCollection();
+    }
+
+    /**
+     * Extracts values enumeration of an attribute and returns the corresponding array of values.
+     *
+     * @param key The key of the attribute.
+     * @return The values of the attribute.
+     */
+    private String[] getHeaderValues(String key) {
+        List<String> list = new ArrayList<String>();
+        Enumeration<String> values = request.getValues(key);
+        while (values.hasMoreElements()) {
+            list.add(values.nextElement());
+        }
+        String[] retValue = list.toArray(new String[list.size()]);
+        return retValue;
+    }
+
+    /**
+     * Converts the content of a string enumeration to an array of strings.
+     *
+     * @param enumeration The enumeration to convert.
+     * @return The corresponding array.
+     */
+    private Set<String> enumeration2set(Enumeration<String> enumeration) {
+        Set<String> retValue = new HashSet<String>();
+        while (enumeration.hasMoreElements()) {
+            retValue.add(enumeration.nextElement());
+        }
+        return retValue;
+    }
+
+    /**
+     * Transforms a string array in a string set.
+     *
+     * @param valueArray The array to convert.
+     * @return The corresponding set.
+     */
+    private Set<String> array2set(String[] valueArray) {
+        Set<String> values = new HashSet<String>();
+        for (int i = 0; i < valueArray.length; i++) {
+            values.add(valueArray[i]);
+        }
+        return values;
+    }
+
+    /**
+     * Checks if values of a header attribute are the same as the one passed in
+     * the set.
+     *
+     * @param name The name of the header.
+     * @param testSet The set of values it must contain.
+     * @return <code>true</code> if all the values, and only them, are present
+     * in the header values.
+     */
+    private boolean compareHeaders(String name, Set<String> testSet) {
+        Enumeration<String> values = request.getValues(name);
+        boolean matched = true;
+        while (values.hasMoreElements() && matched) {
+            String currentValue = values.nextElement();
+            matched = testSet.remove(currentValue);
+        }
+        matched = matched && testSet.isEmpty();
+        return matched;
+    }
+
+    /**
+     * Entry set implementation for {@link HeaderValuesMap}.
+     */
+    private class HeadersEntrySet implements Set<Map.Entry<String, String[]>> {
+
+        @Override
+        public boolean add(java.util.Map.Entry<String, String[]> e) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean addAll(
+                Collection<? extends java.util.Map.Entry<String, String[]>> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void clear() {
+            throw new UnsupportedOperationException();
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean contains(Object o) {
+            return containsEntry((java.util.Map.Entry<String, String[]>) o);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean containsAll(Collection<?> c) {
+            Collection<Map.Entry<String, String[]>> realCollection =
+                (Collection<Map.Entry<String, String[]>>) c;
+            for (Map.Entry<String, String[]> entry : realCollection) {
+                if (!containsEntry(entry)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return HeaderValuesMap.this.isEmpty();
+        }
+
+        @Override
+        public Iterator<java.util.Map.Entry<String, String[]>> iterator() {
+            return new HeadersEntrySetIterator();
+        }
+
+        @Override
+        public boolean remove(Object o) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean removeAll(Collection<?> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean retainAll(Collection<?> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int size() {
+            return HeaderValuesMap.this.size();
+        }
+
+        @Override
+        public Object[] toArray() {
+            return toList().toArray();
+        }
+
+        @Override
+        public <T> T[] toArray(T[] a) {
+            return toList().toArray(a);
+        }
+
+        /**
+         * Checks whether the given entry is present in the headers.
+         *
+         * @param entry The entry to check.
+         * @return <code></code> if the key and the values of the entry are present.
+         */
+        private boolean containsEntry(Map.Entry<String, String[]> entry) {
+            Enumeration<String> entryValues = request.getValues(key(entry.getKey()));
+            String[] valueArray = entry.getValue();
+            Set<String> values = array2set(valueArray);
+            while (entryValues.hasMoreElements()) {
+                if (!values.remove(entryValues.nextElement())) {
+                    return false;
+                }
+            }
+            return values.isEmpty();
+        }
+
+        /**
+         * Turns this entry set into a list.
+         *
+         * @return The collection, turned into a list.
+         */
+        private List<Map.Entry<String, String[]>> toList() {
+            List<Map.Entry<String, String[]>> entries = new ArrayList<Map.Entry<String, String[]>>();
+            Enumeration<String> names = request.getKeys();
+            while (names.hasMoreElements()) {
+                entries.add(extractNextEntry(names));
+            }
+            return entries;
+        }
+
+        /**
+         * Returns the next entry, by getting the next element in the given enumeration.
+         *
+         * @param names The enumeration to get the next name from..
+         * @return The next map entry.
+         */
+        private MapEntry<String, String[]> extractNextEntry(
+                Enumeration<String> names) {
+            String name = names.nextElement();
+            return new MapEntryArrayValues<String, String>(name, getHeaderValues(name), false);
+        }
+
+        /**
+         * Iterates {@link HeadersEntrySet} elements.
+         */
+        private class HeadersEntrySetIterator implements Iterator<Map.Entry<String, String[]>> {
+
+            /**
+             * The enumeration to use.
+             */
+            private Enumeration<String> namesEnumeration = request.getKeys();
+
+            @Override
+            public boolean hasNext() {
+                return namesEnumeration.hasMoreElements();
+            }
+
+            @Override
+            public Map.Entry<String, String[]> next() {
+                return extractNextEntry(namesEnumeration);
+            }
+
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+        }
+    }
+
+    /**
+     * It is a collection of all values of the header. Each element is an array
+     * of values of a single header.
+     */
+    private class HeaderValuesCollection implements Collection<String[]> {
+
+        @Override
+        public boolean add(String[] e) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean addAll(Collection<? extends String[]> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void clear() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean contains(Object o) {
+            return containsValue(o);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean containsAll(Collection<?> c) {
+            Collection<String[]> realCollection = (Collection<String[]>) c;
+            for (String[] value : realCollection) {
+                if (!containsValue(value)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return HeaderValuesMap.this.isEmpty();
+        }
+
+        @Override
+        public Iterator<String[]> iterator() {
+            return new HeaderValuesCollectionIterator();
+        }
+
+        @Override
+        public boolean remove(Object o) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean removeAll(Collection<?> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean retainAll(Collection<?> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int size() {
+            return HeaderValuesMap.this.size();
+        }
+
+        @Override
+        public Object[] toArray() {
+            return toList().toArray();
+        }
+
+        @Override
+        public <T> T[] toArray(T[] a) {
+            return toList().toArray(a);
+        }
+
+        /**
+         * Turns this collection into a list.
+         *
+         * @return The list.
+         */
+        private List<String[]> toList() {
+            List<String[]> entries = new ArrayList<String[]>();
+            Enumeration<String> names = request.getKeys();
+            while (names.hasMoreElements()) {
+                entries.add(enumeration2array(request.getValues(names.nextElement())));
+            }
+            return entries;
+        }
+
+        /**
+         * Converts the content of a string enumeration to an array of strings.
+         *
+         * @param enumeration The enumeration to convert.
+         * @return The corresponding array.
+         */
+        private String[] enumeration2array(Enumeration<String> enumeration) {
+            List<String> list1 = new ArrayList<String>();
+            while (enumeration.hasMoreElements()) {
+                list1.add(enumeration.nextElement());
+            }
+
+            return list1.toArray(new String[list1.size()]);
+        }
+
+        /**
+         * Iterates elements of {@link HeaderValuesCollection}.
+         */
+        private class HeaderValuesCollectionIterator implements Iterator<String[]> {
+
+            /**
+             * The enumeration of the name of header attributes.
+             */
+            private Enumeration<String> namesEnumeration = request.getKeys();
+
+            @Override
+            public boolean hasNext() {
+                return namesEnumeration.hasMoreElements();
+            }
+
+            @Override
+            public String[] next() {
+                return enumeration2array(request.getValues(namesEnumeration.nextElement()));
+            }
+
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/KeySet.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/KeySet.java
new file mode 100644
index 0000000..b029b54
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/KeySet.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.request.collection;
+
+import static org.apache.tiles.request.collection.CollectionUtil.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.tiles.request.attribute.HasKeys;
+
+/**
+ * Exposes keys of a {@link HasKeys} object as a set.
+ *
+ * @version $Rev$ $Date$
+ */
+public class KeySet implements Set<String> {
+
+    /**
+     * The request to read.
+     */
+    private HasKeys<?> request;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request to read.
+     */
+    public KeySet(HasKeys<?> request) {
+        this.request = request;
+    }
+
+    @Override
+    public boolean add(String e) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean addAll(Collection<? extends String> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean contains(Object o) {
+        return request.getValue(key(o)) != null;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean containsAll(Collection<?> c) {
+        Collection<String> realCollection = (Collection<String>) c;
+        for (String key : realCollection) {
+            if (request.getValue(key(key)) == null) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return !request.getKeys().hasMoreElements();
+    }
+
+    @Override
+    public Iterator<String> iterator() {
+        return new KeySetIterator();
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean removeAll(Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean retainAll(Collection<?> c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int size() {
+        return enumerationSize(request.getKeys());
+    }
+
+    @Override
+    public Object[] toArray() {
+        return toList().toArray();
+    }
+
+    @Override
+    public <T> T[] toArray(T[] a) {
+        return toList().toArray(a);
+    }
+
+    /**
+     * Turns this set into a list.
+     *
+     * @return The corresponding list.
+     */
+    private List<String> toList() {
+        List<String> entries = new ArrayList<String>();
+        Enumeration<String> names = request.getKeys();
+        while (names.hasMoreElements()) {
+            entries.add(names.nextElement());
+        }
+        return entries;
+    }
+
+    /**
+     * Iterates elements of {@link KeySet}.
+     */
+    private class KeySetIterator implements Iterator<String> {
+
+        /**
+         * The key names enumeration.
+         */
+        private Enumeration<String> namesEnumeration = request.getKeys();
+
+        @Override
+        public boolean hasNext() {
+            return namesEnumeration.hasMoreElements();
+        }
+
+        @Override
+        public String next() {
+            return namesEnumeration.nextElement();
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/MapEntry.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/MapEntry.java
new file mode 100644
index 0000000..faf2702
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/MapEntry.java
@@ -0,0 +1,136 @@
+/*
+ * $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.request.collection;
+
+
+import java.util.Map;
+
+
+/**
+ * <p>Map.Entry implementation that can be constructed to either be read-only
+ * or not.</p>
+ *
+ * @version $Rev$ $Date$
+ * @param <K> The key type.
+ * @param <V> The value type.
+ */
+
+public class MapEntry<K, V> implements Map.Entry<K, V> {
+
+
+    /**
+     * <p>The entry key.</p>
+     */
+    private K key;
+
+    /**
+     * <p>The entry value.</p>
+     */
+    private V value;
+
+    /**
+     * <p>Whether the entry can be modified.</p>
+     */
+    private boolean modifiable = false;
+
+
+    /**
+     * <p>Creates a map entry that can either allow modifications or not.</p>
+     *
+     * @param key        The entry key
+     * @param value      The entry value
+     * @param modifiable Whether the entry should allow modification or not
+     */
+    public MapEntry(K key, V value, boolean modifiable) {
+        this.key = key;
+        this.value = value;
+        this.modifiable = modifiable;
+    }
+
+
+    /**
+     * <p>Gets the entry key.</p>
+     *
+     * @return The entry key
+     */
+    public K getKey() {
+        return key;
+    }
+
+
+    /**
+     * <p>Gets the entry value.</p>
+     *
+     * @return The entry key
+     */
+    public V getValue() {
+        return value;
+    }
+
+
+    /**
+     * <p>Sets the entry value if the entry can be modified.</p>
+     *
+     * @param val The new value
+     * @return The old entry value
+     * @throws UnsupportedOperationException If the entry cannot be modified
+     */
+    public V setValue(V val) {
+        if (modifiable) {
+            V oldVal = this.value;
+            this.value = val;
+            return oldVal;
+        }
+        throw new UnsupportedOperationException("The map entry is not modifiable");
+    }
+
+
+    /**
+     * <p>Determines if this entry is equal to the passed object.</p>
+     *
+     * @param o The object to test
+     * @return True if equal, else false
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public boolean equals(Object o) {
+        if (o != null && o instanceof Map.Entry) {
+            Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
+            return (this.getKey() == null ? entry.getKey() == null : this
+                    .getKey().equals(entry.getKey()))
+                    && (this.getValue() == null ? entry.getValue() == null
+                            : this.getValue().equals(entry.getValue()));
+        }
+        return false;
+    }
+
+
+    /**
+     * <p>Returns the hashcode for this entry.</p>
+     *
+     * @return The and'ed hashcode of the key and value
+     */
+    @Override
+    public int hashCode() {
+        return (this.getKey() == null ? 0 : this.getKey().hashCode())
+            ^ (this.getValue() == null ? 0 : this.getValue().hashCode());
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/MapEntryArrayValues.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/MapEntryArrayValues.java
new file mode 100644
index 0000000..4184f81
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/MapEntryArrayValues.java
@@ -0,0 +1,103 @@
+/*
+ * $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.request.collection;
+
+
+import java.util.Map;
+
+
+/**
+ * Multi-valued map entry.
+ *
+ * @version $Rev$ $Date$
+ * @param <K> The key type.
+ * @param <V> The value type.
+ */
+
+public class MapEntryArrayValues<K, V> extends MapEntry<K, V[]> {
+
+    /**
+     * Constructor.
+     *
+     * @param key The key of the entry.
+     * @param value The array of values.
+     * @param modifiable If <code>true</code> the entry is modifiable.
+     */
+    public MapEntryArrayValues(K key, V[] value, boolean modifiable) {
+        super(key, value, modifiable);
+    }
+
+
+    /**
+     * <p>Returns the hashcode for this entry.</p>
+     *
+     * @return The and'ed hashcode of the key and value
+     */
+    @Override
+    public int hashCode() {
+        int valueHash = 0;
+        V[] value = getValue();
+        if (value != null) {
+            for (int i = 0; i < value.length; i++) {
+                valueHash += value[i].hashCode();
+            }
+        }
+
+        return (this.getKey() == null ? 0 : this.getKey().hashCode())
+                ^ valueHash;
+    }
+
+    /**
+     * <p>Determines if this entry is equal to the passed object.</p>
+     *
+     * @param o The object to test
+     * @return True if equal, else false
+     */
+    @Override
+    @SuppressWarnings("unchecked")
+    public boolean equals(Object o) {
+        if (o != null && o instanceof Map.Entry) {
+            Map.Entry<K, V[]> entry = (Map.Entry<K, V[]>) o;
+            if (this.getKey() == null ? entry.getKey() == null : this
+                    .getKey().equals(entry.getKey())) {
+                V[] values = getValue();
+                V[] otherValues = entry.getValue();
+                if (values != null) {
+                    if (otherValues != null) {
+                        if (values.length == otherValues.length) {
+                            boolean same = true;
+                            for (int i = 0; i < values.length && same; i++) {
+                                same = values[i] == null ? otherValues[i] == null
+                                        : values[i].equals(otherValues[i]);
+                            }
+                            return same;
+                        }
+                    } else {
+                        return false;
+                    }
+                } else {
+                    return otherValues == null;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMap.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMap.java
new file mode 100644
index 0000000..5452691
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMap.java
@@ -0,0 +1,438 @@
+/*
+ * $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.request.collection;
+
+import static org.apache.tiles.request.collection.CollectionUtil.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tiles.request.attribute.HasKeys;
+
+/**
+ * Wraps an {@link HasKeys} object into a read-only map.
+ *
+ * @version $Rev$ $Date$
+ * @param <V> The type of the values.
+ */
+public class ReadOnlyEnumerationMap<V> implements Map<String, V> {
+
+    /**
+     * The request.
+     */
+    protected HasKeys<V> request;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request object to use.
+     */
+    public ReadOnlyEnumerationMap(HasKeys<V> request) {
+        this.request = request;
+    }
+
+    /** {@inheritDoc} */
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /** {@inheritDoc} */
+    public boolean containsKey(Object key) {
+        return (request.getValue(key(key)) != null);
+    }
+
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    public boolean containsValue(Object value) {
+        V realValue = (V) value;
+        for (Enumeration<String> keysIt = request.getKeys(); keysIt.hasMoreElements();) {
+            if (realValue.equals(request.getValue(keysIt.nextElement()))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /** {@inheritDoc} */
+    public Set<Map.Entry<String, V>> entrySet() {
+        return new ReadOnlyEnumerationMapEntrySet();
+    }
+
+
+    /** {@inheritDoc} */
+    public V get(Object key) {
+        return (request.getValue(key(key)));
+    }
+
+
+    /** {@inheritDoc} */
+    public boolean isEmpty() {
+        return !request.getKeys().hasMoreElements();
+    }
+
+
+    /** {@inheritDoc} */
+    public Set<String> keySet() {
+        return new KeySet(request);
+    }
+
+
+    /** {@inheritDoc} */
+    public V put(String key, V value) {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /** {@inheritDoc} */
+    public void putAll(Map<? extends String, ? extends V> map) {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /** {@inheritDoc} */
+    public V remove(Object key) {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /** {@inheritDoc} */
+    public int size() {
+        return enumerationSize(request.getKeys());
+    }
+
+
+    /** {@inheritDoc} */
+    public Collection<V> values() {
+        return new ReadOnlyEnumerationMapValuesCollection();
+    }
+
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean equals(Object o) {
+        HasKeys<V> otherRequest = ((ReadOnlyEnumerationMap<V>) o).request;
+        boolean retValue = true;
+        Set<String> otherKeys = new HashSet<String>();
+        for (Enumeration<String> attribs = otherRequest.getKeys(); attribs
+                .hasMoreElements();) {
+            otherKeys.add(attribs.nextElement());
+        }
+        for (Enumeration<String> attribs = request.getKeys(); attribs
+                .hasMoreElements()
+                && retValue;) {
+            String parameterName = attribs.nextElement();
+            retValue = request.getValue(parameterName).equals(
+                    otherRequest.getValue(parameterName));
+            otherKeys.remove(parameterName);
+        }
+
+        return retValue && otherKeys.isEmpty();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        int retValue = 0;
+        for (Enumeration<String> attribs = request.getKeys(); attribs
+                .hasMoreElements();) {
+            String parameterName = attribs.nextElement();
+            V value = request.getValue(parameterName);
+            retValue += parameterName.hashCode() ^ (value == null ? 0 : value.hashCode());
+        }
+        return retValue;
+    }
+
+    /**
+     * Entry set implementation for {@link ReadOnlyEnumerationMap}.
+     */
+    class ReadOnlyEnumerationMapEntrySet implements Set<Map.Entry<String, V>> {
+
+        @Override
+        public boolean add(java.util.Map.Entry<String, V> e) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean addAll(
+                Collection<? extends java.util.Map.Entry<String, V>> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void clear() {
+            throw new UnsupportedOperationException();
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean contains(Object o) {
+            return containsEntry((java.util.Map.Entry<String, V>) o);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean containsAll(Collection<?> c) {
+            Collection<Map.Entry<String, V>> realCollection =
+                (Collection<Map.Entry<String, V>>) c;
+            for (Map.Entry<String, V> entry : realCollection) {
+                if (!containsEntry(entry)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return ReadOnlyEnumerationMap.this.isEmpty();
+        }
+
+        @Override
+        public Iterator<java.util.Map.Entry<String, V>> iterator() {
+            return new ReadOnlyEnumerationMapEntrySetIterator();
+        }
+
+        @Override
+        public boolean remove(Object o) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean removeAll(Collection<?> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean retainAll(Collection<?> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int size() {
+            return ReadOnlyEnumerationMap.this.size();
+        }
+
+        @Override
+        public Object[] toArray() {
+            return toList().toArray();
+        }
+
+        @Override
+        public <T> T[] toArray(T[] a) {
+            return toList().toArray(a);
+        }
+
+        /**
+         * Checks whether the entry is present.
+         *
+         * @param entry The entry to check.
+         * @return <code>true</code> if the entry is present.
+         */
+        protected boolean containsEntry(Map.Entry<String, V> entry) {
+            V storedValue = request.getValue(key(entry.getKey()));
+            return storedValue != null && storedValue.equals(entry.getValue());
+        }
+
+        /**
+         * Turns this set into a list.
+         *
+         * @return The list.
+         */
+        private List<Map.Entry<String, V>> toList() {
+            List<Map.Entry<String, V>> entries = new ArrayList<Map.Entry<String, V>>();
+            Enumeration<String> names = request.getKeys();
+            while (names.hasMoreElements()) {
+                entries.add(extractNextEntry(names));
+            }
+            return entries;
+        }
+
+        /**
+         * Returns the next entry, given the enumeration.
+         *
+         * @param names The enumeration to get the next key from.
+         * @return The next entry.
+         */
+        private MapEntry<String, V> extractNextEntry(
+                Enumeration<String> names) {
+            String name = names.nextElement();
+            return new MapEntry<String, V>(name, request.getValue(name),
+                    false);
+        }
+
+        /**
+         * Iterates entries of {@link ReadOnlyEnumerationMap}.
+         */
+        private class ReadOnlyEnumerationMapEntrySetIterator implements Iterator<Map.Entry<String, V>> {
+
+            /**
+             * Enumerates keys.
+             */
+            private Enumeration<String> namesEnumeration = request.getKeys();
+
+            @Override
+            public boolean hasNext() {
+                return namesEnumeration.hasMoreElements();
+            }
+
+            @Override
+            public java.util.Map.Entry<String, V> next() {
+                return extractNextEntry(namesEnumeration);
+            }
+
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+        }
+    }
+
+    /**
+     * Values collection for {@link ReadOnlyEnumerationMap}.
+     */
+    private class ReadOnlyEnumerationMapValuesCollection implements Collection<V> {
+
+        @Override
+        public boolean add(V e) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean addAll(Collection<? extends V> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void clear() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean contains(Object o) {
+            return containsValue(o);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean containsAll(Collection<?> c) {
+            Collection<String> realCollection = (Collection<String>) c;
+            List<String> valueList = new ArrayList<String>(realCollection);
+            for (Enumeration<String> keysEnum = request.getKeys(); keysEnum.hasMoreElements();) {
+                valueList.remove(request.getValue(keysEnum.nextElement()));
+                if (valueList.isEmpty()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return ReadOnlyEnumerationMap.this.isEmpty();
+        }
+
+        @Override
+        public Iterator<V> iterator() {
+            return new ReadOnlyEnumerationMapValuesCollectionIterator();
+        }
+
+        @Override
+        public boolean remove(Object o) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean removeAll(Collection<?> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean retainAll(Collection<?> c) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int size() {
+            return ReadOnlyEnumerationMap.this.size();
+        }
+
+        @Override
+        public Object[] toArray() {
+            return toList().toArray();
+        }
+
+        @Override
+        public <T> T[] toArray(T[] a) {
+            return toList().toArray(a);
+        }
+
+        /**
+         * Turns this collection into a list.
+         *
+         * @return The list.
+         */
+        private List<V> toList() {
+            List<V> entries = new ArrayList<V>();
+            Enumeration<String> names = request.getKeys();
+            while (names.hasMoreElements()) {
+                entries.add(request.getValue(names.nextElement()));
+            }
+            return entries;
+        }
+
+        /**
+         * Iterates values of {@link ReadOnlyEnumerationMap}.
+         */
+        private class ReadOnlyEnumerationMapValuesCollectionIterator implements Iterator<V> {
+
+            /**
+             * Enumerates attribute keys.
+             */
+            private Enumeration<String> namesEnumeration = request.getKeys();
+
+            @Override
+            public boolean hasNext() {
+                return namesEnumeration.hasMoreElements();
+            }
+
+            @Override
+            public V next() {
+                return request.getValue(namesEnumeration.nextElement());
+            }
+
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/RemovableKeySet.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/RemovableKeySet.java
new file mode 100644
index 0000000..1430f80
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/RemovableKeySet.java
@@ -0,0 +1,95 @@
+/*
+ * $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.request.collection;
+
+import static org.apache.tiles.request.collection.CollectionUtil.*;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.tiles.request.attribute.HasRemovableKeys;
+
+/**
+ * Wraps {@link HasRemovableKeys} keys as a set.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RemovableKeySet extends KeySet {
+
+    /**
+     * The request.
+     */
+    private HasRemovableKeys<?> request;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request.
+     */
+    public RemovableKeySet(HasRemovableKeys<?> request) {
+        super(request);
+        this.request = request;
+    }
+
+    @Override
+    public boolean remove(Object o) {
+        String skey = key(o);
+        Object previous = request.getValue(skey);
+        if (previous != null) {
+            request.removeValue(skey);
+            return true;
+        }
+        return false;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean removeAll(Collection<?> c) {
+        Collection<String> realCollection = (Collection<String>) c;
+        boolean retValue = false;
+        for (String entry : realCollection) {
+            retValue |= remove(entry);
+        }
+        return retValue;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean retainAll(Collection<?> c) {
+        Collection<String> realCollection = (Collection<String>) c;
+        boolean retValue = false;
+        Set<String> keysToRemove = new LinkedHashSet<String>();
+        for (Enumeration<String> keys = request.getKeys(); keys.hasMoreElements();) {
+            String key = keys.nextElement();
+            if (!realCollection.contains(key)) {
+                retValue = true;
+                keysToRemove.add(key);
+            }
+        }
+        for (String key : keysToRemove) {
+            request.removeValue(key);
+        }
+        return retValue;
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/ScopeMap.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/ScopeMap.java
new file mode 100644
index 0000000..93465ea
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/ScopeMap.java
@@ -0,0 +1,178 @@
+/*
+ * $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.request.collection;
+
+import static org.apache.tiles.request.collection.CollectionUtil.*;
+
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+
+/**
+ * Exposes a scope context as a <String, Object> map.
+ *
+ * @version $Rev$ $Date$
+ */
+
+public class ScopeMap extends ReadOnlyEnumerationMap<Object> {
+
+    /**
+     * The context to read.
+     */
+    private AttributeExtractor context;
+
+    /**
+     * Constructor.
+     *
+     * @param context The servlet context to use.
+     */
+    public ScopeMap(AttributeExtractor context) {
+        super(context);
+        this.context = context;
+    }
+
+
+    /** {@inheritDoc} */
+    public void clear() {
+        Enumeration<String> keys = context.getKeys();
+        while (keys.hasMoreElements()) {
+            context.removeValue(keys.nextElement());
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Set<Map.Entry<String, Object>> entrySet() {
+        return new ScopeEntrySet();
+    }
+
+    /** {@inheritDoc} */
+    public Set<String> keySet() {
+        return new RemovableKeySet(context);
+    }
+
+    /** {@inheritDoc} */
+    public Object put(String key, Object value) {
+        String skey = key(key);
+        Object previous = context.getValue(skey);
+        context.setValue(skey, value);
+        return previous;
+    }
+
+    /** {@inheritDoc} */
+    public void putAll(Map<? extends String, ? extends Object> map) {
+        Iterator<? extends String> keys = map.keySet().iterator();
+        while (keys.hasNext()) {
+            String key = keys.next();
+            context.setValue(key, map.get(key));
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object remove(Object key) {
+        String skey = key(key);
+        Object previous = context.getValue(skey);
+        context.removeValue(skey);
+        return (previous);
+    }
+
+    /**
+     * Entry set implementation for {@link ScopeMap}.
+     */
+    private class ScopeEntrySet extends ReadOnlyEnumerationMap<Object>.ReadOnlyEnumerationMapEntrySet {
+
+        @Override
+        public boolean add(java.util.Map.Entry<String, Object> e) {
+            String key = e.getKey();
+            Object value = e.getValue();
+            Object oldValue = get(key);
+            if (oldValue == null || !oldValue.equals(value)) {
+                context.setValue(key, value);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean addAll(
+                Collection<? extends java.util.Map.Entry<String, Object>> c) {
+            boolean retValue = false;
+            for (Map.Entry<String, Object> entry : c) {
+                retValue |= add(entry);
+            }
+            return retValue;
+        }
+
+        @Override
+        public void clear() {
+            ScopeMap.this.clear();
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean remove(Object o) {
+            Map.Entry<String, Object> entry = (java.util.Map.Entry<String, Object>) o;
+            String key = entry.getKey();
+            Object currentValue = context.getValue(key);
+            if (currentValue != null && currentValue.equals(entry.getValue())) {
+                context.removeValue(key);
+                return true;
+            }
+            return false;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean removeAll(Collection<?> c) {
+            Collection<Map.Entry<String, Object>> realCollection = (Collection<java.util.Map.Entry<String, Object>>) c;
+            boolean retValue = false;
+            for (Map.Entry<String, Object> entry : realCollection) {
+                retValue |= remove(entry);
+            }
+            return retValue;
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean retainAll(Collection<?> c) {
+            Collection<Map.Entry<String, Object>> realCollection = (Collection<java.util.Map.Entry<String, Object>>) c;
+            boolean retValue = false;
+            Set<String> keysToRemove = new LinkedHashSet<String>();
+            for (Enumeration<String> keys = context.getKeys(); keys.hasMoreElements();) {
+                String key = keys.nextElement();
+                Object value = context.getValue(key);
+                Map.Entry<String, Object> entry = new MapEntry<String, Object>(key, value, false);
+                if (!realCollection.contains(entry)) {
+                    retValue = true;
+                    keysToRemove.add(key);
+                }
+            }
+            for (String key : keysToRemove) {
+                context.removeValue(key);
+            }
+            return retValue;
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/package-info.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/package-info.java
new file mode 100644
index 0000000..02818bf
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/collection/package-info.java
@@ -0,0 +1,36 @@
+/*
+ * $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.
+ */
+/**
+ * Special collections and maps for attributes (like HTTP request 
+ * or session attributes).
+ * These can be used to access another object's attributes through 
+ * a Map interface.
+ * <ul>
+ * <li> {@link org.apache.tiles.request.collection.ScopeMap} provides
+ * full get/put/remove access to the attributes.
+ * <li> {@link org.apache.tiles.request.collection.ReadOnlyEnumerationMap}
+ * provides get-only access.
+ * <li> {@link org.apache.tiles.request.collection.HeaderValuesMap}
+ * provides get-only access to a multi-valued map (typically, headers).
+ * </ul>
+ */
+package org.apache.tiles.request.collection;
+
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/locale/LocaleUtil.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/locale/LocaleUtil.java
new file mode 100644
index 0000000..4da8afd
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/locale/LocaleUtil.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.request.locale;
+
+import java.util.Locale;
+
+/**
+ * Utilities for locale manipulation.
+ *
+ * @version $Rev$ $Date$
+ */
+public final class LocaleUtil {
+
+    /**
+     * The "null" Locale, i.e. a Locale that points to no real locale.
+     *
+     * @deprecated use Locale.ROOT instead.
+     */
+    @Deprecated
+    public static final Locale NULL_LOCALE = Locale.ROOT;
+
+    /**
+     * Private constructor to avoid instantiation.
+     */
+    private LocaleUtil() {
+    }
+
+    /**
+     * <p>
+     * Returns the "parent" locale of a given locale.
+     * </p>
+     * <p>
+     * If the original locale is only language-based, the {@link #NULL_LOCALE}
+     * object is returned.
+     * </p>
+     * <p>
+     * If the original locale is {@link #NULL_LOCALE}, then <code>null</code>
+     * is returned.
+     * </p>
+     *
+     * @param locale The original locale.
+     * @return The parent locale.
+     */
+    public static Locale getParentLocale(Locale locale) {
+        Locale retValue = null;
+        String language = locale.getLanguage();
+        String country = locale.getCountry();
+        String variant = locale.getVariant();
+        if (!"".equals(variant)) {
+            retValue = new Locale(language, country);
+        } else if (!"".equals(country)) {
+            retValue = new Locale(language);
+        } else if (!"".equals(language)) {
+            retValue = Locale.ROOT;
+        }
+
+        return retValue;
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/locale/PostfixedApplicationResource.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/locale/PostfixedApplicationResource.java
new file mode 100644
index 0000000..9864239
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/locale/PostfixedApplicationResource.java
@@ -0,0 +1,196 @@
+/*
+ * $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.request.locale;
+
+import java.util.Locale;
+
+import org.apache.tiles.request.ApplicationResource;
+
+/**
+ * An ApplicationResource whose localization is managed by postfixing the file name.
+ * The various localizations are file sitting next to each other, with the locale identified in the postfix.
+ * 
+ * For instance:
+ * <pre>
+ * /WEB-INF/tiles.xml
+ * /WEB-INF/tiles_fr.xml
+ * /WEB-INF/tiles_it.xml
+ * /WEB-INF/tiles_it_IT.xml
+ * </pre>
+ * 
+ * Two PostfixedApplicationResources are equals if they share the same localized path and the same class.
+ * 
+ * @version $Rev$ $Date$
+ */
+public abstract class PostfixedApplicationResource implements ApplicationResource {
+
+    /** The path without its suffix and its locale postfix. */
+    private String pathPrefix;
+    /** The suffix. */
+    private String suffix;
+    /** The Locale. */
+    private Locale locale;
+
+    /**
+     * Create a new PostfixedApplicationResource for the specified path.
+     * @param localePath the path including localization.
+     */
+    protected PostfixedApplicationResource(String localePath) {
+        int prefixIndex = localePath.indexOf('_', localePath.lastIndexOf("/"));
+        int suffixIndex = localePath.lastIndexOf('.');
+        if (suffixIndex < 0) {
+            suffix = "";
+            suffixIndex = localePath.length();
+        } else {
+            suffix = localePath.substring(suffixIndex);
+        }
+        if (prefixIndex < 0) {
+            pathPrefix = localePath.substring(0, suffixIndex);
+            locale = Locale.ROOT;
+        } else {
+            pathPrefix = localePath.substring(0, prefixIndex);
+            String localeString = localePath.substring(prefixIndex + 1, suffixIndex);
+            int countryIndex = localeString.indexOf('_');
+            if (countryIndex < 0) {
+                locale = new Locale(localeString);
+            } else {
+                int variantIndex = localeString.indexOf('_', countryIndex + 1);
+                if (variantIndex < 0) {
+                    locale = new Locale(localeString.substring(0, countryIndex),
+                            localeString.substring(countryIndex + 1));
+                } else {
+                    locale = new Locale(localeString.substring(0, countryIndex), localeString.substring(
+                            countryIndex + 1, variantIndex), localeString.substring(variantIndex + 1));
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a new PostfixedApplicationResource for the specified path.
+     * @param path the path excluding localization.
+     * @param locale the Locale.
+     */
+    protected PostfixedApplicationResource(String path, Locale locale) {
+        int suffixIndex = path.lastIndexOf('.');
+        if (suffixIndex < 0) {
+            suffix = "";
+            pathPrefix = path;
+        } else {
+            pathPrefix = path.substring(0, suffixIndex);
+            suffix = path.substring(suffixIndex);
+        }
+        this.locale = locale;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String getLocalePath() {
+        return getLocalePath(locale);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String getPath() {
+        return pathPrefix + suffix;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final String getLocalePath(Locale newLocale) {
+        return pathPrefix + getPostfix(newLocale) + suffix;
+    }
+
+    /**
+     * Get the postfix for that Locale.
+     * @param locale a locale.
+     * @return the matching postfix.
+     */
+    private static final String getPostfix(Locale locale) {
+        if (locale == null) {
+            return "";
+        }
+
+        StringBuilder builder = new StringBuilder();
+        String language = locale.getLanguage();
+        String country = locale.getCountry();
+        String variant = locale.getVariant();
+        if (!"".equals(language)) {
+            builder.append("_");
+            builder.append(language);
+            if (!"".equals(country)) {
+                builder.append("_");
+                builder.append(country);
+                if (!"".equals(variant)) {
+                    builder.append("_");
+                    builder.append(variant);
+                }
+            }
+        }
+        return builder.toString();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public final Locale getLocale() {
+        return locale;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((locale == null) ? 0 : locale.hashCode());
+        result = prime * result + ((pathPrefix == null) ? 0 : pathPrefix.hashCode());
+        result = prime * result + ((suffix == null) ? 0 : suffix.hashCode());
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        PostfixedApplicationResource other = (PostfixedApplicationResource) obj;
+        if (locale == null) {
+            if (other.locale != null)
+                return false;
+        } else if (!locale.equals(other.locale))
+            return false;
+        if (pathPrefix == null) {
+            if (other.pathPrefix != null)
+                return false;
+        } else if (!pathPrefix.equals(other.pathPrefix))
+            return false;
+        if (suffix == null) {
+            if (other.suffix != null)
+                return false;
+        } else if (!suffix.equals(other.suffix))
+            return false;
+        return true;
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/locale/URLApplicationResource.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/locale/URLApplicationResource.java
new file mode 100644
index 0000000..9535a20
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/locale/URLApplicationResource.java
@@ -0,0 +1,107 @@
+/*
+ * $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.request.locale;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Locale;
+
+/**
+ * A {@link PostfixedApplicationResource} that can be accessed through a URL.
+ *
+ * @version $Rev$ $Date$
+ */
+
+public class URLApplicationResource extends PostfixedApplicationResource {
+
+    /** the URL where the contents can be found. */
+    private URL url;
+    /** if the URL matches a file, this is the file. */
+    private File file;
+
+    /**
+     * Creates a URLApplicationResource for the specified path that can be accessed through the specified URL.
+     * 
+     * @param localePath the path including localization.
+     * @param url the URL where the contents can be found.
+     */
+    public URLApplicationResource(String localePath, URL url) {
+        super(localePath);
+        this.url = url;
+        if ("file".equals(url.getProtocol())) {
+            file = new File(url.getPath());
+        }
+    }
+
+    /**
+     * Creates a URLApplicationResource for the specified path that can be accessed through the specified URL.
+     * 
+     * @param path the path excluding localization.
+     * @param locale the Locale.
+     * @param url the URL where the contents can be found.
+     */
+    public URLApplicationResource(String path, Locale locale, URL url) throws MalformedURLException {
+        super(path, locale);
+        this.url = new URL(url, getLocalePath());
+        if ("file".equals(url.getProtocol())) {
+            file = new File(this.url.getPath());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public InputStream getInputStream() throws IOException {
+        if (file != null) {
+            return new FileInputStream(file);
+        } else {
+            return url.openConnection().getInputStream();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public long getLastModified() throws IOException {
+        if (file != null) {
+            return file.lastModified();
+        } else {
+            URLConnection connection = url.openConnection();
+            if (connection instanceof JarURLConnection) {
+                return ((JarURLConnection) connection).getJarEntry().getTime();
+            } else {
+                long result = connection.getLastModified();
+                return result;
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "Resource " + getLocalePath() + " at " + url.toString();
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/package-info.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/package-info.java
new file mode 100644
index 0000000..65d2558
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/package-info.java
@@ -0,0 +1,46 @@
+/*
+ * $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.
+ */
+/**
+ * This package defines the concept of "request" as the event causing a 
+ * document to be generated from a template. The process is also called
+ * "rendering the template". Typical examples are servlet requests or 
+ * portlet requests. 
+ * 
+ * This API is independent of the underlying technology, allowing the user 
+ * to deal with similarities in servlets and portlets, or various template 
+ * technologies, in a uniform way.
+ * 
+ * It is based on 3 main interfaces:
+ * <ul>
+ * <li>{@link org.apache.tiles.request.Request} is the main abstraction, 
+ * encapsulating the parameters of the template (as attributes and scopes)
+ * and the target document (as java.io.OutputStream). 
+ * {@link org.apache.tiles.request.DispatchRequest} holds some features common
+ * to servlets and portlets that are unlikely to be found outside of a JavaEE
+ * web environment.
+ * <li>{@link org.apache.tiles.ApplicationContext} can be used to access
+ * application-wide configuration and resources (typically the files containing
+ * the templates).
+ * <li>{@link org.apache.tiles.request.render.Renderer} is the interface supported
+ * by the engine in charge of rendering the template.
+ * <ul>
+ */
+package org.apache.tiles.request;
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/CannotAccessMethodException.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/CannotAccessMethodException.java
new file mode 100644
index 0000000..3311ab5
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/CannotAccessMethodException.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.request.reflect;
+
+import org.apache.tiles.request.RequestException;
+
+/**
+ * Indicates that a method cannot be accessed.
+ *
+ * @version $Rev$ $Date$
+ */
+public class CannotAccessMethodException extends RequestException {
+
+    /**
+     * Constructor.
+     */
+    public CannotAccessMethodException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     */
+    public CannotAccessMethodException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param e The exception to be wrapped.
+     */
+    public CannotAccessMethodException(Throwable e) {
+        super(e);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @param e The exception to be wrapped.
+     */
+    public CannotAccessMethodException(String message, Throwable e) {
+        super(message, e);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/CannotInstantiateObjectException.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/CannotInstantiateObjectException.java
new file mode 100644
index 0000000..08b6621
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/CannotInstantiateObjectException.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.request.reflect;
+
+import org.apache.tiles.request.RequestException;
+
+/**
+ * Indicates that an object cannot be instantiated.
+ *
+ * @version $Rev$ $Date$
+ */
+public class CannotInstantiateObjectException extends RequestException {
+
+    /**
+     * Constructor.
+     */
+    public CannotInstantiateObjectException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     */
+    public CannotInstantiateObjectException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param e The exception to be wrapped.
+     */
+    public CannotInstantiateObjectException(Throwable e) {
+        super(e);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @param e The exception to be wrapped.
+     */
+    public CannotInstantiateObjectException(String message, Throwable e) {
+        super(message, e);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/ClassUtil.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/ClassUtil.java
new file mode 100644
index 0000000..96a9898
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/ClassUtil.java
@@ -0,0 +1,144 @@
+/*
+ * $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.request.reflect;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Utilities to work with dynamic class loading and instantiation.
+ *
+ * @version $Rev$ $Date$
+ */
+public final class ClassUtil {
+
+    /**
+     * Constructor, private to avoid instantiation.
+     */
+    private ClassUtil() {
+    }
+
+    /**
+     * Returns the class and casts it to the correct subclass.<br>
+     * It tries to use the thread's current classloader first and, if it does
+     * not succeed, uses the classloader of ClassUtil.
+     *
+     * @param <T> The subclass to use.
+     * @param className The name of the class to load.
+     * @param baseClass The base class to subclass to.
+     * @return The loaded class.
+     * @throws ClassNotFoundException If the class has not been found.
+     */
+    public static <T> Class<? extends T> getClass(String className,
+            Class<T> baseClass) throws ClassNotFoundException {
+        ClassLoader classLoader = Thread.currentThread()
+                .getContextClassLoader();
+        if (classLoader == null) {
+            classLoader = ClassUtil.class.getClassLoader();
+        }
+        return Class.forName(className, true, classLoader)
+                .asSubclass(baseClass);
+    }
+
+    /**
+     * Returns an instance of the given class name, by calling the default
+     * constructor.
+     *
+     * @param className The class name to load and to instantiate.
+     * @return The new instance of the class name.
+     * @throws CannotInstantiateObjectException If something goes wrong during
+     * instantiation.
+     */
+    public static Object instantiate(String className) {
+        return instantiate(className, false);
+    }
+
+    /**
+     * Returns an instance of the given class name, by calling the default
+     * constructor.
+     *
+     * @param className The class name to load and to instantiate.
+     * @param returnNull If <code>true</code>, if the class is not found it
+     * returns <code>true</code>, otherwise it throws a
+     * <code>TilesException</code>.
+     * @return The new instance of the class name.
+     * @throws CannotInstantiateObjectException If something goes wrong during instantiation.
+     */
+    public static Object instantiate(String className, boolean returnNull) {
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        if (classLoader == null) {
+            classLoader = ClassUtil.class.getClassLoader();
+        }
+        try {
+            Class<? extends Object> namedClass = getClass(className, Object.class);
+            return namedClass.newInstance();
+        } catch (ClassNotFoundException e) {
+            if (returnNull) {
+                return null;
+            }
+            throw new CannotInstantiateObjectException(
+                    "Unable to resolve factory class: '" + className + "'", e);
+        } catch (IllegalAccessException e) {
+            throw new CannotInstantiateObjectException(
+                    "Unable to access factory class: '" + className + "'", e);
+        } catch (InstantiationException e) {
+            throw new CannotInstantiateObjectException(
+                    "Unable to instantiate factory class: '"
+                            + className
+                            + "'. Make sure that this class has a default constructor",
+                    e);
+        }
+    }
+
+    /**
+     * Collects bean infos from a class and filling a list.
+     *
+     * @param clazz The class to be inspected.
+     * @param name2descriptor The map in the form: name of the property ->
+     * descriptor.
+     */
+    public static void collectBeanInfo(Class<?> clazz,
+            Map<String, PropertyDescriptor> name2descriptor) {
+        Logger log = LoggerFactory.getLogger(ClassUtil.class);
+        BeanInfo info = null;
+        try {
+            info = Introspector.getBeanInfo(clazz);
+        } catch (Exception ex) {
+            if (log.isDebugEnabled()) {
+                log.debug("Cannot inspect class " + clazz, ex);
+            }
+        }
+        if (info == null) {
+            return;
+        }
+        for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
+            pd.setValue("type", pd.getPropertyType());
+            pd.setValue("resolvableAtDesignTime", Boolean.TRUE);
+            name2descriptor.put(pd.getName(), pd);
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/package-info.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/package-info.java
new file mode 100644
index 0000000..299c145
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/reflect/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Utilities to work with dynamic class loading and instantiation.
+ */
+package org.apache.tiles.request.reflect;
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/BasicRendererFactory.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/BasicRendererFactory.java
new file mode 100644
index 0000000..e580400
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/BasicRendererFactory.java
@@ -0,0 +1,84 @@
+/*
+ * $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.request.render;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * Basic renderer factory implementation.
+ *
+ * @version $Rev$ $Date$
+ */
+public class BasicRendererFactory implements RendererFactory {
+
+    /**
+     * The renderer name/renderer map.
+     */
+    protected Map<String, Renderer> renderers;
+
+    /**
+     * The default renderer.
+     */
+    protected Renderer defaultRenderer;
+
+    /**
+     * Constructor.
+     */
+    public BasicRendererFactory() {
+        renderers = new HashMap<String, Renderer>();
+    }
+
+    /** {@inheritDoc} */
+    public Renderer getRenderer(String name) {
+        Renderer retValue;
+        if (name != null) {
+            retValue = renderers.get(name);
+            if (retValue == null) {
+                throw new NoSuchRendererException("Cannot find a renderer named '" + name + "'");
+            }
+        } else {
+            retValue = defaultRenderer;
+        }
+
+        return retValue;
+    }
+
+    /**
+     * Sets the default renderer.
+     *
+     * @param renderer The default renderer.
+     */
+    public void setDefaultRenderer(Renderer renderer) {
+        this.defaultRenderer = renderer;
+    }
+
+    /**
+     * Registers a renderer.
+     *
+     * @param name The name of the renderer.
+     * @param renderer The renderer to register.
+     */
+    public void registerRenderer(String name, Renderer renderer) {
+        renderers.put(name, renderer);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/CannotRenderException.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/CannotRenderException.java
new file mode 100644
index 0000000..0e5ddf3
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/CannotRenderException.java
@@ -0,0 +1,65 @@
+/*
+ * $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.request.render;
+
+
+/**
+ * Indicates that something went wrong during the rendering process.
+ *
+ * @version $Rev$ $Date$
+ */
+public class CannotRenderException extends RenderException {
+
+    /**
+     * Constructor.
+     */
+    public CannotRenderException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     */
+    public CannotRenderException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param e The exception to be wrapped.
+     */
+    public CannotRenderException(Throwable e) {
+        super(e);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @param e The exception to be wrapped.
+     */
+    public CannotRenderException(String message, Throwable e) {
+        super(message, e);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/ChainedDelegateRenderer.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/ChainedDelegateRenderer.java
new file mode 100644
index 0000000..0b1de93
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/ChainedDelegateRenderer.java
@@ -0,0 +1,85 @@
+/*
+ * $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.request.render;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tiles.request.Request;
+
+/**
+ * Renders an attribute that has no associated renderer using delegation to
+ * other renderers.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ChainedDelegateRenderer implements Renderer {
+
+    /**
+     * The list of chained renderers.
+     */
+    private List<Renderer> renderers;
+
+    /**
+     * Constructor.
+     */
+    public ChainedDelegateRenderer() {
+        renderers = new ArrayList<Renderer>();
+    }
+
+    /**
+     * Adds an attribute renderer to the list. The first inserted this way, the
+     * first is checked when rendering.
+     *
+     * @param renderer The renderer to add.
+     */
+    public void addAttributeRenderer(Renderer renderer) {
+        renderers.add(renderer);
+    }
+
+
+    @Override
+    public void render(String value, Request request) throws IOException {
+        if (value == null) {
+            throw new NullPointerException("The attribute value is null");
+        }
+
+        for (Renderer renderer : renderers) {
+            if (renderer.isRenderable(value, request)) {
+                renderer.render(value, request);
+                return;
+            }
+        }
+
+        throw new CannotRenderException("Cannot renderer value '" + value + "'");
+    }
+
+    /** {@inheritDoc} */
+    public boolean isRenderable(String value, Request request) {
+        for (Renderer renderer : renderers) {
+            if (renderer.isRenderable(value, request)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/DispatchRenderer.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/DispatchRenderer.java
new file mode 100644
index 0000000..ef3ed25
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/DispatchRenderer.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.request.render;
+
+import java.io.IOException;
+
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.DispatchRequest;
+
+/**
+ * Renders an attribute that contains a reference to a template.
+ *
+ * @version $Rev$ $Date$
+ */
+public class DispatchRenderer implements Renderer {
+
+    /** {@inheritDoc} */
+    @Override
+    public void render(String path, Request request) throws IOException {
+        if (path == null) {
+            throw new CannotRenderException("Cannot dispatch a null path");
+        }
+        if (!(request instanceof DispatchRequest)) {
+            throw new CannotRenderException("Cannot dispatch outside of a web environment");
+        }
+
+        ((DispatchRequest) request).dispatch(path);
+    }
+
+    /** {@inheritDoc} */
+    public boolean isRenderable(String path, Request request) {
+        return path != null && path.startsWith("/");
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/NoSuchRendererException.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/NoSuchRendererException.java
new file mode 100644
index 0000000..3240d0f
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/NoSuchRendererException.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.request.render;
+
+
+/**
+ * It is raised when a named renderer has not been found with that name.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NoSuchRendererException extends RenderException {
+
+    /**
+     * Constructor.
+     */
+    public NoSuchRendererException() {
+        super();
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @param e The exception to be wrapped.
+     */
+    public NoSuchRendererException(String message, Throwable e) {
+        super(message, e);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     */
+    public NoSuchRendererException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param e The exception to be wrapped.
+     */
+    public NoSuchRendererException(Throwable e) {
+        super(e);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/PublisherRenderer.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/PublisherRenderer.java
new file mode 100644
index 0000000..a6a0baa
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/PublisherRenderer.java
@@ -0,0 +1,101 @@
+/*
+ * $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.request.render;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.tiles.request.Request;
+
+/**
+ * Provides a Publisher-Subscriber implementation around the provided renderer to delegate to.
+ *
+ * @version $Rev: 1035784 $ $Date: 2010-11-16 20:24:12 +0000 (Tue, 16 Nov 2010) $
+ */
+public class PublisherRenderer implements Renderer {
+
+    public interface RendererListener{
+        /** Called before the delegate's render method is called. */
+        void start(String template, Request request) throws IOException;
+        /** Called after the delegate's render method is called. */
+        void end(String template, Request request) throws IOException;
+        /** If the delegate render method throws an IOException it is passed through this. */
+        void handleIOException(IOException ex, Request request) throws IOException;
+    }
+
+    private final Renderer renderer;
+    private final List<RendererListener> listeners = new ArrayList<RendererListener>();
+    private final List<RendererListener> listenersReversed = new ArrayList<RendererListener>();
+
+    public PublisherRenderer(Renderer renderer){
+        this.renderer = renderer;
+    }
+
+    @Override
+    public void render(String path, Request request) throws IOException {
+        if (path == null) {
+            throw new CannotRenderException("Cannot dispatch a null path");
+        }
+        try{
+            for(RendererListener listener : listeners){
+                listener.start(path, request);
+            }
+            renderer.render(path, request);
+        }catch(IOException ex){
+            handleIOException(ex, request);
+        }finally{
+            for(RendererListener wrapper : listenersReversed){
+                wrapper.end(path, request);
+            }
+        }
+    }
+
+    @Override
+    public boolean isRenderable(String path, Request request) {
+        return renderer.isRenderable(path, request);
+    }
+
+    public void addListener(RendererListener listener){
+        listeners.add(listener);
+        listenersReversed.clear();
+        listenersReversed.addAll(listeners);
+        Collections.reverse(listenersReversed);
+    }
+
+    private void handleIOException(IOException exception, Request request) throws IOException{
+        IOException ex = exception;
+        boolean throwIt = listeners.isEmpty();
+        for(RendererListener listener : listenersReversed){
+            try{
+                listener.handleIOException(ex, request);
+                throwIt = false;
+            }catch(IOException newEx){
+                ex = newEx;
+                throwIt = true;
+            }
+        }
+        if(throwIt){
+            throw ex;
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/RenderException.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/RenderException.java
new file mode 100644
index 0000000..37fcee7
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/RenderException.java
@@ -0,0 +1,65 @@
+/*
+ * $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.request.render;
+
+import org.apache.tiles.request.RequestException;
+
+/**
+ * Thrown when rendering fails.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RenderException extends RequestException {
+
+    /**
+     * Constructor.
+     */
+    public RenderException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message of the exception.
+     */
+    public RenderException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param cause The cause.
+     */
+    public RenderException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message of the exception.
+     * @param cause The cause.
+     */
+    public RenderException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/Renderer.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/Renderer.java
new file mode 100644
index 0000000..79db2ac
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/Renderer.java
@@ -0,0 +1,53 @@
+/*
+ * $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.request.render;
+
+import java.io.IOException;
+
+import org.apache.tiles.request.Request;
+
+/**
+ * An object that can render a path, depending on the request passed as a
+ * parameter.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface Renderer {
+
+    /**
+     * Renders a path.
+     *
+     * @param path The path to render.
+     * @param request The Tiles request context.
+     * @throws IOException If something goes wrong during rendition.
+     */
+    void render(String path, Request request) throws IOException;
+
+    /**
+     * Checks if this renderer can render a path. Note that this does not mean
+     * it is the <strong>best</strong> renderer available, but checks only its capability.
+     *
+     * @param path The path to be rendered.
+     * @param request The request context.
+     * @return <code>true</code> if this renderer can render the attribute.
+     */
+    boolean isRenderable(String path, Request request);
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/RendererFactory.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/RendererFactory.java
new file mode 100644
index 0000000..163206f
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/RendererFactory.java
@@ -0,0 +1,48 @@
+/*
+ * $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.request.render;
+
+
+
+/**
+ * <p>
+ * Factory interface used to create/retrieve instances of the
+ * {@link AttributeRenderer} interface.
+ * </p>
+ *
+ * <p>
+ * This factory provides an extension point into the default tiles
+ * implementation. Implementors wishing to provide per request initialization of
+ * the AttributeRenderer (for instance) may provide a custom renderer.
+ * </p>
+ *
+ * @version $Rev$ $Date$
+ */
+public interface RendererFactory {
+
+    /**
+     * Returns a renderer by its name.
+     *
+     * @param name The name of the renderer.
+     * @return The renderer.
+     */
+    Renderer getRenderer(String name);
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/StringRenderer.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/StringRenderer.java
new file mode 100644
index 0000000..b31a57a
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/StringRenderer.java
@@ -0,0 +1,47 @@
+/*
+ * $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.request.render;
+
+import java.io.IOException;
+
+import org.apache.tiles.request.Request;
+
+/**
+ * Renders an attribute that contains a string.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StringRenderer implements Renderer {
+
+    /** {@inheritDoc} */
+    @Override
+    public void render(String value, Request request) throws IOException {
+        if (value == null) {
+            throw new CannotRenderException("Cannot render a null string");
+        }
+        request.getWriter().write(value);
+    }
+
+    /** {@inheritDoc} */
+    public boolean isRenderable(String value, Request request) {
+        return value != null;
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/package-info.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/package-info.java
new file mode 100644
index 0000000..1796814
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/package-info.java
@@ -0,0 +1,35 @@
+/*
+ * $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.
+ */
+/**
+ * Classes to allow rendering of a template (described by its path) 
+ * in a uniform way.
+ * 
+ * Besides the top level interface {@link org.apache.tiles.request.render.Renderer},
+ * the package contains:
+ * <ul>
+ * <li>trivial examples: {@link org.apache.tiles.request.render.StringRenderer} 
+ * and {@link org.apache.tiles.request.render.DispatchRenderer}.
+ * <li>usual design patterns: {@link org.apache.tiles.request.render.ChainedDelegateRenderer},
+ * {@link org.apache.tiles.request.render.PublisherRenderer}, and 
+ * {@link org.apache.tiles.request.render.RendererFactory}.
+ * </ul>
+ */
+package org.apache.tiles.request.render;
diff --git a/tiles-request/tiles-request-api/src/site/site.xml b/tiles-request/tiles-request-api/src/site/site.xml
new file mode 100644
index 0000000..8e92dd7
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Request Microframework">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Request Microframework"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/AbstractClientRequestTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/AbstractClientRequestTest.java
new file mode 100644
index 0000000..9a62e8a
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/AbstractClientRequestTest.java
@@ -0,0 +1,153 @@
+/*
+ * $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.request;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link AbstractClientRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AbstractClientRequestTest {
+
+    /**
+     * The request to test.
+     */
+    private AbstractClientRequest request;
+
+    /**
+     * The application context.
+     */
+    private ApplicationContext applicationContext;
+
+    /**
+     * The application scope.
+     */
+    private Map<String, Object> applicationScope;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        applicationContext = createMock(ApplicationContext.class);
+        applicationScope = new HashMap<String, Object>();
+        request = createMockBuilder(AbstractClientRequest.class)
+                .withConstructor(applicationContext).createMock();
+
+        expect(applicationContext.getApplicationScope()).andReturn(applicationScope).anyTimes();
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.AbstractClientRequest#dispatch(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testDispatch() throws IOException {
+        Map<String, Object> requestScope = new HashMap<String, Object>();
+
+        expect(request.getContext("request")).andReturn(requestScope).anyTimes();
+        request.doForward("/my/path.html");
+        request.doInclude("/my/path2.html");
+
+        replay(request, applicationContext);
+        request.dispatch("/my/path.html");
+        request.dispatch("/my/path2.html");
+        verify(request, applicationContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.AbstractClientRequest#include(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testInclude() throws IOException {
+        Map<String, Object> requestScope = new HashMap<String, Object>();
+
+        expect(request.getContext("request")).andReturn(requestScope).anyTimes();
+        request.doInclude("/my/path2.html");
+
+        replay(request, applicationContext);
+        request.include("/my/path2.html");
+        assertTrue((Boolean)request.getContext("request").get(AbstractRequest.FORCE_INCLUDE_ATTRIBUTE_NAME));
+        verify(request, applicationContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.AbstractClientRequest#getApplicationContext()}.
+     */
+    @Test
+    public void testGetApplicationContext() {
+        replay(request, applicationContext);
+        assertEquals(applicationContext, request.getApplicationContext());
+        verify(request, applicationContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.AbstractClientRequest#getContext(java.lang.String)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetContext() {
+        Map<String, Object> scope = createMock(Map.class);
+
+        expect(request.getContext("myScope")).andReturn(scope);
+
+        replay(request, applicationContext, scope);
+        assertEquals(scope, request.getContext("myScope"));
+        verify(request, applicationContext, scope);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.AbstractClientRequest#getAvailableScopes()}.
+     */
+    @Test
+    public void testGetAvailableScopes() {
+        String[] scopes = new String[] {"one", "two", "three"};
+
+        expect(request.getAvailableScopes()).andReturn(Arrays.asList(scopes));
+
+        replay(request, applicationContext);
+        assertArrayEquals(scopes, request.getAvailableScopes().toArray());
+        verify(request, applicationContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.AbstractClientRequest#getApplicationScope()}.
+     */
+    @Test
+    public void testGetApplicationScope() {
+        replay(request, applicationContext);
+        assertEquals(applicationScope, request.getApplicationScope());
+        verify(request, applicationContext);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/AbstractRequestTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/AbstractRequestTest.java
new file mode 100644
index 0000000..367fea7
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/AbstractRequestTest.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.request;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link AbstractRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AbstractRequestTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.AbstractRequest#setForceInclude(boolean)}.
+     */
+    @Test
+    public void testSetForceInclude() {
+        AbstractRequest request = createMockBuilder(AbstractRequest.class).createMock();
+        Map<String, Object> scope = new HashMap<String, Object>();
+
+        expect(request.getContext("request")).andReturn(scope).anyTimes();
+
+        replay(request);
+        assertFalse(request.isForceInclude());
+        request.setForceInclude(true);
+        assertTrue(request.isForceInclude());
+        verify(request);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/AbstractViewRequestTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/AbstractViewRequestTest.java
new file mode 100644
index 0000000..283648d
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/AbstractViewRequestTest.java
@@ -0,0 +1,122 @@
+/*
+ * $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.request;
+
+import static org.junit.Assert.*;
+import static org.easymock.classextension.EasyMock.*;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link AbstractViewRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AbstractViewRequestTest {
+
+    /**
+     * The request to test.
+     */
+    private AbstractViewRequest request;
+
+    /**
+     * The internal request.
+     */
+    private DispatchRequest wrappedRequest;
+
+    /**
+     * The application context.
+     */
+    private ApplicationContext applicationContext;
+
+    /**
+     * The application scope.
+     */
+    private Map<String, Object> applicationScope;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        wrappedRequest = createMock(DispatchRequest.class);
+        request = createMockBuilder(AbstractViewRequest.class).withConstructor(
+                wrappedRequest).createMock();
+        applicationContext = createMock(ApplicationContext.class);
+        applicationScope = new HashMap<String, Object>();
+
+        expect(wrappedRequest.getApplicationContext()).andReturn(applicationContext).anyTimes();
+        expect(applicationContext.getApplicationScope()).andReturn(applicationScope).anyTimes();
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.AbstractViewRequest#dispatch(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testDispatch() throws IOException {
+        Map<String, Object> requestScope = new HashMap<String, Object>();
+
+        expect(request.getContext("request")).andReturn(requestScope);
+        wrappedRequest.include("/my/path.html");
+
+        replay(wrappedRequest, request, applicationContext);
+        request.dispatch("/my/path.html");
+        assertTrue((Boolean) requestScope.get(AbstractRequest.FORCE_INCLUDE_ATTRIBUTE_NAME));
+        verify(wrappedRequest, request, applicationContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.AbstractViewRequest#include(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testInclude() throws IOException {
+        Map<String, Object> requestScope = new HashMap<String, Object>();
+
+        expect(request.getContext("request")).andReturn(requestScope);
+        wrappedRequest.include("/my/path.html");
+
+        replay(wrappedRequest, request, applicationContext);
+        request.include("/my/path.html");
+        assertTrue((Boolean) requestScope.get(AbstractRequest.FORCE_INCLUDE_ATTRIBUTE_NAME));
+        verify(wrappedRequest, request, applicationContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.AbstractViewRequest#doInclude(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testDoInclude() throws IOException {
+        wrappedRequest.include("/my/path.html");
+
+        replay(wrappedRequest, request, applicationContext);
+        request.doInclude("/my/path.html");
+        verify(wrappedRequest, request, applicationContext);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/ApplicationAccessTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/ApplicationAccessTest.java
new file mode 100644
index 0000000..4d4de4c
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/ApplicationAccessTest.java
@@ -0,0 +1,55 @@
+/*
+ * $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.request;
+
+import static org.junit.Assert.*;
+import static org.easymock.EasyMock.*;
+
+import java.util.Map;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link ApplicationAccess}.
+ *
+ * @version $Rev: 1066446 $ $Date: 2011-02-02 13:38:04 +0100 (Wed, 02 Feb 2011) $
+ */
+public class ApplicationAccessTest {
+
+    /**
+     * Test method for {@link ApplicationAccess#register(ApplicationContext)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testRegister() {
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        Map<String, Object> applicationScope = createMock(Map.class);
+
+        expect(applicationContext.getApplicationScope()).andReturn(applicationScope);
+        expect(applicationScope.put(ApplicationAccess
+                .APPLICATION_CONTEXT_ATTRIBUTE, applicationContext)).andReturn(null);
+
+        replay(applicationContext, applicationScope);
+        ApplicationAccess.register(applicationContext);
+        verify(applicationContext, applicationScope);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/ApplicationContextWrapperTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/ApplicationContextWrapperTest.java
new file mode 100644
index 0000000..f5ebfa7
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/ApplicationContextWrapperTest.java
@@ -0,0 +1,142 @@
+/*
+ * $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.request;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link ApplicationContextWrapper}.
+ *
+ * @version $Rev: 1066446 $ $Date: 2011-02-02 13:38:04 +0100 (Wed, 02 Feb 2011) $
+ */
+public class ApplicationContextWrapperTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.ApplicationContextWrapper#getWrappedApplicationContext()}.
+     */
+    @Test
+    public void testGetWrappedApplicationContext() {
+        ApplicationContext wrappedContext = createMock(ApplicationContext.class);
+
+        replay(wrappedContext);
+        ApplicationContextWrapper wrapper = new ApplicationContextWrapper(wrappedContext);
+        assertEquals(wrappedContext, wrapper.getWrappedApplicationContext());
+        verify(wrappedContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.ApplicationContextWrapper#getApplicationScope()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetApplicationScope() {
+        ApplicationContext wrappedContext = createMock(ApplicationContext.class);
+        Map<String, Object> applicationScope = createMock(Map.class);
+
+        expect(wrappedContext.getApplicationScope()).andReturn(applicationScope);
+
+        replay(wrappedContext, applicationScope);
+        ApplicationContextWrapper wrapper = new ApplicationContextWrapper(wrappedContext);
+        assertEquals(applicationScope, wrapper.getApplicationScope());
+        verify(wrappedContext, applicationScope);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.ApplicationContextWrapper#getContext()}.
+     */
+    @Test
+    public void testGetContext() {
+        ApplicationContext wrappedContext = createMock(ApplicationContext.class);
+        Object obj = createMock(Object.class);
+
+        expect(wrappedContext.getContext()).andReturn(obj);
+
+        replay(wrappedContext, obj);
+        ApplicationContextWrapper wrapper = new ApplicationContextWrapper(wrappedContext);
+        assertEquals(obj, wrapper.getContext());
+        verify(wrappedContext, obj);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.ApplicationContextWrapper#getInitParams()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetInitParams() {
+        ApplicationContext wrappedContext = createMock(ApplicationContext.class);
+        Map<String, String> obj = createMock(Map.class);
+
+        expect(wrappedContext.getInitParams()).andReturn(obj);
+
+        replay(wrappedContext, obj);
+        ApplicationContextWrapper wrapper = new ApplicationContextWrapper(wrappedContext);
+        assertEquals(obj, wrapper.getInitParams());
+        verify(wrappedContext, obj);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.ApplicationContextWrapper#getResource(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetResource() throws IOException {
+        ApplicationContext wrappedContext = createMock(ApplicationContext.class);
+        ApplicationResource obj = createMock(ApplicationResource.class);
+        ApplicationResource objFr = createMock(ApplicationResource.class);
+
+        expect(wrappedContext.getResource("whatever.html")).andReturn(obj);
+        expect(wrappedContext.getResource(obj, Locale.FRENCH)).andReturn(objFr);
+
+        replay(wrappedContext);
+        ApplicationContextWrapper wrapper = new ApplicationContextWrapper(wrappedContext);
+        assertEquals(obj, wrapper.getResource("whatever.html"));
+        assertEquals(objFr, wrapper.getResource(obj, Locale.FRENCH));
+        verify(wrappedContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.ApplicationContextWrapper#getResources(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetResources() throws IOException {
+        ApplicationContext wrappedContext = createMock(ApplicationContext.class);
+        Collection<ApplicationResource> obj = createMock(Collection.class);
+
+        expect(wrappedContext.getResources("whatever.html")).andReturn(obj);
+
+        replay(wrappedContext, obj);
+        ApplicationContextWrapper wrapper = new ApplicationContextWrapper(wrappedContext);
+        assertEquals(obj, wrapper.getResources("whatever.html"));
+        verify(wrappedContext, obj);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/DefaultRequestWrapperTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/DefaultRequestWrapperTest.java
new file mode 100644
index 0000000..c768d4c
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/DefaultRequestWrapperTest.java
@@ -0,0 +1,319 @@
+/*
+ * $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.request;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.classextension.EasyMock.createMock;
+import static org.easymock.classextension.EasyMock.replay;
+import static org.easymock.classextension.EasyMock.verify;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.tiles.request.attribute.Addable;
+import org.junit.Test;
+
+/**
+ * Tests {@link DefaultRequestWrapper}.
+ *
+ * @version $Rev: 1215009 $ $Date: 2011-12-16 01:32:31 +0100 (Fri, 16 Dec 2011) $
+ */
+public class DefaultRequestWrapperTest {
+
+    /**
+     * Creates the RequestWrapper to be tested.
+     *
+     * @param wrappedRequest the request to be wrapped.
+     * @return the RequestWrapper.
+     */
+    protected RequestWrapper createRequestWrapper(Request wrappedRequest) {
+        DefaultRequestWrapper request = new DefaultRequestWrapper(wrappedRequest);
+        return request;
+    }
+
+    /**
+     * Creates a mock Request adequate to the test.
+     *
+     * @return the Request object.
+     */
+    protected Request createMockRequest() {
+        Request wrappedRequest = createMock(Request.class);
+        return wrappedRequest;
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getWrappedRequest()}.
+     */
+    @Test
+    public void testGetWrappedRequest() {
+        Request wrappedRequest = createMockRequest();
+
+        replay(wrappedRequest);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertEquals(wrappedRequest, request.getWrappedRequest());
+        verify(wrappedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getHeader()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetHeader() {
+        Request wrappedRequest = createMockRequest();
+        Map<String, String> header = createMock(Map.class);
+
+        expect(wrappedRequest.getHeader()).andReturn(header);
+
+        replay(wrappedRequest);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertEquals(header, request.getHeader());
+        verify(wrappedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getResponseHeaders()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetResponseHeaders() {
+        Request wrappedRequest = createMockRequest();
+        Addable<String> header = createMock(Addable.class);
+
+        expect(wrappedRequest.getResponseHeaders()).andReturn(header);
+
+        replay(wrappedRequest);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertEquals(header, request.getResponseHeaders());
+        verify(wrappedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getHeaderValues()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetHeaderValues() {
+        Request wrappedRequest = createMockRequest();
+        Map<String, String[]> header = createMock(Map.class);
+
+        expect(wrappedRequest.getHeaderValues()).andReturn(header);
+
+        replay(wrappedRequest);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertEquals(header, request.getHeaderValues());
+        verify(wrappedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getContext(java.lang.String)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetContext() {
+        Request wrappedRequest = createMockRequest();
+        Map<String, Object> context = createMock(Map.class);
+
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+
+        expect(wrappedRequest.getContext("one")).andReturn(context);
+
+        replay(wrappedRequest, context);
+        assertEquals(context, request.getContext("one"));
+        verify(wrappedRequest, context);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getAvailableScopes()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetAvailableScopes() {
+        Request wrappedRequest = createMockRequest();
+        Map<String, Object> context = createMock(Map.class);
+
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+
+        String[] scopes = new String[] {"one", "two", "three"};
+        expect(wrappedRequest.getAvailableScopes()).andReturn(Arrays.asList(scopes));
+
+        replay(wrappedRequest, context);
+        assertArrayEquals(scopes, request.getAvailableScopes().toArray());
+        verify(wrappedRequest, context);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getApplicationContext()}.
+     */
+    @Test
+    public void testGetApplicationContext() {
+        Request wrappedRequest = createMockRequest();
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+
+        expect(wrappedRequest.getApplicationContext()).andReturn(applicationContext);
+
+        replay(wrappedRequest, applicationContext);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertEquals(applicationContext, request.getApplicationContext());
+        verify(wrappedRequest, applicationContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getOutputStream()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetOutputStream() throws IOException {
+        Request wrappedRequest = createMockRequest();
+        OutputStream stream = createMock(OutputStream.class);
+
+        expect(wrappedRequest.getOutputStream()).andReturn(stream);
+
+        replay(wrappedRequest, stream);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertEquals(stream, request.getOutputStream());
+        verify(wrappedRequest, stream);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getWriter()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetWriter() throws IOException {
+        Request wrappedRequest = createMockRequest();
+        Writer writer = createMock(Writer.class);
+
+        expect(wrappedRequest.getWriter()).andReturn(writer);
+
+        replay(wrappedRequest, writer);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertEquals(writer, request.getWriter());
+        verify(wrappedRequest, writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getPrintWriter()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetPrintWriter() throws IOException {
+        Request wrappedRequest = createMockRequest();
+        PrintWriter writer = createMock(PrintWriter.class);
+
+        expect(wrappedRequest.getPrintWriter()).andReturn(writer);
+
+        replay(wrappedRequest, writer);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertEquals(writer, request.getPrintWriter());
+        verify(wrappedRequest, writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#isResponseCommitted()}.
+     */
+    @Test
+    public void testIsResponseCommitted() {
+        Request wrappedRequest = createMockRequest();
+
+        expect(wrappedRequest.isResponseCommitted()).andReturn(Boolean.TRUE);
+
+        replay(wrappedRequest);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertTrue(request.isResponseCommitted());
+        verify(wrappedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getParam()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetParam() {
+        Request wrappedRequest = createMockRequest();
+        Map<String, String> param = createMock(Map.class);
+
+        expect(wrappedRequest.getParam()).andReturn(param);
+
+        replay(wrappedRequest, param);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertEquals(param, request.getParam());
+        verify(wrappedRequest, param);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getParamValues()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetParamValues() {
+        Request wrappedRequest = createMockRequest();
+        Map<String, String[]> param = createMock(Map.class);
+
+        expect(wrappedRequest.getParamValues()).andReturn(param);
+
+        replay(wrappedRequest, param);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertEquals(param, request.getParamValues());
+        verify(wrappedRequest, param);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#getRequestLocale()}.
+     */
+    @Test
+    public void testGetRequestLocale() {
+        Request wrappedRequest = createMockRequest();
+        Locale param = Locale.ITALY;
+
+        expect(wrappedRequest.getRequestLocale()).andReturn(param);
+
+        replay(wrappedRequest);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertEquals(param, request.getRequestLocale());
+        verify(wrappedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DefaultRequestWrapper#isUserInRole(java.lang.String)}.
+     */
+    @Test
+    public void testIsUserInRole() {
+        Request wrappedRequest = createMockRequest();
+
+        expect(wrappedRequest.isUserInRole("myrole")).andReturn(Boolean.TRUE);
+
+        replay(wrappedRequest);
+        RequestWrapper request = createRequestWrapper(wrappedRequest);
+        assertTrue(request.isUserInRole("myrole"));
+        verify(wrappedRequest);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/DispatchRequestWrapperTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/DispatchRequestWrapperTest.java
new file mode 100644
index 0000000..a9b05ad
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/DispatchRequestWrapperTest.java
@@ -0,0 +1,96 @@
+/*
+ * $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.request;
+
+import static org.easymock.classextension.EasyMock.createMock;
+import static org.easymock.classextension.EasyMock.replay;
+import static org.easymock.classextension.EasyMock.verify;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link DispatchRequestWrapper}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class DispatchRequestWrapperTest extends DefaultRequestWrapperTest {
+
+    @Override
+    protected DispatchRequest createMockRequest() {
+        DispatchRequest wrappedRequest = createMock(DispatchRequest.class);
+        return wrappedRequest;
+    }
+
+    @Override
+    protected DispatchRequestWrapper createRequestWrapper(Request wrappedRequest) {
+        DispatchRequestWrapper request = new DispatchRequestWrapper((DispatchRequest) wrappedRequest);
+        return request;
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DispatchRequestWrapper#dispatch(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testDispatch() throws IOException {
+        DispatchRequest wrappedRequest = createMockRequest();
+
+        wrappedRequest.dispatch("/my/path.html");
+
+        replay(wrappedRequest);
+        DispatchRequestWrapper request = createRequestWrapper(wrappedRequest);
+        request.dispatch("/my/path.html");
+        verify(wrappedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DispatchRequestWrapper#include(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testInclude() throws IOException {
+        DispatchRequest wrappedRequest = createMockRequest();
+
+        wrappedRequest.include("/my/path.html");
+
+        replay(wrappedRequest);
+        DispatchRequestWrapper request = createRequestWrapper(wrappedRequest);
+        request.include("/my/path.html");
+        verify(wrappedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.DispatchRequestWrapper#setContentType(java.lang.String)}.
+     */
+    @Test
+    public void testSetContentType() {
+        DispatchRequest wrappedRequest = createMockRequest();
+
+        wrappedRequest.setContentType("text/html");
+
+        replay(wrappedRequest);
+        DispatchRequestWrapper request = createRequestWrapper(wrappedRequest);
+        request.setContentType("text/html");
+        verify(wrappedRequest);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/NotAvailableFeatureExceptionTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/NotAvailableFeatureExceptionTest.java
new file mode 100644
index 0000000..da14b2b
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/NotAvailableFeatureExceptionTest.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.request;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link NotAvailableFeatureException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NotAvailableFeatureExceptionTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.NotAvailableFeatureException#NotAvailableFeatureException()}.
+     */
+    @Test
+    public void testNotAvailableFeatureException() {
+        NotAvailableFeatureException exception = new NotAvailableFeatureException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link NotAvailableFeatureException#NotAvailableFeatureException(String)}.
+     */
+    @Test
+    public void testNotAvailableFeatureExceptionString() {
+        NotAvailableFeatureException exception = new NotAvailableFeatureException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link NotAvailableFeatureException#NotAvailableFeatureException(Throwable)}.
+     */
+    @Test
+    public void testNotAvailableFeatureExceptionThrowable() {
+        Throwable cause = new Throwable();
+        NotAvailableFeatureException exception = new NotAvailableFeatureException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for {@link NotAvailableFeatureException#NotAvailableFeatureException(String, Throwable)}.
+     */
+    @Test
+    public void testNotAvailableFeatureExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        NotAvailableFeatureException exception = new NotAvailableFeatureException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/RequestExceptionTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/RequestExceptionTest.java
new file mode 100644
index 0000000..d3693d7
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/RequestExceptionTest.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.request;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link RequestException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RequestExceptionTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.RequestException#RequestException()}.
+     */
+    @Test
+    public void testRequestException() {
+        RequestException exception = new RequestException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.RequestException#RequestException(java.lang.String)}.
+     */
+    @Test
+    public void testRequestExceptionString() {
+        RequestException exception = new RequestException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.RequestException#RequestException(java.lang.Throwable)}.
+     */
+    @Test
+    public void testRequestExceptionThrowable() {
+        Throwable cause = new Throwable();
+        RequestException exception = new RequestException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for {@link RequestException#RequestException(String, Throwable)}.
+     */
+    @Test
+    public void testRequestExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        RequestException exception = new RequestException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/AddableParameterMapTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/AddableParameterMapTest.java
new file mode 100644
index 0000000..7e702da
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/AddableParameterMapTest.java
@@ -0,0 +1,114 @@
+/*
+ * $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.request.collection;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tiles.request.attribute.HasAddableKeys;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link AddableParameterMap}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class AddableParameterMapTest {
+
+    /**
+     * The object to test.
+     */
+    private AddableParameterMap map;
+
+    /**
+     * The extractor to use.
+     */
+    private HasAddableKeys<String> extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        extractor = createMock(HasAddableKeys.class);
+        map = new AddableParameterMap(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.AddableParameterMap#entrySet()}.
+     */
+    @Test
+    public void testEntrySet() {
+        Set<Map.Entry<String, String>> entrySet = map.entrySet();
+        MapEntry<String, String> entry1 = new MapEntry<String, String>("one", "value1", false);
+        MapEntry<String, String> entry2 = new MapEntry<String, String>("two", "value2", false);
+        List<Map.Entry<String, String>> entries = new ArrayList<Map.Entry<String, String>>(2);
+        entries.add(entry1);
+        entries.add(entry2);
+
+        extractor.setValue("one", "value1");
+        expectLastCall().times(2);
+        extractor.setValue("two", "value2");
+        replay(extractor);
+        entrySet.add(entry1);
+        entrySet.addAll(entries);
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link AddableParameterMap#put(String, String)}.
+     */
+    @Test
+    public void testPut() {
+        expect(extractor.getValue("one")).andReturn(null);
+        extractor.setValue("one", "value1");
+
+        replay(extractor);
+        assertNull(map.put("one", "value1"));
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.AddableParameterMap#putAll(java.util.Map)}.
+     */
+    @Test
+    public void testPutAll() {
+        Map<String, String> map = new HashMap<String, String>();
+        map.put("one", "value1");
+        map.put("two", "value2");
+
+        extractor.setValue("one", "value1");
+        extractor.setValue("two", "value2");
+
+        replay(extractor);
+        this.map.putAll(map);
+        verify(extractor);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/CollectionUtilTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/CollectionUtilTest.java
new file mode 100644
index 0000000..4fcc496
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/CollectionUtilTest.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.request.collection;
+
+import org.apache.tiles.request.collection.CollectionUtil;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import org.junit.Test;
+
+/**
+ * Test {@link RequestUtil}.
+ *
+ * @version $Rev: 1066446 $ $Date: 2011-02-02 13:38:04 +0100 (Wed, 02 Feb 2011) $
+ */
+public class CollectionUtilTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.RequestUtil#key(java.lang.Object)}.
+     */
+    @Test
+    public void testKey() {
+        assertEquals("1", CollectionUtil.key(1));
+        assertEquals("hello", CollectionUtil.key("hello"));
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.RequestUtil#key(java.lang.Object)}.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testKeyException() {
+        CollectionUtil.key(null);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.RequestUtil#enumerationSize(java.util.Enumeration)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testEnumerationSize() {
+        Enumeration<Object> enumeration = createMock(Enumeration.class);
+
+        expect(enumeration.hasMoreElements()).andReturn(true);
+        expect(enumeration.nextElement()).andReturn(1);
+        expect(enumeration.hasMoreElements()).andReturn(true);
+        expect(enumeration.nextElement()).andReturn(1);
+        expect(enumeration.hasMoreElements()).andReturn(false);
+
+        replay(enumeration);
+        assertEquals(2, CollectionUtil.enumerationSize(enumeration));
+        verify(enumeration);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/HeaderValuesCollectionTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/HeaderValuesCollectionTest.java
new file mode 100644
index 0000000..d845a0e
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/HeaderValuesCollectionTest.java
@@ -0,0 +1,419 @@
+/*
+ * $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.request.collection;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tiles.request.attribute.EnumeratedValuesExtractor;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link HeaderValuesMap#values()}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class HeaderValuesCollectionTest {
+
+
+    /**
+     * The extractor to use.
+     */
+    private EnumeratedValuesExtractor extractor;
+
+    /**
+     * The map to test.
+     */
+    private HeaderValuesMap map;
+
+    /**
+     * The collection.
+     */
+    private Collection<String[]> coll;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        extractor = createMock(EnumeratedValuesExtractor.class);
+        map = new HeaderValuesMap(extractor);
+        coll = map.values();
+    }
+
+    /**
+     * Tests {@link Collection#add(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAdd() {
+        coll.add(null);
+    }
+
+    /**
+     * Tests {@link Collection#addAll(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAddAll() {
+        coll.addAll(null);
+    }
+
+    /**
+     * Tests {@link Collection#clear(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testClear() {
+        coll.clear();
+    }
+
+    /**
+     * Tests {@link Collection#contains(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsValue() {
+        assertFalse(map.containsValue(1));
+
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys, values1, values2);
+        assertTrue(coll.contains(new String[] {"value2", "value3"}));
+        verify(extractor, keys, values1, values2);
+    }
+
+    /**
+     * Tests {@link Collection#contains(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsValueFalse() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys, values1, values2);
+        assertFalse(coll.contains(new String[] {"value2", "value4"}));
+        verify(extractor, keys, values1, values2);
+    }
+
+    /**
+     * Tests {@link Collection#containsAll(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsAll() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys).times(2);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+
+        expect(extractor.getValues("one")).andReturn(values1).times(2);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys, values1, values2);
+        List<String[]> coll = new ArrayList<String[]>();
+        coll.add(new String[] {"value1"});
+        coll.add(new String[] {"value2", "value3"});
+        assertTrue(this.coll.containsAll(coll));
+        verify(extractor, keys, values1, values2);
+    }
+
+    /**
+     * Tests {@link Collection#containsAll(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsAllFalse() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+
+        replay(extractor, keys, values1, values2);
+        List<String[]> coll = new ArrayList<String[]>();
+        coll.add(new String[] {"value4"});
+        assertFalse(this.coll.containsAll(coll));
+        verify(extractor, keys, values1, values2);
+    }
+
+    /**
+     * Test method for {@link Collection#isEmpty()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIsEmpty() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+
+        replay(extractor, keys);
+        assertFalse(coll.isEmpty());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Collection#iterator()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIterator() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys, values2);
+        Iterator<String[]> entryIt = coll.iterator();
+        assertTrue(entryIt.hasNext());
+        assertArrayEquals(new String[] { "value2", "value3" }, entryIt.next());
+        verify(extractor, keys, values2);
+    }
+
+    /**
+     * Test method for {@link Collection#iterator()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test(expected = UnsupportedOperationException.class)
+    public void testIteratorRemove() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+
+        try {
+            replay(extractor, keys);
+            coll.iterator().remove();
+        } finally {
+            verify(extractor, keys);
+        }
+    }
+
+    /**
+     * Tests {@link Collection#remove(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemove() {
+        coll.remove(null);
+    }
+
+    /**
+     * Tests {@link Collection#removeAll(java.util.Collection)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemoveAll() {
+        coll.removeAll(null);
+    }
+
+    /**
+     * Tests {@link Collection#retainAll(java.util.Collection)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRetainAll() {
+        coll.retainAll(null);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#size()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSize() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys);
+        assertEquals(2, coll.size());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Collection#toArray()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testToArray() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        String[][] entryArray = new String[2][];
+        entryArray[0] = new String[] {"value1"};
+        entryArray[1] = new String[] {"value2", "value3"};
+
+        replay(extractor, keys, values1, values2);
+        assertArrayEquals(entryArray, coll.toArray());
+        verify(extractor, keys, values1, values2);
+    }
+
+    /**
+     * Test method for {@link Collection#toArray(Object[])}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testToArrayTArray() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        String[][] entryArray = new String[2][];
+        entryArray[0] = new String[] {"value1"};
+        entryArray[1] = new String[] {"value2", "value3"};
+        String[][] realArray = new String[2][];
+
+        replay(extractor, keys, values1, values2);
+        assertArrayEquals(entryArray, coll.toArray(realArray));
+        verify(extractor, keys, values1, values2);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/HeaderValuesMapEntrySetTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/HeaderValuesMapEntrySetTest.java
new file mode 100644
index 0000000..1e94d53
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/HeaderValuesMapEntrySetTest.java
@@ -0,0 +1,360 @@
+/*
+ * $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.request.collection;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tiles.request.attribute.EnumeratedValuesExtractor;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link HeaderValuesMap entry set}.
+ *
+ */
+public class HeaderValuesMapEntrySetTest {
+
+
+    /**
+     * The extractor to use.
+     */
+    private EnumeratedValuesExtractor extractor;
+
+    /**
+     * The map to test.
+     */
+    private HeaderValuesMap map;
+
+    /**
+     * The set to test.
+     */
+    private Set<Map.Entry<String, String[]>> entrySet;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        extractor = createMock(EnumeratedValuesExtractor.class);
+        map = new HeaderValuesMap(extractor);
+        entrySet = map.entrySet();
+    }
+
+    /**
+     * Tests {@link Set#add(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAdd() {
+        entrySet.add(null);
+    }
+
+    /**
+     * Tests {@link Set#addAll(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAddAll() {
+        entrySet.addAll(null);
+    }
+
+    /**
+     * Tests {@link Set#clear(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testClear() {
+        entrySet.clear();
+    }
+
+    /**
+     * Tests {@link Set#contains(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContains() {
+        Map.Entry<String, String[]> entry = createMock(Map.Entry.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(entry.getKey()).andReturn("two");
+        expect(entry.getValue()).andReturn(new String[] {"value2", "value3"});
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, entry, values2);
+        assertTrue(entrySet.contains(entry));
+        verify(extractor, entry, values2);
+    }
+
+    /**
+     * Tests {@link Set#containsAll(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsAll() {
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+        Map.Entry<String, String[]> entry1 = createMock(Map.Entry.class);
+        Map.Entry<String, String[]> entry2 = createMock(Map.Entry.class);
+
+        expect(entry1.getKey()).andReturn("one");
+        expect(entry1.getValue()).andReturn(new String[] {"value1"});
+        expect(entry2.getKey()).andReturn("two");
+        expect(entry2.getValue()).andReturn(new String[] {"value2", "value3"});
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, values1, values2, entry1, entry2);
+        List<Map.Entry<String, String[]>> coll = new ArrayList<Map.Entry<String, String[]>>();
+        coll.add(entry1);
+        coll.add(entry2);
+        assertTrue(entrySet.containsAll(coll));
+        verify(extractor, values1, values2, entry1, entry2);
+    }
+
+    /**
+     * Tests {@link Set#containsAll(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsAllFalse() {
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Map.Entry<String, String[]> entry1 = createMock(Map.Entry.class);
+
+        expect(entry1.getKey()).andReturn("one");
+        expect(entry1.getValue()).andReturn(new String[] {"value4"});
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+
+        replay(extractor, values1, entry1);
+        List<Map.Entry<String, String[]>> coll = new ArrayList<Map.Entry<String, String[]>>();
+        coll.add(entry1);
+        assertFalse(entrySet.containsAll(coll));
+        verify(extractor, values1, entry1);
+    }
+
+    /**
+     * Test method for {@link Set#isEmpty()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIsEmpty() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+
+        replay(extractor, keys);
+        assertFalse(entrySet.isEmpty());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Set#iterator()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIterator() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys, values2);
+        Iterator<Map.Entry<String, String[]>> entryIt = entrySet.iterator();
+        assertTrue(entryIt.hasNext());
+        MapEntryArrayValues<String, String> entry = new MapEntryArrayValues<String, String>(
+                "two", new String[] { "value2", "value3" }, false);
+        assertEquals(entry, entryIt.next());
+        verify(extractor, keys, values2);
+    }
+
+    /**
+     * Test method for {@link Set#iterator()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test(expected = UnsupportedOperationException.class)
+    public void testIteratorRemove() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+
+        try {
+            replay(extractor, keys);
+            entrySet.iterator().remove();
+        } finally {
+            verify(extractor, keys);
+        }
+    }
+
+    /**
+     * Tests {@link Set#remove(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemove() {
+        entrySet.remove(null);
+    }
+
+    /**
+     * Tests {@link Set#removeAll(java.util.Collection)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemoveAll() {
+        entrySet.removeAll(null);
+    }
+
+    /**
+     * Tests {@link Set#retainAll(java.util.Collection)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRetainAll() {
+        entrySet.retainAll(null);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#size()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSize() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys);
+        assertEquals(2, entrySet.size());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Set#toArray()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testToArray() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        MapEntryArrayValues<String, String>[] entryArray = new MapEntryArrayValues[2];
+        entryArray[0] = new MapEntryArrayValues<String, String>("one", new String[] {"value1"}, false);
+        entryArray[1] = new MapEntryArrayValues<String, String>("two", new String[] {"value2", "value3"}, false);
+
+        replay(extractor, keys, values1, values2);
+        assertArrayEquals(entryArray, entrySet.toArray());
+        verify(extractor, keys, values1, values2);
+    }
+
+    /**
+     * Test method for {@link Set#toArray(Object[])}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testToArrayTArray() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        MapEntryArrayValues<String, String>[] entryArray = new MapEntryArrayValues[2];
+        entryArray[0] = new MapEntryArrayValues<String, String>("one", new String[] {"value1"}, false);
+        entryArray[1] = new MapEntryArrayValues<String, String>("two", new String[] {"value2", "value3"}, false);
+        MapEntryArrayValues<String, String>[] realArray = new MapEntryArrayValues[2];
+
+        replay(extractor, keys, values1, values2);
+        assertArrayEquals(entryArray, entrySet.toArray(realArray));
+        verify(extractor, keys, values1, values2);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/HeaderValuesMapTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/HeaderValuesMapTest.java
new file mode 100644
index 0000000..715e5d9
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/HeaderValuesMapTest.java
@@ -0,0 +1,344 @@
+/*
+ * $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.request.collection;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+
+import org.apache.tiles.request.attribute.EnumeratedValuesExtractor;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link HeaderValuesMap}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class HeaderValuesMapTest {
+
+    /**
+     * The extractor to use.
+     */
+    private EnumeratedValuesExtractor extractor;
+
+    /**
+     * The map to test.
+     */
+    private HeaderValuesMap map;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        extractor = createMock(EnumeratedValuesExtractor.class);
+        map = new HeaderValuesMap(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#hashCode()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testHashCode() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys, values1, values2);
+        assertEquals(
+                ("one".hashCode() ^ "value1".hashCode())
+                        + ("two".hashCode() ^ ("value2".hashCode() + "value3"
+                                .hashCode())),
+                map.hashCode());
+        verify(extractor, keys, values1, values2);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#clear()}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testClear() {
+        map.clear();
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#containsKey(java.lang.Object)}.
+     */
+    @Test
+    public void testContainsKey() {
+        expect(extractor.getValue("one")).andReturn("value1");
+        expect(extractor.getValue("two")).andReturn(null);
+
+        replay(extractor);
+        assertTrue(map.containsKey("one"));
+        assertFalse(map.containsKey("two"));
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#containsValue(java.lang.Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsValue() {
+        assertFalse(map.containsValue(1));
+
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys, values1, values2);
+        assertTrue(map.containsValue(new String[] {"value2", "value3"}));
+        verify(extractor, keys, values1, values2);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#containsValue(java.lang.Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsValueFalse() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys, values1, values2);
+        assertFalse(map.containsValue(new String[] {"value2", "value4"}));
+        verify(extractor, keys, values1, values2);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#equals(java.lang.Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testEqualsObject() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+        EnumeratedValuesExtractor otherExtractor = createMock(EnumeratedValuesExtractor.class);
+        Enumeration<String> otherValues1 = createMock(Enumeration.class);
+        Enumeration<String> otherValues2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("one")).andReturn(values1);
+        expect(values1.hasMoreElements()).andReturn(true);
+        expect(values1.nextElement()).andReturn("value1");
+        expect(values1.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        expect(otherExtractor.getValues("one")).andReturn(otherValues1);
+        expect(otherValues1.hasMoreElements()).andReturn(true);
+        expect(otherValues1.nextElement()).andReturn("value1");
+        expect(otherValues1.hasMoreElements()).andReturn(false);
+
+        expect(otherExtractor.getValues("two")).andReturn(otherValues2);
+        expect(otherValues2.hasMoreElements()).andReturn(true);
+        expect(otherValues2.nextElement()).andReturn("value2");
+        expect(otherValues2.hasMoreElements()).andReturn(true);
+        expect(otherValues2.nextElement()).andReturn("value3");
+        expect(otherValues2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, otherExtractor, keys, values1, values2, otherValues1, otherValues2);
+        HeaderValuesMap otherMap = new HeaderValuesMap(otherExtractor);
+        assertTrue(map.equals(otherMap));
+        verify(extractor, otherExtractor, keys, values1, values2, otherValues1, otherValues2);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#get(java.lang.Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGet() {
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getValues("two")).andReturn(values2);
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value2");
+        expect(values2.hasMoreElements()).andReturn(true);
+        expect(values2.nextElement()).andReturn("value3");
+        expect(values2.hasMoreElements()).andReturn(false);
+
+        replay(extractor, values2);
+        assertArrayEquals(new String[] {"value2", "value3"}, map.get("two"));
+        verify(extractor, values2);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#isEmpty()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIsEmpty() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+
+        replay(extractor, keys);
+        assertFalse(map.isEmpty());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#isEmpty()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIsEmptyTrue() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys);
+        assertTrue(map.isEmpty());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#keySet()}.
+     */
+    @Test
+    public void testKeySet() {
+        replay(extractor);
+        assertTrue(map.keySet() instanceof KeySet);
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link HeaderValuesMap#put(String, String[])}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testPut() {
+        map.put("one", new String[] {"value1", "value2"});
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#putAll(java.util.Map)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testPutAll() {
+        map.putAll(new HashMap<String, String[]>());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#remove(java.lang.Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemove() {
+        map.remove("one");
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#size()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSize() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys);
+        assertEquals(2, map.size());
+        verify(extractor, keys);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/KeySetTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/KeySetTest.java
new file mode 100644
index 0000000..e7a939e
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/KeySetTest.java
@@ -0,0 +1,301 @@
+/*
+ * $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.request.collection;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.tiles.request.attribute.HasKeys;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link KeySet}.
+ *
+ */
+public class KeySetTest {
+
+
+    /**
+     * The extractor to use.
+     */
+    private HasKeys<Integer> extractor;
+
+    /**
+     * The key set.
+     */
+    private Set<String> entrySet;
+
+    /**
+     * Sets up the test.
+     */
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        extractor = createMock(HasKeys.class);
+        entrySet = new KeySet(extractor);
+    }
+
+    /**
+     * Tests {@link Set#add(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAdd() {
+        entrySet.add(null);
+    }
+
+    /**
+     * Tests {@link Set#addAll(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAddAll() {
+        entrySet.addAll(null);
+    }
+
+    /**
+     * Tests {@link Set#clear(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testClear() {
+        entrySet.clear();
+    }
+
+    /**
+     * Tests {@link Set#contains(Object)}.
+     */
+    @Test
+    public void testContains() {
+        expect(extractor.getValue("one")).andReturn(1);
+
+        replay(extractor);
+        assertTrue(entrySet.contains("one"));
+        verify(extractor);
+    }
+
+    /**
+     * Tests {@link Set#contains(Object)}.
+     */
+    @Test
+    public void testContainsFalse() {
+        expect(extractor.getValue("one")).andReturn(null);
+
+        replay(extractor);
+        assertFalse(entrySet.contains("one"));
+        verify(extractor);
+    }
+
+    /**
+     * Tests {@link Set#containsAll(Object)}.
+     */
+    @Test
+    public void testContainsAll() {
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(1);
+
+        replay(extractor);
+        List<String> coll = new ArrayList<String>();
+        coll.add("one");
+        coll.add("two");
+        assertTrue(entrySet.containsAll(coll));
+        verify(extractor);
+    }
+
+    /**
+     * Tests {@link Set#containsAll(Object)}.
+     */
+    @Test
+    public void testContainsAllFalse() {
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(null);
+
+        replay(extractor);
+        List<String> coll = new ArrayList<String>();
+        coll.add("one");
+        coll.add("two");
+        assertFalse(entrySet.containsAll(coll));
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link Set#isEmpty()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIsEmpty() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+
+        replay(extractor, keys);
+        assertFalse(entrySet.isEmpty());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Set#isEmpty()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIsEmptyTrue() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys);
+        assertTrue(entrySet.isEmpty());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Set#iterator()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIterator() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+
+        replay(extractor, keys, values2);
+        Iterator<String> entryIt = entrySet.iterator();
+        assertTrue(entryIt.hasNext());
+        assertEquals("two", entryIt.next());
+        verify(extractor, keys, values2);
+    }
+
+    /**
+     * Test method for {@link Set#iterator()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test(expected = UnsupportedOperationException.class)
+    public void testIteratorRemove() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+
+        try {
+            replay(extractor, keys);
+            entrySet.iterator().remove();
+        } finally {
+            verify(extractor, keys);
+        }
+    }
+
+    /**
+     * Tests {@link Set#remove(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemove() {
+        entrySet.remove(null);
+    }
+
+    /**
+     * Tests {@link Set#removeAll(java.util.Collection)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemoveAll() {
+        entrySet.removeAll(null);
+    }
+
+    /**
+     * Tests {@link Set#retainAll(java.util.Collection)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRetainAll() {
+        entrySet.retainAll(null);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#size()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSize() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys);
+        assertEquals(2, entrySet.size());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Set#toArray()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testToArray() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+        replay(extractor, keys, values1, values2);
+        assertArrayEquals(new String[] {"one", "two"}, entrySet.toArray());
+        verify(extractor, keys, values1, values2);
+    }
+
+    /**
+     * Test method for {@link Set#toArray(Object[])}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testToArrayTArray() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys, values1, values2);
+        String[] realArray = new String[2];
+        assertArrayEquals(new String[] {"one", "two"}, entrySet.toArray(realArray));
+        verify(extractor, keys, values1, values2);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/MapEntryArrayValuesTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/MapEntryArrayValuesTest.java
new file mode 100644
index 0000000..3a04d58
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/MapEntryArrayValuesTest.java
@@ -0,0 +1,95 @@
+/*
+ * $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.request.collection;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link MapEntryArrayValues}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class MapEntryArrayValuesTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.MapEntryArrayValues#hashCode()}.
+     */
+    @Test
+    public void testHashCode() {
+        MapEntryArrayValues<String, String> entry = new MapEntryArrayValues<String, String>(
+                "key", new String[] { "value1", "value2" }, false);
+        assertEquals("key".hashCode() ^ ("value1".hashCode() + "value2".hashCode()), entry.hashCode());
+        entry = new MapEntryArrayValues<String, String>(
+                null, new String[] { "value1", "value2" }, false);
+        assertEquals(0 ^ ("value1".hashCode() + "value2".hashCode()), entry.hashCode());
+        entry = new MapEntryArrayValues<String, String>(
+                "key", null, false);
+        assertEquals("key".hashCode() ^ 0, entry.hashCode());
+        entry = new MapEntryArrayValues<String, String>(
+                null, null, false);
+        assertEquals(0 ^ 0, entry.hashCode());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.MapEntryArrayValues#equals(java.lang.Object)}.
+     */
+    @Test
+    public void testEqualsObject() {
+        MapEntryArrayValues<String, String> entry = new MapEntryArrayValues<String, String>(
+                "key", new String[] { "value1", "value2" }, false);
+        assertFalse(entry.equals(null));
+        assertFalse(entry.equals("whatever"));
+        MapEntryArrayValues<String, String> entry2 = new MapEntryArrayValues<String, String>(
+                "key", new String[] { "value1", "value2" }, false);
+        assertTrue(entry.equals(entry2));
+        entry2 = new MapEntryArrayValues<String, String>(
+                "key", null, false);
+        assertFalse(entry.equals(entry2));
+        entry2 = new MapEntryArrayValues<String, String>("key2", new String[] {
+                "value1", "value2" }, false);
+        assertFalse(entry.equals(entry2));
+        entry2 = new MapEntryArrayValues<String, String>("key", new String[] {
+                "value1", "value3" }, false);
+        assertFalse(entry.equals(entry2));
+        entry = new MapEntryArrayValues<String, String>(null, new String[] {
+                "value1", "value2" }, false);
+        entry2 = new MapEntryArrayValues<String, String>(null, new String[] {
+                "value1", "value2" }, false);
+        assertTrue(entry.equals(entry2));
+        entry = new MapEntryArrayValues<String, String>("key", null, false);
+        entry2 = new MapEntryArrayValues<String, String>("key", null, false);
+        assertTrue(entry.equals(entry2));
+        entry2 = new MapEntryArrayValues<String, String>("key", new String[] {
+                "value1", "value2" }, false);
+        assertFalse(entry.equals(entry2));
+        entry = new MapEntryArrayValues<String, String>(null, new String[] {
+                null, "value2" }, false);
+        entry2 = new MapEntryArrayValues<String, String>(null, new String[] {
+                null, "value2" }, false);
+        assertTrue(entry.equals(entry2));
+        entry2 = new MapEntryArrayValues<String, String>(null, new String[] {
+                "value1", "value2" }, false);
+        assertFalse(entry.equals(entry2));
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/MapEntryTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/MapEntryTest.java
new file mode 100644
index 0000000..a458b1b
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/MapEntryTest.java
@@ -0,0 +1,110 @@
+/*
+ * $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.request.collection;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link MapEntry}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class MapEntryTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.MapEntry#hashCode()}.
+     */
+    @Test
+    public void testHashCode() {
+        MapEntry<String, String> entry = new MapEntry<String, String>("key", "value", false);
+        assertEquals("key".hashCode() ^ "value".hashCode(), entry.hashCode());
+        entry = new MapEntry<String, String>(null, "value", false);
+        assertEquals(0 ^ "value".hashCode(), entry.hashCode());
+        entry = new MapEntry<String, String>("key", null, false);
+        assertEquals("key".hashCode() ^ 0, entry.hashCode());
+        entry = new MapEntry<String, String>(null, null, false);
+        assertEquals(0 ^ 0, entry.hashCode());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.MapEntry#getKey()}.
+     */
+    @Test
+    public void testGetKey() {
+        MapEntry<String, String> entry = new MapEntry<String, String>("key", "value", false);
+        assertEquals("key", entry.getKey());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.MapEntry#getValue()}.
+     */
+    @Test
+    public void testGetValue() {
+        MapEntry<String, String> entry = new MapEntry<String, String>("key", "value", false);
+        assertEquals("value", entry.getValue());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.MapEntry#setValue(java.lang.Object)}.
+     */
+    @Test
+    public void testSetValue() {
+        MapEntry<String, String> entry = new MapEntry<String, String>("key", "value", true);
+        assertEquals("value", entry.getValue());
+        entry.setValue("value2");
+        assertEquals("value2", entry.getValue());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.MapEntry#setValue(java.lang.Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testSetValueException() {
+        MapEntry<String, String> entry = new MapEntry<String, String>("key", "value", false);
+        assertEquals("value", entry.getValue());
+        entry.setValue("value2");
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.MapEntry#equals(java.lang.Object)}.
+     */
+    @Test
+    public void testEqualsObject() {
+        MapEntry<String, String> entry = new MapEntry<String, String>("key", "value", false);
+        assertFalse(entry.equals(null));
+        assertFalse(entry.equals("whatever"));
+        MapEntry<String, String> entry2 = new MapEntry<String, String>("key", "value", false);
+        assertTrue(entry.equals(entry2));
+        entry2 = new MapEntry<String, String>("key2", "value", false);
+        assertFalse(entry.equals(entry2));
+        entry2 = new MapEntry<String, String>("key", "value2", false);
+        assertFalse(entry.equals(entry2));
+        entry = new MapEntry<String, String>(null, "value", false);
+        entry2 = new MapEntry<String, String>(null, "value", false);
+        assertTrue(entry.equals(entry2));
+        entry = new MapEntry<String, String>("key", null, false);
+        entry2 = new MapEntry<String, String>("key", null, false);
+        assertTrue(entry.equals(entry2));
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMapEntrySetTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMapEntrySetTest.java
new file mode 100644
index 0000000..41cd752
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMapEntrySetTest.java
@@ -0,0 +1,318 @@
+/*
+ * $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.request.collection;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tiles.request.attribute.HasKeys;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ReadOnlyEnumerationMap#entrySet()}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ReadOnlyEnumerationMapEntrySetTest {
+
+    /**
+     * The extractor to use.
+     */
+    private HasKeys<Integer> extractor;
+
+    /**
+     * The map to test.
+     */
+    private ReadOnlyEnumerationMap<Integer> map;
+
+    /**
+     * The set to test.
+     */
+    private Set<Map.Entry<String, Integer>> entrySet;
+
+    /**
+     * Sets up the test.
+     */
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        extractor = createMock(HasKeys.class);
+        map = new ReadOnlyEnumerationMap<Integer>(extractor);
+        entrySet = map.entrySet();
+    }
+
+    /**
+     * Tests {@link Set#add(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAdd() {
+        entrySet.add(null);
+    }
+
+    /**
+     * Tests {@link Set#addAll(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAddAll() {
+        entrySet.addAll(null);
+    }
+
+    /**
+     * Tests {@link Set#clear(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testClear() {
+        entrySet.clear();
+    }
+
+    /**
+     * Tests {@link Set#contains(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContains() {
+        Map.Entry<String, Integer> entry = createMock(Map.Entry.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(entry.getKey()).andReturn("two");
+        expect(entry.getValue()).andReturn(2);
+
+        expect(extractor.getValue("two")).andReturn(2);
+
+        replay(extractor, entry, values2);
+        assertTrue(entrySet.contains(entry));
+        verify(extractor, entry, values2);
+    }
+
+    /**
+     * Tests {@link Set#containsAll(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsAll() {
+        Map.Entry<String, Integer> entry1 = createMock(Map.Entry.class);
+        Map.Entry<String, Integer> entry2 = createMock(Map.Entry.class);
+
+        expect(entry1.getKey()).andReturn("one");
+        expect(entry1.getValue()).andReturn(1);
+        expect(entry2.getKey()).andReturn("two");
+        expect(entry2.getValue()).andReturn(2);
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+
+        replay(extractor, entry1, entry2);
+        List<Map.Entry<String, Integer>> coll = new ArrayList<Map.Entry<String, Integer>>();
+        coll.add(entry1);
+        coll.add(entry2);
+        assertTrue(entrySet.containsAll(coll));
+        verify(extractor, entry1, entry2);
+    }
+
+    /**
+     * Tests {@link Set#containsAll(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsAllFalse() {
+        Map.Entry<String, String> entry1 = createMock(Map.Entry.class);
+
+        expect(entry1.getKey()).andReturn("one");
+        expect(entry1.getValue()).andReturn("value4");
+
+        expect(extractor.getValue("one")).andReturn(1);
+
+        replay(extractor, entry1);
+        List<Map.Entry<String, String>> coll = new ArrayList<Map.Entry<String, String>>();
+        coll.add(entry1);
+        assertFalse(entrySet.containsAll(coll));
+        verify(extractor, entry1);
+    }
+
+    /**
+     * Test method for {@link Set#isEmpty()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIsEmpty() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+
+        replay(extractor, keys);
+        assertFalse(entrySet.isEmpty());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Set#iterator()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIterator() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+
+        expect(extractor.getValue("two")).andReturn(2);
+
+        replay(extractor, keys);
+        Iterator<Map.Entry<String, Integer>> entryIt = entrySet.iterator();
+        assertTrue(entryIt.hasNext());
+        MapEntry<String, Integer> entry = new MapEntry<String, Integer>(
+                "two", 2, false);
+        assertEquals(entry, entryIt.next());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Set#iterator()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test(expected = UnsupportedOperationException.class)
+    public void testIteratorRemove() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+
+        try {
+            replay(extractor, keys);
+            entrySet.iterator().remove();
+        } finally {
+            verify(extractor, keys);
+        }
+    }
+
+    /**
+     * Tests {@link Set#remove(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemove() {
+        entrySet.remove(null);
+    }
+
+    /**
+     * Tests {@link Set#removeAll(java.util.Collection)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemoveAll() {
+        entrySet.removeAll(null);
+    }
+
+    /**
+     * Tests {@link Set#retainAll(java.util.Collection)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRetainAll() {
+        entrySet.retainAll(null);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#size()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSize() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys);
+        assertEquals(2, entrySet.size());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Set#toArray()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testToArray() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+
+        MapEntry<String, Integer>[] entryArray = new MapEntry[2];
+        entryArray[0] = new MapEntry<String, Integer>("one", 1, false);
+        entryArray[1] = new MapEntry<String, Integer>("two", 2, false);
+
+        replay(extractor, keys, values1, values2);
+        assertArrayEquals(entryArray, entrySet.toArray());
+        verify(extractor, keys, values1, values2);
+    }
+
+    /**
+     * Test method for {@link Set#toArray(Object[])}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testToArrayTArray() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> values1 = createMock(Enumeration.class);
+        Enumeration<String> values2 = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+
+        MapEntry<String, Integer>[] entryArray = new MapEntry[2];
+        entryArray[0] = new MapEntry<String, Integer>("one", 1, false);
+        entryArray[1] = new MapEntry<String, Integer>("two", 2, false);
+
+        replay(extractor, keys, values1, values2);
+        MapEntry<String, String>[] realArray = new MapEntry[2];
+        assertArrayEquals(entryArray, entrySet.toArray(realArray));
+        verify(extractor, keys, values1, values2);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMapTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMapTest.java
new file mode 100644
index 0000000..547b219
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMapTest.java
@@ -0,0 +1,330 @@
+/*
+ * $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.request.collection;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+
+import org.apache.tiles.request.attribute.HasKeys;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ReadOnlyEnumerationMap}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ReadOnlyEnumerationMapTest {
+
+    /**
+     * The extractor to use.
+     */
+    private HasKeys<Integer> extractor;
+
+    /**
+     * The map to test.
+     */
+    private ReadOnlyEnumerationMap<Integer> map;
+
+    /**
+     * Sets up the test.
+     */
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        extractor = createMock(HasKeys.class);
+        map = new ReadOnlyEnumerationMap<Integer>(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ReadOnlyEnumerationMap#clear()}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testClear() {
+        map.clear();
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ReadOnlyEnumerationMap#containsKey(java.lang.Object)}.
+     */
+    @Test
+    public void testContainsKey() {
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(null);
+
+        replay(extractor);
+        assertTrue(map.containsKey("one"));
+        assertFalse(map.containsKey("two"));
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link ReadOnlyEnumerationMap#containsValue(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsValue() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+
+        replay(extractor, keys);
+        assertTrue(map.containsValue(2));
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link ReadOnlyEnumerationMap#containsValue(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsValueFalse() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(1);
+
+        replay(extractor, keys);
+        assertFalse(map.containsValue(3));
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ReadOnlyEnumerationMap#get(java.lang.Object)}.
+     */
+    @Test
+    public void testGet() {
+        expect(extractor.getValue("two")).andReturn(2);
+
+        replay(extractor);
+        assertEquals(new Integer(2), map.get("two"));
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ReadOnlyEnumerationMap#isEmpty()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIsEmpty() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+
+        replay(extractor, keys);
+        assertFalse(map.isEmpty());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ReadOnlyEnumerationMap#isEmpty()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIsEmptyTrue() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys);
+        assertTrue(map.isEmpty());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ReadOnlyEnumerationMap#keySet()}.
+     */
+    @Test
+    public void testKeySet() {
+        replay(extractor);
+        assertTrue(map.keySet() instanceof KeySet);
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link ReadOnlyEnumerationMap#put(String, String[])}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testPut() {
+        map.put("one", 1);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ReadOnlyEnumerationMap#putAll(java.util.Map)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testPutAll() {
+        map.putAll(new HashMap<String, Integer>());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ReadOnlyEnumerationMap#remove(java.lang.Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemove() {
+        map.remove("one");
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ReadOnlyEnumerationMap#size()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSize() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys);
+        assertEquals(2, map.size());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.extractor.collection.AbstractEnumerationMap#hashCode()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testHashCode() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("first");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("second");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        Integer value1 = 1;
+
+        expect(extractor.getValue("first")).andReturn(value1);
+        expect(extractor.getValue("second")).andReturn(null);
+
+        replay(extractor, keys);
+        assertEquals(("first".hashCode() ^ value1.hashCode())
+                + ("second".hashCode() ^ 0), map.hashCode());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.extractor.collection.AbstractEnumerationMap#equals(java.lang.Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testEqualsObject() {
+        HasKeys<Integer> otherRequest = createMock(HasKeys.class);
+        ReadOnlyEnumerationMap<Integer> otherMap = createMockBuilder(
+                ReadOnlyEnumerationMap.class).withConstructor(otherRequest)
+                .createMock();
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> otherKeys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(otherRequest.getKeys()).andReturn(otherKeys);
+
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("first");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("second");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValue("first")).andReturn(1);
+        expect(extractor.getValue("second")).andReturn(2);
+
+        expect(otherKeys.hasMoreElements()).andReturn(true);
+        expect(otherKeys.nextElement()).andReturn("first");
+        expect(otherKeys.hasMoreElements()).andReturn(true);
+        expect(otherKeys.nextElement()).andReturn("second");
+        expect(otherKeys.hasMoreElements()).andReturn(false);
+
+        expect(otherRequest.getValue("first")).andReturn(1);
+        expect(otherRequest.getValue("second")).andReturn(2);
+
+        replay(extractor, otherRequest, otherMap, keys, otherKeys);
+        assertTrue(map.equals(otherMap));
+        verify(extractor, otherRequest, otherMap, keys, otherKeys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.extractor.collection.AbstractEnumerationMap#equals(java.lang.Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testEqualsObjectFalse() {
+        HasKeys<Integer> otherRequest = createMock(HasKeys.class);
+        ReadOnlyEnumerationMap<Integer> otherMap = createMockBuilder(
+                ReadOnlyEnumerationMap.class).withConstructor(otherRequest)
+                .createMock();
+        Enumeration<String> keys = createMock(Enumeration.class);
+        Enumeration<String> otherKeys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(otherRequest.getKeys()).andReturn(otherKeys);
+
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("first");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("second");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValue("first")).andReturn(1);
+        expect(extractor.getValue("second")).andReturn(2);
+
+        expect(otherKeys.hasMoreElements()).andReturn(true);
+        expect(otherKeys.nextElement()).andReturn("first");
+        expect(otherKeys.hasMoreElements()).andReturn(true);
+        expect(otherKeys.nextElement()).andReturn("second");
+        expect(otherKeys.hasMoreElements()).andReturn(false);
+
+        expect(otherRequest.getValue("first")).andReturn(1);
+        expect(otherRequest.getValue("second")).andReturn(3);
+
+        replay(extractor, otherRequest, otherMap, keys, otherKeys);
+        assertFalse(map.equals(otherMap));
+        verify(extractor, otherRequest, otherMap, keys, otherKeys);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMapValuesCollectionTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMapValuesCollectionTest.java
new file mode 100644
index 0000000..13f35dd
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ReadOnlyEnumerationMapValuesCollectionTest.java
@@ -0,0 +1,337 @@
+/*
+ * $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.request.collection;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tiles.request.attribute.HasKeys;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ReadOnlyEnumerationMap#values()}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ReadOnlyEnumerationMapValuesCollectionTest {
+    /**
+     * The extractor to use.
+     */
+    private HasKeys<Integer> extractor;
+
+    /**
+     * The map to test.
+     */
+    private ReadOnlyEnumerationMap<Integer> map;
+
+    /**
+     * The collection to test.
+     */
+    private Collection<Integer> coll;
+
+    /**
+     * Sets up the test.
+     */
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        extractor = createMock(HasKeys.class);
+        map = new ReadOnlyEnumerationMap<Integer>(extractor);
+        coll = map.values();
+    }
+
+    /**
+     * Tests {@link Collection#add(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAdd() {
+        coll.add(null);
+    }
+
+    /**
+     * Tests {@link Collection#addAll(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testAddAll() {
+        coll.addAll(null);
+    }
+
+    /**
+     * Tests {@link Collection#clear(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testClear() {
+        coll.clear();
+    }
+
+    /**
+     * Tests {@link Collection#contains(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsValue() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+
+        replay(extractor, keys);
+        assertTrue(coll.contains(2));
+        verify(extractor, keys);
+    }
+
+    /**
+     * Tests {@link Collection#contains(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsValueFalse() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+
+        replay(extractor, keys);
+        assertFalse(coll.contains(3));
+        verify(extractor, keys);
+    }
+
+    /**
+     * Tests {@link Collection#containsAll(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsAll() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+
+        replay(extractor, keys);
+        List<Integer> coll = new ArrayList<Integer>();
+        coll.add(1);
+        coll.add(2);
+        assertTrue(this.coll.containsAll(coll));
+        verify(extractor, keys);
+    }
+
+    /**
+     * Tests {@link Collection#containsAll(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testContainsAllFalse() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+
+        replay(extractor, keys);
+        List<Integer> coll = new ArrayList<Integer>();
+        coll.add(3);
+        assertFalse(this.coll.containsAll(coll));
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Collection#isEmpty()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIsEmpty() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+
+        replay(extractor, keys);
+        assertFalse(coll.isEmpty());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Collection#iterator()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testIterator() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+
+        expect(extractor.getValue("two")).andReturn(2);
+
+        replay(extractor, keys);
+        Iterator<Integer> entryIt = coll.iterator();
+        assertTrue(entryIt.hasNext());
+        assertEquals(new Integer(2), entryIt.next());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Collection#iterator()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test(expected = UnsupportedOperationException.class)
+    public void testIteratorRemove() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+
+        try {
+            replay(extractor, keys);
+            coll.iterator().remove();
+        } finally {
+            verify(extractor, keys);
+        }
+    }
+
+    /**
+     * Tests {@link Collection#remove(Object)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemove() {
+        coll.remove(null);
+    }
+
+    /**
+     * Tests {@link Collection#removeAll(java.util.Collection)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRemoveAll() {
+        coll.removeAll(null);
+    }
+
+    /**
+     * Tests {@link Collection#retainAll(java.util.Collection)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testRetainAll() {
+        coll.retainAll(null);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.HeaderValuesMap#size()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSize() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        replay(extractor, keys);
+        assertEquals(2, coll.size());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Collection#toArray()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testToArray() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+
+        Integer[] entryArray = new Integer[] {1, 2};
+
+        replay(extractor, keys);
+        assertArrayEquals(entryArray, coll.toArray());
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link Collection#toArray(Object[])}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testToArrayTArray() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+
+        Integer[] entryArray = new Integer[] {1, 2};
+
+        replay(extractor, keys);
+        Integer[] realArray = new Integer[2];
+        assertArrayEquals(entryArray, coll.toArray(realArray));
+        verify(extractor, keys);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/RemovableKeySetTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/RemovableKeySetTest.java
new file mode 100644
index 0000000..c7fcda3
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/RemovableKeySetTest.java
@@ -0,0 +1,132 @@
+/*
+ * $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.request.collection;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.tiles.request.attribute.HasRemovableKeys;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link RemovableKeySet}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RemovableKeySetTest {
+
+    /**
+     * The extractor to use.
+     */
+    private HasRemovableKeys<Integer> extractor;
+
+    /**
+     * The key set to test.
+     */
+    private RemovableKeySet entrySet;
+
+    /**
+     * Sets up the test.
+     */
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        extractor = createMock(HasRemovableKeys.class);
+        entrySet = new RemovableKeySet(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.RemovableKeySet#remove(java.lang.Object)}.
+     */
+    @Test
+    public void testRemove() {
+        expect(extractor.getValue("one")).andReturn(1);
+        extractor.removeValue("one");
+
+        replay(extractor);
+        assertTrue(entrySet.remove("one"));
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.RemovableKeySet#remove(java.lang.Object)}.
+     */
+    @Test
+    public void testRemoveNoEffect() {
+        expect(extractor.getValue("one")).andReturn(null);
+
+        replay(extractor);
+        assertFalse(entrySet.remove("one"));
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.RemovableKeySet#removeAll(java.util.Collection)}.
+     */
+    @Test
+    public void testRemoveAll() {
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+        extractor.removeValue("one");
+        extractor.removeValue("two");
+
+        replay(extractor);
+        List<String> coll = new ArrayList<String>();
+        coll.add("one");
+        coll.add("two");
+        assertTrue(entrySet.removeAll(coll));
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.RemovableKeySet#retainAll(java.util.Collection)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testRetainAll() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("three");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        extractor.removeValue("three");
+
+        replay(extractor, keys);
+        List<String> coll = new ArrayList<String>();
+        coll.add("one");
+        coll.add("two");
+        assertTrue(entrySet.retainAll(coll));
+        verify(extractor, keys);
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ScopeMapEntrySetTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ScopeMapEntrySetTest.java
new file mode 100644
index 0000000..229af41
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ScopeMapEntrySetTest.java
@@ -0,0 +1,249 @@
+/*
+ * $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.request.collection;
+
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ScopeMap#entrySet()}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ScopeMapEntrySetTest {
+
+    /**
+     * The map to test.
+     */
+    private ScopeMap map;
+
+    /**
+     * The extractor to use.
+     */
+    private AttributeExtractor extractor;
+
+    /**
+     * The entry set to test.
+     */
+    private Set<Map.Entry<String, Object>> entrySet;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        extractor = createMock(AttributeExtractor.class);
+        map = new ScopeMap(extractor);
+        entrySet = map.entrySet();
+    }
+
+    /**
+     * Tests {@link Set#add(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testAdd() {
+        Map.Entry<String, Object> entry = createMock(Map.Entry.class);
+
+        expect(entry.getKey()).andReturn("one");
+        expect(entry.getValue()).andReturn(1);
+        expect(extractor.getValue("one")).andReturn(null);
+
+        extractor.setValue("one", 1);
+
+        replay(extractor, entry);
+        assertTrue(entrySet.add(entry));
+        verify(extractor, entry);
+    }
+
+    /**
+     * Tests {@link Set#add(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testAddNoEffect() {
+        Map.Entry<String, Object> entry = createMock(Map.Entry.class);
+
+        expect(entry.getKey()).andReturn("one");
+        expect(entry.getValue()).andReturn(1);
+        expect(extractor.getValue("one")).andReturn(1);
+
+        replay(extractor, entry);
+        assertFalse(entrySet.add(entry));
+        verify(extractor, entry);
+    }
+
+    /**
+     * Tests {@link Set#addAll(java.util.Collection)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testAddAll() {
+        Map.Entry<String, Object> entry1 = createMock(Map.Entry.class);
+        Map.Entry<String, Object> entry2 = createMock(Map.Entry.class);
+
+        expect(entry1.getKey()).andReturn("one");
+        expect(entry1.getValue()).andReturn(1);
+        expect(entry2.getKey()).andReturn("two");
+        expect(entry2.getValue()).andReturn(2);
+        expect(extractor.getValue("one")).andReturn(null);
+        expect(extractor.getValue("two")).andReturn(null);
+
+        extractor.setValue("one", 1);
+        extractor.setValue("two", 2);
+
+        replay(extractor, entry1, entry2);
+        List<Map.Entry<String, Object>> coll = new ArrayList<Map.Entry<String, Object>>();
+        coll.add(entry1);
+        coll.add(entry2);
+        assertTrue(entrySet.addAll(coll));
+        verify(extractor, entry1, entry2);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ScopeMap#clear()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testClear() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        extractor.removeValue("one");
+        extractor.removeValue("two");
+
+        replay(extractor, keys);
+        entrySet.clear();
+        verify(extractor, keys);
+    }
+
+    /**
+     * Tests {@link Set#remove(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testRemove() {
+        Map.Entry<String, Object> entry = createMock(Map.Entry.class);
+
+        expect(entry.getKey()).andReturn("one");
+        expect(entry.getValue()).andReturn(1);
+        expect(extractor.getValue("one")).andReturn(1);
+        extractor.removeValue("one");
+
+        replay(extractor, entry);
+        assertTrue(entrySet.remove(entry));
+        verify(extractor, entry);
+    }
+
+    /**
+     * Tests {@link Set#remove(Object)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testRemoveNoEffect() {
+        Map.Entry<String, Object> entry = createMock(Map.Entry.class);
+
+        expect(entry.getKey()).andReturn("one");
+        expect(extractor.getValue("one")).andReturn(null);
+
+        replay(extractor, entry);
+        assertFalse(entrySet.remove(entry));
+        verify(extractor, entry);
+    }
+
+    /**
+     * Tests {@link Set#addAll(java.util.Collection)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testRemoveAll() {
+        Map.Entry<String, Object> entry1 = createMock(Map.Entry.class);
+        Map.Entry<String, Object> entry2 = createMock(Map.Entry.class);
+
+        expect(entry1.getKey()).andReturn("one");
+        expect(entry1.getValue()).andReturn(1);
+        expect(entry2.getKey()).andReturn("two");
+        expect(entry2.getValue()).andReturn(2);
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(2);
+        extractor.removeValue("one");
+        extractor.removeValue("two");
+
+        replay(extractor, entry1, entry2);
+        List<Map.Entry<String, Object>> coll = new ArrayList<Map.Entry<String, Object>>();
+        coll.add(entry1);
+        coll.add(entry2);
+        assertTrue(entrySet.removeAll(coll));
+        verify(extractor, entry1, entry2);
+    }
+
+    /**
+     * Tests {@link Set#addAll(java.util.Collection)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testRetainAll() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("three");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        Map.Entry<String, Object> entry1 = new MapEntry<String, Object>("one", 1, false);
+        Map.Entry<String, Object> entry2 = new MapEntry<String, Object>("two", 2, false);
+
+        expect(extractor.getValue("one")).andReturn(1);
+        expect(extractor.getValue("two")).andReturn(3);
+        expect(extractor.getValue("three")).andReturn(4);
+        extractor.removeValue("two");
+        extractor.removeValue("three");
+
+        replay(extractor, keys);
+        List<Map.Entry<String, Object>> coll = new ArrayList<Map.Entry<String, Object>>();
+        coll.add(entry1);
+        coll.add(entry2);
+        assertTrue(entrySet.retainAll(coll));
+        verify(extractor, keys);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ScopeMapTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ScopeMapTest.java
new file mode 100644
index 0000000..914d118
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/collection/ScopeMapTest.java
@@ -0,0 +1,136 @@
+/*
+ * $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.request.collection;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ScopeMap}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ScopeMapTest {
+
+    /**
+     * The map tot est.
+     */
+    private ScopeMap map;
+
+    /**
+     * The extractor to use.
+     */
+    private AttributeExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        extractor = createMock(AttributeExtractor.class);
+        map = new ScopeMap(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ScopeMap#clear()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testClear() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(extractor.getKeys()).andReturn(keys);
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("one");
+        expect(keys.hasMoreElements()).andReturn(true);
+        expect(keys.nextElement()).andReturn("two");
+        expect(keys.hasMoreElements()).andReturn(false);
+
+        extractor.removeValue("one");
+        extractor.removeValue("two");
+
+        replay(extractor, keys);
+        map.clear();
+        verify(extractor, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ScopeMap#keySet()}.
+     */
+    @Test
+    public void testKeySet() {
+        replay(extractor);
+        assertTrue(map.keySet() instanceof RemovableKeySet);
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ScopeMap#put(java.lang.String, java.lang.Object)}.
+     */
+    @Test
+    public void testPutStringObject() {
+        expect(extractor.getValue("one")).andReturn(null);
+        extractor.setValue("one", 1);
+
+        replay(extractor);
+        assertNull(map.put("one", 1));
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ScopeMap#putAll(java.util.Map)}.
+     */
+    @Test
+    public void testPutAllMapOfQextendsStringQextendsObject() {
+        Map<String, Object> items = new LinkedHashMap<String, Object>();
+        items.put("one", 1);
+        items.put("two", 2);
+
+        extractor.setValue("one", 1);
+        extractor.setValue("two", 2);
+
+        replay(extractor);
+        map.putAll(items);
+        verify(extractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.collection.ScopeMap#remove(java.lang.Object)}.
+     */
+    @Test
+    public void testRemoveObject() {
+        expect(extractor.getValue("one")).andReturn(1);
+        extractor.removeValue("one");
+
+        replay(extractor);
+        assertEquals(new Integer(1), map.remove("one"));
+        verify(extractor);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/locale/LocaleUtilTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/locale/LocaleUtilTest.java
new file mode 100644
index 0000000..d603542
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/locale/LocaleUtilTest.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.request.locale;
+
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link LocaleUtil}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class LocaleUtilTest extends TestCase {
+
+    /**
+     * Test method for {@link LocaleUtil#getParentLocale(Locale)}.
+     */
+    public void testGetParentLocale() {
+        assertNull("The parent locale of NULL_LOCALE is not correct",
+                LocaleUtil.getParentLocale(Locale.ROOT));
+        assertEquals("The parent locale of 'en' is not correct",
+                Locale.ROOT, LocaleUtil
+                        .getParentLocale(Locale.ENGLISH));
+        assertEquals("The parent locale of 'en_US' is not correct",
+                Locale.ENGLISH, LocaleUtil.getParentLocale(Locale.US));
+        Locale locale = new Locale("es", "ES", "Traditional_WIN");
+        Locale parentLocale = new Locale("es", "ES");
+        assertEquals("The parent locale of 'es_ES_Traditional_WIN' is not correct",
+                parentLocale, LocaleUtil.getParentLocale(locale));
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/locale/PostfixedApplicationResourceTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/locale/PostfixedApplicationResourceTest.java
new file mode 100644
index 0000000..02c13e4
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/locale/PostfixedApplicationResourceTest.java
@@ -0,0 +1,111 @@
+/*
+ * $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.request.locale;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+
+import org.junit.Test;
+
+/**
+ * Tests PostfixedApplicationResource.
+ *
+ * @version $Rev$ $Date$
+ */
+public class PostfixedApplicationResourceTest {
+
+    private class TestApplicationResource extends PostfixedApplicationResource {
+        public TestApplicationResource(String path, Locale locale) {
+            super(path, locale);
+        }
+
+        public TestApplicationResource(String localePath) {
+            super(localePath);
+        }
+
+        @Override
+        public InputStream getInputStream() throws IOException {
+            return null;
+        }
+
+        @Override
+        public long getLastModified() throws IOException {
+            return 0;
+        }
+        
+    };
+    
+    /**
+     * Test getLocalePath(String path, Locale locale).
+     */
+    @Test
+    public void testGetLocalePath() {
+        TestApplicationResource resource = new TestApplicationResource("/my/path_fr.html");
+        assertEquals("/my/path.html", resource.getLocalePath(null));
+        assertEquals("/my/path.html", resource.getLocalePath(Locale.ROOT));
+        assertEquals("/my/path_it.html", resource.getLocalePath(Locale.ITALIAN));
+        assertEquals("/my/path_it_IT.html", resource.getLocalePath(Locale.ITALY));
+        assertEquals("/my/path_en_GB_scotland.html", resource.getLocalePath(new Locale("en", "GB", "scotland")));
+    }
+    
+    @Test
+    public void testBuildFromString() {
+        TestApplicationResource resource = new TestApplicationResource("/my/path_en_GB_scotland.html");
+        assertEquals("/my/path_en_GB_scotland.html", resource.getLocalePath());
+        assertEquals("/my/path.html", resource.getPath());
+        assertEquals(new Locale("en", "GB", "scotland"), resource.getLocale());
+        resource = new TestApplicationResource("/my/path_it_IT.html");
+        assertEquals("/my/path_it_IT.html", resource.getLocalePath());
+        assertEquals("/my/path.html", resource.getPath());
+        assertEquals(Locale.ITALY, resource.getLocale());
+        resource = new TestApplicationResource("/my/path_it.html");
+        assertEquals("/my/path_it.html", resource.getLocalePath());
+        assertEquals("/my/path.html", resource.getPath());
+        assertEquals(Locale.ITALIAN, resource.getLocale());
+        resource = new TestApplicationResource("/my/path.html");
+        assertEquals("/my/path.html", resource.getLocalePath());
+        assertEquals("/my/path.html", resource.getPath());
+        assertEquals(Locale.ROOT, resource.getLocale());
+    }
+    
+    @Test
+    public void testBuildFromStringAndLocale() {
+        TestApplicationResource resource = new TestApplicationResource("/my/path.html", new Locale("en", "GB", "scotland"));
+        assertEquals("/my/path_en_GB_scotland.html", resource.getLocalePath());
+        assertEquals("/my/path.html", resource.getPath());
+        assertEquals(new Locale("en", "GB", "scotland"), resource.getLocale());
+        resource = new TestApplicationResource("/my/path.html", Locale.ITALY);
+        assertEquals("/my/path_it_IT.html", resource.getLocalePath());
+        assertEquals("/my/path.html", resource.getPath());
+        assertEquals(Locale.ITALY, resource.getLocale());
+        resource = new TestApplicationResource("/my/path.html", Locale.ITALIAN);
+        assertEquals("/my/path_it.html", resource.getLocalePath());
+        assertEquals("/my/path.html", resource.getPath());
+        assertEquals(Locale.ITALIAN, resource.getLocale());
+        resource = new TestApplicationResource("/my/path.html", Locale.ROOT);
+        assertEquals("/my/path.html", resource.getLocalePath());
+        assertEquals("/my/path.html", resource.getPath());
+        assertEquals(Locale.ROOT, resource.getLocale());
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/reflect/CannotAccessMethodExceptionTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/reflect/CannotAccessMethodExceptionTest.java
new file mode 100644
index 0000000..5d2a8a3
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/reflect/CannotAccessMethodExceptionTest.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.request.reflect;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link CannotAccessMethodException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class CannotAccessMethodExceptionTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.CannotAccessMethodException#CannotAccessMethodException()}.
+     */
+    @Test
+    public void testCannotAccessMethodException() {
+        CannotAccessMethodException exception = new CannotAccessMethodException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link CannotAccessMethodException#CannotAccessMethodException(String)}.
+     */
+    @Test
+    public void testCannotAccessMethodExceptionString() {
+        CannotAccessMethodException exception = new CannotAccessMethodException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link CannotAccessMethodException#CannotAccessMethodException(Throwable)}.
+     */
+    @Test
+    public void testCannotAccessMethodExceptionThrowable() {
+        Throwable cause = new Throwable();
+        CannotAccessMethodException exception = new CannotAccessMethodException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for {@link CannotAccessMethodException#CannotAccessMethodException(String, Throwable)}.
+     */
+    @Test
+    public void testCannotAccessMethodExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        CannotAccessMethodException exception = new CannotAccessMethodException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/reflect/CannotInstantiateObjectExceptionTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/reflect/CannotInstantiateObjectExceptionTest.java
new file mode 100644
index 0000000..4b3bd9b
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/reflect/CannotInstantiateObjectExceptionTest.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.request.reflect;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link CannotInstantiateObjectException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class CannotInstantiateObjectExceptionTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.CannotInstantiateObjectException#CannotInstantiateObjectException()}.
+     */
+    @Test
+    public void testCannotInstantiateObjectException() {
+        CannotInstantiateObjectException exception = new CannotInstantiateObjectException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link CannotInstantiateObjectException#CannotInstantiateObjectException(String)}.
+     */
+    @Test
+    public void testCannotInstantiateObjectExceptionString() {
+        CannotInstantiateObjectException exception = new CannotInstantiateObjectException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link CannotInstantiateObjectException#CannotInstantiateObjectException(Throwable)}.
+     */
+    @Test
+    public void testCannotInstantiateObjectExceptionThrowable() {
+        Throwable cause = new Throwable();
+        CannotInstantiateObjectException exception = new CannotInstantiateObjectException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for {@link CannotInstantiateObjectException#CannotInstantiateObjectException(String, Throwable)}.
+     */
+    @Test
+    public void testCannotInstantiateObjectExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        CannotInstantiateObjectException exception = new CannotInstantiateObjectException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/reflect/ClassUtilTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/reflect/ClassUtilTest.java
new file mode 100644
index 0000000..e1b94f3
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/reflect/ClassUtilTest.java
@@ -0,0 +1,176 @@
+/*
+ * $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.request.reflect;
+
+import static org.junit.Assert.*;
+
+import java.beans.PropertyDescriptor;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link ClassUtil}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ClassUtilTest {
+
+    /**
+     * The size of descriptor map.
+     */
+    private static final int MAP_SIZE = 3;
+
+    /**
+     * Test method for {@link ClassUtil#collectBeanInfo(Class, Map)}.
+     */
+    @Test
+    public void testCollectBeanInfo() {
+        Map<String, PropertyDescriptor> name2descriptor = new HashMap<String, PropertyDescriptor>();
+        ClassUtil.collectBeanInfo(TestInterface.class, name2descriptor);
+        assertEquals(MAP_SIZE, name2descriptor.size());
+        PropertyDescriptor descriptor = name2descriptor.get("value");
+        assertEquals("value", descriptor.getName());
+        assertEquals(int.class, descriptor.getPropertyType());
+        assertNotNull(descriptor.getReadMethod());
+        assertNotNull(descriptor.getWriteMethod());
+        descriptor = name2descriptor.get("value2");
+        assertEquals("value2", descriptor.getName());
+        assertEquals(long.class, descriptor.getPropertyType());
+        assertNotNull(descriptor.getReadMethod());
+        assertNull(descriptor.getWriteMethod());
+        descriptor = name2descriptor.get("value3");
+        assertEquals("value3", descriptor.getName());
+        assertEquals(String.class, descriptor.getPropertyType());
+        assertNull(descriptor.getReadMethod());
+        assertNotNull(descriptor.getWriteMethod());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.reflect.ClassUtil#getClass(String, Class)}.
+     * @throws ClassNotFoundException If something goes wrong.
+     */
+    @Test
+    public void testGetClass() throws ClassNotFoundException {
+        assertEquals(TestInterface.class, ClassUtil.getClass(
+                TestInterface.class.getName(), Object.class));
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.reflect.ClassUtil#getClass(String, Class)}.
+     * @throws ClassNotFoundException If something goes wrong.
+     */
+    @Test(expected = ClassNotFoundException.class)
+    public void testGetClassException() throws ClassNotFoundException {
+        ClassUtil.getClass("this.class.does.not.Exist", Object.class);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.reflect.ClassUtil#instantiate(String, boolean)}.
+     */
+    @Test
+    public void testInstantiate() {
+        assertNotNull(ClassUtil.instantiate(TestClass.class.getName(), true));
+        assertNull(ClassUtil.instantiate("this.class.does.not.Exist", true));
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.reflect.ClassUtil#instantiate(String, boolean)}.
+     */
+    @Test
+    public void testInstantiateOneParameter() {
+        assertNotNull(ClassUtil.instantiate(TestClass.class.getName()));
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.reflect.ClassUtil#instantiate(String)}.
+     */
+    @Test(expected = CannotInstantiateObjectException.class)
+    public void testInstantiateOneParameterException() {
+        assertNotNull(ClassUtil.instantiate("this.class.does.not.Exist"));
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.reflect.ClassUtil#instantiate(String)}.
+     */
+    @Test(expected = CannotInstantiateObjectException.class)
+    public void testInstantiateInstantiationException() {
+        ClassUtil.instantiate(TestInterface.class.getName());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.reflect.ClassUtil#instantiate(String)}.
+     */
+    @Test(expected = CannotInstantiateObjectException.class)
+    public void testInstantiateIllegalAccessException() {
+        ClassUtil.instantiate(TestPrivateClass.class.getName());
+    }
+
+    /**
+     * Interface to be used as test.
+     *
+     * @version $Rev$ $Date$
+     */
+    public static interface TestInterface {
+
+        /**
+         * The value.
+         *
+         * @return The value.
+         */
+        int getValue();
+
+        /**
+         * The value.
+         *
+         * @param value The value.
+         */
+        void setValue(int value);
+
+        /**
+         * The value.
+         *
+         * @return The value.
+         */
+        long getValue2();
+
+        /**
+         * The value.
+         *
+         * @param value3 The value.
+         */
+        void setValue3(String value3);
+    }
+
+    /**
+     * A test static class.
+     */
+    public static class TestClass {
+    }
+
+    /**
+     * A test static private class.
+     */
+    private static class TestPrivateClass {
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/BasicRendererFactoryTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/BasicRendererFactoryTest.java
new file mode 100644
index 0000000..722357b
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/BasicRendererFactoryTest.java
@@ -0,0 +1,111 @@
+/*
+ * $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.request.render;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Basic renderer factory implementation.
+ *
+ * @version $Rev$ $Date$
+ */
+public class BasicRendererFactoryTest {
+
+    /**
+     * The renderer factory.
+     */
+    private BasicRendererFactory rendererFactory;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        rendererFactory = new BasicRendererFactory();
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        replay(applicationContext);
+    }
+
+    /**
+     * Tests execution and
+     * {@link BasicRendererFactory#getRenderer(String)}.
+     */
+    @Test
+    public void testInitAndGetRenderer() {
+        Renderer renderer1 = createMock(Renderer.class);
+        Renderer renderer2 = createMock(Renderer.class);
+        Renderer renderer3 = createMock(Renderer.class);
+        Renderer renderer4 = createMock(Renderer.class);
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+
+        replay(renderer1, renderer2, renderer3, renderer4, applicationContext);
+        rendererFactory.registerRenderer("string", renderer1);
+        rendererFactory.registerRenderer("test", renderer2);
+        rendererFactory.registerRenderer("test2", renderer3);
+        rendererFactory.setDefaultRenderer(renderer4);
+        Renderer renderer = rendererFactory.getRenderer("string");
+        assertSame(renderer1, renderer);
+        renderer = rendererFactory.getRenderer("test");
+        assertSame(renderer2, renderer);
+        renderer = rendererFactory.getRenderer("test2");
+        assertSame(renderer3, renderer);
+        renderer = rendererFactory.getRenderer(null);
+        assertSame(renderer4, renderer);
+        verify(renderer1, renderer2, renderer3, renderer4, applicationContext);
+    }
+
+    /**
+     * Tests execution and
+     * {@link BasicRendererFactory#getRenderer(String)}.
+     */
+    @Test(expected = NoSuchRendererException.class)
+    public void testGetRendererException() {
+        Renderer renderer1 = createMock(Renderer.class);
+        Renderer renderer2 = createMock(Renderer.class);
+        Renderer renderer3 = createMock(Renderer.class);
+        Renderer renderer4 = createMock(Renderer.class);
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+
+        replay(renderer1, renderer2, renderer3, renderer4, applicationContext);
+        rendererFactory.registerRenderer("string", renderer1);
+        rendererFactory.registerRenderer("test", renderer2);
+        rendererFactory.registerRenderer("test2", renderer3);
+        rendererFactory.setDefaultRenderer(renderer4);
+        try {
+            rendererFactory.getRenderer("nothing");
+        } finally {
+            verify(renderer1, renderer2, renderer3, renderer4, applicationContext);
+        }
+    }
+
+    /**
+     * Tests {@link BasicRendererFactory#initializeRenderer(AttributeRenderer)}.
+     */
+    @Test
+    public void testInitializeRenderer() {
+        // TODO This will be removed in future, only named renderers should be available.
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/ChainedDelegateRendererTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/ChainedDelegateRendererTest.java
new file mode 100644
index 0000000..2f89255
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/ChainedDelegateRendererTest.java
@@ -0,0 +1,211 @@
+/*
+ * $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.request.render;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.apache.tiles.request.Request;
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ChainedDelegateRenderer}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ChainedDelegateRendererTest {
+
+    /**
+     * The renderer.
+     */
+    private ChainedDelegateRenderer renderer;
+
+    /**
+     * A mock string attribute renderer.
+     */
+    private Renderer stringRenderer;
+
+    /**
+     * A mock template attribute renderer.
+     */
+    private Renderer templateRenderer;
+
+    /**
+     * A mock definition attribute renderer.
+     */
+    private Renderer definitionRenderer;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        stringRenderer = createMock(Renderer.class);
+        templateRenderer = createMock(Renderer.class);
+        definitionRenderer = createMock(Renderer.class);
+        renderer = new ChainedDelegateRenderer();
+        renderer.addAttributeRenderer(definitionRenderer);
+        renderer.addAttributeRenderer(templateRenderer);
+        renderer.addAttributeRenderer(stringRenderer);
+    }
+
+    /**
+     * Tests
+     * {@link ChainedDelegateRenderer#render(String, Request)}
+     * writing a definition.
+     *
+     * @throws IOException If something goes wrong during rendition.
+     */
+    @Test
+    public void testWriteDefinition() throws IOException {
+        Request requestContext = EasyMock
+                .createMock(Request.class);
+
+        expect(
+                definitionRenderer.isRenderable("my.definition",
+                        requestContext)).andReturn(Boolean.TRUE);
+        definitionRenderer.render("my.definition", requestContext);
+
+        replay(requestContext, stringRenderer, templateRenderer,
+                definitionRenderer);
+        renderer.render("my.definition", requestContext);
+        verify(requestContext, stringRenderer, templateRenderer,
+                definitionRenderer);
+    }
+
+    /**
+     * Tests
+     * {@link ChainedDelegateRenderer#render(String, Request)}
+     * writing a definition.
+     *
+     * @throws IOException If something goes wrong during rendition.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testWriteNull() throws IOException {
+        StringWriter writer = new StringWriter();
+        Request requestContext = EasyMock
+                .createMock(Request.class);
+
+        replay(requestContext, stringRenderer, templateRenderer,
+                definitionRenderer);
+        try {
+            renderer.render(null, requestContext);
+        } finally {
+            writer.close();
+            verify(requestContext, stringRenderer, templateRenderer,
+                    definitionRenderer);
+        }
+    }
+
+    /**
+     * Tests
+     * {@link ChainedDelegateRenderer#render(String, Request)}
+     * writing a definition.
+     *
+     * @throws IOException If something goes wrong during rendition.
+     */
+    @Test(expected = CannotRenderException.class)
+    public void testWriteNotRenderable() throws IOException {
+        StringWriter writer = new StringWriter();
+        Request requestContext = EasyMock
+                .createMock(Request.class);
+
+        expect(
+                definitionRenderer.isRenderable("Result",
+                        requestContext)).andReturn(Boolean.FALSE);
+        expect(
+                templateRenderer.isRenderable("Result",
+                        requestContext)).andReturn(Boolean.FALSE);
+        expect(stringRenderer.isRenderable("Result", requestContext))
+                .andReturn(Boolean.FALSE);
+
+        replay(requestContext, stringRenderer, templateRenderer,
+                definitionRenderer);
+        try {
+            renderer.render("Result", requestContext);
+        } finally {
+            writer.close();
+            verify(requestContext, stringRenderer, templateRenderer,
+                    definitionRenderer);
+        }
+    }
+
+    /**
+     * Tests
+     * {@link ChainedDelegateRenderer#render(String, Request)}
+     * writing a string.
+     *
+     * @throws IOException If something goes wrong during rendition.
+     */
+    @Test
+    public void testWriteString() throws IOException {
+        Request requestContext = EasyMock
+                .createMock(Request.class);
+        expect(
+                definitionRenderer.isRenderable("Result",
+                        requestContext)).andReturn(Boolean.FALSE);
+        expect(
+                templateRenderer.isRenderable("Result",
+                        requestContext)).andReturn(Boolean.FALSE);
+        expect(
+                stringRenderer.isRenderable("Result",
+                        requestContext)).andReturn(Boolean.TRUE);
+        stringRenderer.render("Result", requestContext);
+
+        replay(requestContext, stringRenderer, templateRenderer,
+                definitionRenderer);
+        renderer.render("Result", requestContext);
+        verify(requestContext, stringRenderer, templateRenderer,
+                definitionRenderer);
+    }
+
+    /**
+     * Tests
+     * {@link ChainedDelegateRenderer#render(String, Request)}
+     * writing a template.
+     *
+     * @throws IOException If something goes wrong during rendition.
+     */
+    @Test
+    public void testWriteTemplate() throws IOException {
+        StringWriter writer = new StringWriter();
+        Request requestContext = EasyMock
+                .createMock(Request.class);
+        templateRenderer.render("/myTemplate.jsp", requestContext);
+        expect(
+                definitionRenderer.isRenderable("/myTemplate.jsp",
+                        requestContext)).andReturn(Boolean.FALSE);
+        expect(
+                templateRenderer.isRenderable("/myTemplate.jsp",
+                        requestContext)).andReturn(Boolean.TRUE);
+
+        replay(requestContext, stringRenderer, templateRenderer,
+                definitionRenderer);
+        renderer.render("/myTemplate.jsp", requestContext);
+        writer.close();
+        verify(requestContext, stringRenderer, templateRenderer,
+                definitionRenderer);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/DispatchRendererTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/DispatchRendererTest.java
new file mode 100644
index 0000000..52a9936
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/DispatchRendererTest.java
@@ -0,0 +1,92 @@
+/*
+ * $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.request.render;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.DispatchRequest;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link DispatchRenderer}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class DispatchRendererTest {
+
+    /**
+     * The renderer.
+     */
+    private DispatchRenderer renderer;
+
+    /** {@inheritDoc} */
+    @Before
+    public void setUp() {
+        renderer = new DispatchRenderer();
+    }
+
+    /**
+     * Tests
+     * {@link DispatchRenderer#render(String, DispatchRequest)}.
+     *
+     * @throws IOException If something goes wrong during rendition.
+     */
+    @Test
+    public void testWrite() throws IOException {
+        DispatchRequest requestContext = createMock(DispatchRequest.class);
+        requestContext.dispatch("/myTemplate.jsp");
+        replay(requestContext);
+        renderer.render("/myTemplate.jsp", requestContext);
+        verify(requestContext);
+    }
+
+    /**
+     * Tests
+     * {@link DispatchRenderer#render(String, DispatchRequest)}.
+     *
+     * @throws IOException If something goes wrong during rendition.
+     */
+    @Test(expected = CannotRenderException.class)
+    public void testWriteNull() throws IOException {
+        DispatchRequest requestContext = createMock(DispatchRequest.class);
+        replay(requestContext);
+        renderer.render(null, requestContext);
+        verify(requestContext);
+    }
+
+    /**
+     * Tests
+     * {@link DispatchRenderer#isRenderable(String, DispatchRequest)}.
+     */
+    @Test
+    public void testIsRenderable() {
+        Request requestContext = createMock(DispatchRequest.class);
+        replay(requestContext);
+        assertTrue(renderer.isRenderable("/myTemplate.jsp", requestContext));
+        assertFalse(renderer.isRenderable(null, requestContext));
+        verify(requestContext);
+    }
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/NoSuchRendererExceptionTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/NoSuchRendererExceptionTest.java
new file mode 100644
index 0000000..4c3ef77
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/NoSuchRendererExceptionTest.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.request.render;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link NoSuchRendererException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NoSuchRendererExceptionTest {
+
+    /**
+     * Test method for {@link NoSuchRendererException#NoSuchRendererException()}.
+     */
+    @Test
+    public void testNoSuchRendererException() {
+        NoSuchRendererException exception = new NoSuchRendererException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link NoSuchRendererException#NoSuchRendererException(java.lang.String)}.
+     */
+    @Test
+    public void testNoSuchRendererExceptionString() {
+        NoSuchRendererException exception = new NoSuchRendererException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link NoSuchRendererException#NoSuchRendererException(java.lang.Throwable)}.
+     */
+    @Test
+    public void testNoSuchRendererExceptionThrowable() {
+        Throwable cause = new Throwable();
+        NoSuchRendererException exception = new NoSuchRendererException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for {@link NoSuchRendererException#NoSuchRendererException(java.lang.String, java.lang.Throwable)}.
+     */
+    @Test
+    public void testNoSuchRendererExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        NoSuchRendererException exception = new NoSuchRendererException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/StringRendererTest.java b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/StringRendererTest.java
new file mode 100644
index 0000000..df7e1a9
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/test/java/org/apache/tiles/request/render/StringRendererTest.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.request.render;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+import org.apache.tiles.request.Request;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link StringRenderer}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StringRendererTest {
+
+    /**
+     * The renderer.
+     */
+    private StringRenderer renderer;
+
+    /** {@inheritDoc} */
+    @Before
+    public void setUp() {
+        renderer = new StringRenderer();
+    }
+
+    /**
+     * Tests
+     * {@link StringRenderer#render(String, Request)}.
+     *
+     * @throws IOException If something goes wrong during rendition.
+     */
+    @Test
+    public void testWrite() throws IOException {
+        StringWriter writer = new StringWriter();
+        Request requestContext = createMock(Request.class);
+        expect(requestContext.getWriter()).andReturn(writer);
+        replay(requestContext);
+        renderer.render("Result", requestContext);
+        writer.close();
+        assertEquals("Not written 'Result'", "Result", writer.toString());
+        verify(requestContext);
+    }
+
+    /**
+     * Tests
+     * {@link StringRenderer#isRenderable(String, Request)}.
+     */
+    @Test
+    public void testIsRenderable() {
+        Request requestContext = createMock(Request.class);
+        replay(requestContext);
+        assertTrue(renderer.isRenderable("Result", requestContext));
+        verify(requestContext);
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/pom.xml b/tiles-request/tiles-request-freemarker/pom.xml
new file mode 100644
index 0000000..4dabc58
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/pom.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>tiles-request</artifactId>
+    <groupId>org.apache.tiles</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.tiles</groupId>
+  <artifactId>tiles-request-freemarker</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Tiles Request - Freemarker support</name>
+  <description>Freemarker implementation of the Tiles request framework</description>
+  <dependencies>
+  	<dependency>
+  		<groupId>org.apache.tiles</groupId>
+  		<artifactId>tiles-request-api</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.freemarker</groupId>
+  		<artifactId>freemarker</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.apache.tiles</groupId>
+  		<artifactId>tiles-request-servlet</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>javax.servlet</groupId>
+  		<artifactId>servlet-api</artifactId>
+  		<scope>provided</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.slf4j</groupId>
+  		<artifactId>slf4j-jdk14</artifactId>
+  		<optional>true</optional>
+  	</dependency>
+  	<dependency>
+  		<groupId>junit</groupId>
+  		<artifactId>junit</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.easymock</groupId>
+  		<artifactId>easymock</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.easymock</groupId>
+  		<artifactId>easymockclassextension</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.apache.tiles</groupId>
+  		<artifactId>tiles-autotag-core-runtime</artifactId>
+  		<optional>true</optional>
+  	</dependency>
+  </dependencies>
+</project>
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/EnvironmentScopeMap.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/EnvironmentScopeMap.java
new file mode 100644
index 0000000..b2a60ba
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/EnvironmentScopeMap.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.request.freemarker;
+
+import java.util.Set;
+
+import org.apache.tiles.request.collection.ScopeMap;
+import org.apache.tiles.request.freemarker.extractor.EnvironmentScopeExtractor;
+
+import freemarker.core.Environment;
+import freemarker.template.TemplateModelException;
+
+/**
+ * <p>
+ * Private implementation of <code>Map</code> for servlet request attributes.
+ * </p>
+ *
+ * @version $Rev$ $Date$
+ */
+
+final class EnvironmentScopeMap extends ScopeMap {
+
+    /**
+     * The request object to use.
+     */
+    private Environment request = null;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request object to use.
+     */
+    public EnvironmentScopeMap(Environment request) {
+        super(new EnvironmentScopeExtractor(request));
+        this.request = request;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Set<String> keySet() {
+        try {
+            return request.getKnownVariableNames();
+        } catch (TemplateModelException e) {
+            throw new FreemarkerRequestException(
+                    "Cannot get known variable names", e);
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/FreemarkerRequest.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/FreemarkerRequest.java
new file mode 100644
index 0000000..212badf
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/FreemarkerRequest.java
@@ -0,0 +1,154 @@
+/*
+ * $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.request.freemarker;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tiles.request.AbstractViewRequest;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.DispatchRequest;
+import org.apache.tiles.request.servlet.ServletRequest;
+
+import freemarker.core.Environment;
+import freemarker.ext.servlet.HttpRequestHashModel;
+
+/**
+ * The FreeMarker-specific request context.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerRequest extends AbstractViewRequest {
+
+    /**
+     * The natively available scopes. In fact, only "page".
+     */
+    private List<String> scopes;
+
+    /**
+     * The FreeMarker current environment.
+     */
+    private Environment env;
+
+    /**
+     * The page scope map.
+     */
+    private Map<String, Object> pageScope;
+
+    /**
+     * Creates a new Freemarker request.
+     *
+     * @param applicationContext The application context.
+     * @param env The Freemarker's environment object.
+     * @return A new request.
+     */
+    public static FreemarkerRequest createServletFreemarkerRequest(
+            ApplicationContext applicationContext, Environment env) {
+        HttpRequestHashModel requestModel = FreemarkerRequestUtil
+                .getRequestHashModel(env);
+        HttpServletRequest request = requestModel.getRequest();
+        HttpServletResponse response = requestModel.getResponse();
+        DispatchRequest enclosedRequest = new ServletRequest(
+                applicationContext, request, response);
+        return new FreemarkerRequest(enclosedRequest, env);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param enclosedRequest
+     *            The request that exposes non-FreeMarker specific properties
+     * @param env
+     *            The FreeMarker environment.
+     */
+    public FreemarkerRequest(DispatchRequest enclosedRequest,
+            Environment env) {
+        super(enclosedRequest);
+        List<String> scopes = new ArrayList<String>();
+        scopes.addAll(enclosedRequest.getAvailableScopes());
+        scopes.add("page");
+        this.scopes = Collections.unmodifiableList(scopes);
+        this.env = env;
+    }
+
+    /**
+     * Returns the environment object.
+     *
+     * @return The environment.
+     */
+    public Environment getEnvironment() {
+        return env;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Locale getRequestLocale() {
+        return env.getLocale();
+    }
+
+    /**
+     * Returns the page scope.
+     *
+     * @return The page scope.
+     */
+    public Map<String, Object> getPageScope() {
+        if (pageScope == null) {
+            pageScope = new EnvironmentScopeMap(env);
+        }
+        return pageScope;
+    }
+
+    @Override
+    public List<String> getAvailableScopes() {
+        return scopes;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public PrintWriter getPrintWriter() {
+        Writer writer = env.getOut();
+        if (writer instanceof PrintWriter) {
+            return (PrintWriter) writer;
+        }
+        return new PrintWriter(writer);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Writer getWriter() {
+        return env.getOut();
+    }
+
+    @Override
+    public Map<String, Object> getContext(String scope) {
+        return "page".equals(scope) ? getPageScope() : super.getContext(scope);
+    }
+
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/FreemarkerRequestException.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/FreemarkerRequestException.java
new file mode 100644
index 0000000..b2a33f2
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/FreemarkerRequestException.java
@@ -0,0 +1,65 @@
+/*
+ * $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.request.freemarker;
+
+import org.apache.tiles.request.RequestException;
+
+/**
+ * Thrown when a Freemarker request fails.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerRequestException extends RequestException {
+
+    /**
+     * Constructor.
+     */
+    public FreemarkerRequestException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     */
+    public FreemarkerRequestException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param cause The cause to be wrapped.
+     */
+    public FreemarkerRequestException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @param cause The cause to be wrapped.
+     */
+    public FreemarkerRequestException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/FreemarkerRequestUtil.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/FreemarkerRequestUtil.java
new file mode 100644
index 0000000..929e8cc
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/FreemarkerRequestUtil.java
@@ -0,0 +1,94 @@
+/*
+ * $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.request.freemarker;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.servlet.ServletUtil;
+
+import freemarker.core.Environment;
+import freemarker.ext.servlet.FreemarkerServlet;
+import freemarker.ext.servlet.HttpRequestHashModel;
+import freemarker.ext.servlet.ServletContextHashModel;
+import freemarker.template.TemplateModelException;
+
+/**
+ * Utilities to work with Freemarker requests.
+ *
+ * @version $Rev$ $Date$
+ */
+public final class FreemarkerRequestUtil {
+
+    /**
+     * Constructor.
+     */
+    private FreemarkerRequestUtil() {
+    }
+
+    /**
+     * Returns the HTTP request hash model.
+     *
+     * @param env The current FreeMarker environment.
+     * @return The request hash model.
+     */
+    public static HttpRequestHashModel getRequestHashModel(Environment env) {
+        try {
+            return (HttpRequestHashModel) env.getDataModel().get(
+                    FreemarkerServlet.KEY_REQUEST);
+        } catch (TemplateModelException e) {
+            throw new NotAvailableFreemarkerServletException(
+                    "Exception got when obtaining the request hash model", e);
+        }
+    }
+
+    /**
+     * Returns the servlet context hash model.
+     *
+     * @param env The current FreeMarker environment.
+     * @return The servlet context hash model.
+     */
+    public static ServletContextHashModel getServletContextHashModel(
+            Environment env) {
+        try {
+            return (ServletContextHashModel) env.getDataModel().get(
+                    FreemarkerServlet.KEY_APPLICATION);
+        } catch (TemplateModelException e) {
+            throw new NotAvailableFreemarkerServletException(
+                    "Exception got when obtaining the application hash model",
+                    e);
+        }
+    }
+
+    /**
+     * Returns the application context. It must be
+     * first saved creating an {@link ApplicationContext} and using
+     * {@link org.apache.tiles.request.ApplicationAccess#register(ApplicationContext)}.
+     *
+     * @param env The Freemarker environment.
+     * @return The
+     */
+    public static ApplicationContext getApplicationContext(
+            Environment env) {
+        return ServletUtil
+                .getApplicationContext(getServletContextHashModel(env)
+                        .getServlet().getServletContext());
+    }
+
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/NotAvailableFreemarkerServletException.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/NotAvailableFreemarkerServletException.java
new file mode 100644
index 0000000..a265428
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/NotAvailableFreemarkerServletException.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.request.freemarker;
+
+import org.apache.tiles.request.NotAvailableFeatureException;
+
+/**
+ * Thrown when a the Freemarker servlet is not available.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NotAvailableFreemarkerServletException extends
+        NotAvailableFeatureException {
+
+    /**
+     * Constructor.
+     */
+    public NotAvailableFreemarkerServletException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     */
+    public NotAvailableFreemarkerServletException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param e The cause to be wrapped.
+     */
+    public NotAvailableFreemarkerServletException(Throwable e) {
+        super(e);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @param e The cause to be wrapped.
+     */
+    public NotAvailableFreemarkerServletException(String message, Throwable e) {
+        super(message, e);
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagException.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagException.java
new file mode 100644
index 0000000..04df896
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagException.java
@@ -0,0 +1,64 @@
+/*
+ * $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.request.freemarker.autotag;
+
+/**
+ * Thrown when a Freemarker problem under Autotag runtime part happens.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerAutotagException extends RuntimeException {
+
+    /**
+     * Constructor.
+     */
+    public FreemarkerAutotagException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message of the exception.
+     */
+    public FreemarkerAutotagException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param cause The cause.
+     */
+    public FreemarkerAutotagException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The message of the exception.
+     * @param cause The cause.
+     */
+    public FreemarkerAutotagException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagRuntime.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagRuntime.java
new file mode 100644
index 0000000..9eabac3
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagRuntime.java
@@ -0,0 +1,71 @@
+/*
+ * $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.request.freemarker.autotag;
+
+import java.util.Map;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.freemarker.FreemarkerRequest;
+import org.apache.tiles.request.freemarker.FreemarkerRequestUtil;
+
+import freemarker.core.Environment;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateDirectiveModel;
+import freemarker.template.TemplateModel;
+
+/**
+ * A Runtime for implementing a Freemarker Template Directive.   
+ */
+public class FreemarkerAutotagRuntime implements AutotagRuntime, TemplateDirectiveModel {
+
+    private Environment env;
+    private TemplateDirectiveBody body;
+    private Map<String, TemplateModel> params;
+    
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override
+    public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) {
+        this.env = env;
+        this.body = body;
+        this.params = params;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Request createRequest() {
+        return FreemarkerRequest.createServletFreemarkerRequest(FreemarkerRequestUtil.getApplicationContext(env), env);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ModelBody createModelBody() {
+        return new FreemarkerModelBody(env.getOut(), body);
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public Object getParameter(String name, Object defaultValue) {
+        return FreemarkerUtil.getAsObject((TemplateModel)params.get(name), defaultValue);
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerModelBody.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerModelBody.java
new file mode 100644
index 0000000..a006bca
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerModelBody.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.request.freemarker.autotag;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.tiles.autotag.core.runtime.AbstractModelBody;
+
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateException;
+
+/**
+ * Body implementation of a Freemarker model body.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerModelBody extends AbstractModelBody {
+
+    /**
+     * The real body.
+     */
+    private TemplateDirectiveBody templateDirectiveBody;
+
+    /**
+     * Constructor.
+     *
+     * @param defaultWriter The default writer.
+     * @param templateDirectiveBody The real body.
+     */
+    public FreemarkerModelBody(Writer defaultWriter, TemplateDirectiveBody templateDirectiveBody) {
+        super(defaultWriter);
+        this.templateDirectiveBody = templateDirectiveBody;
+    }
+
+    @Override
+    public void evaluate(Writer writer) throws IOException {
+        if (templateDirectiveBody == null) {
+            return;
+        }
+
+        try {
+            templateDirectiveBody.render(writer);
+        } catch (TemplateException e) {
+            throw new IOException("TemplateException when rendering body", e);
+        }
+    }
+
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerUtil.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerUtil.java
new file mode 100644
index 0000000..881e511
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/FreemarkerUtil.java
@@ -0,0 +1,63 @@
+/*
+ * $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.request.freemarker.autotag;
+
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.utility.DeepUnwrap;
+
+/**
+ * Utilities for FreeMarker usage in Tiles.
+ *
+ * @version $Rev$ $Date$
+ */
+public final class FreemarkerUtil {
+
+    /**
+     * Private constructor to avoid instantiation.
+     */
+    private FreemarkerUtil() {
+    }
+
+    /**
+     * Unwraps a TemplateModel to extract an object.
+     *
+     * @param model The TemplateModel to unwrap.
+     * @param defaultValue The default value, as specified in the template
+     * model, or null if not specified.
+     * @return The unwrapped object.
+     */
+    public static Object getAsObject(TemplateModel model, Object defaultValue) {
+        try {
+            Object retValue = defaultValue;
+            if (model != null) {
+                Object value = DeepUnwrap.unwrap(model);
+                if (value != null) {
+                    retValue = value;
+                }
+            }
+            return retValue;
+        } catch (TemplateModelException e) {
+            throw new FreemarkerAutotagException("Cannot unwrap a model", e);
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/package-info.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/package-info.java
new file mode 100644
index 0000000..fb875dc
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/autotag/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Runtime part of Autotag support for Freemarker.
+ */
+package org.apache.tiles.request.freemarker.autotag;
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/extractor/EnvironmentScopeExtractor.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/extractor/EnvironmentScopeExtractor.java
new file mode 100644
index 0000000..78fcb9d
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/extractor/EnvironmentScopeExtractor.java
@@ -0,0 +1,92 @@
+/*
+ * $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.request.freemarker.extractor;
+
+import java.util.Collections;
+import java.util.Enumeration;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+import org.apache.tiles.request.freemarker.FreemarkerRequestException;
+
+import freemarker.core.Environment;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.utility.DeepUnwrap;
+
+/**
+ * Extract attributes from {@link Environment} objects as a scope.
+ *
+ * @version $Rev$ $Date$
+ */
+public class EnvironmentScopeExtractor implements AttributeExtractor {
+
+    /**
+     * The environment.
+     */
+    private Environment request;
+
+    /**
+     * Constructor.
+     *
+     * @param request The environment.
+     */
+    public EnvironmentScopeExtractor(Environment request) {
+        this.request = request;
+    }
+
+    @Override
+    public void removeValue(String name) {
+        request.setVariable(name, null);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Enumeration<String> getKeys() {
+        try {
+            return Collections.<String> enumeration(request.getKnownVariableNames());
+        } catch (TemplateModelException e) {
+            throw new FreemarkerRequestException("Cannot iterate variable names correctly", e);
+        }
+    }
+
+    @Override
+    public Object getValue(String key) {
+        try {
+            TemplateModel variable = request.getVariable(key);
+            if (variable != null) {
+                return DeepUnwrap.unwrap(variable);
+            }
+            return null;
+        } catch (TemplateModelException e) {
+            throw new FreemarkerRequestException("Cannot get attribute with name '" + key + "'", e);
+        }
+    }
+
+    @Override
+    public void setValue(String key, Object value) {
+        try {
+            TemplateModel model = request.getObjectWrapper().wrap(value);
+            request.setVariable(key, model);
+        } catch (TemplateModelException e) {
+            throw new FreemarkerRequestException("Error when wrapping an object setting the '" + key + "' attribute", e);
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/extractor/package-info.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/extractor/package-info.java
new file mode 100644
index 0000000..8e94691
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/extractor/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Extractors to get information from Freemarker objects.
+ */
+package org.apache.tiles.request.freemarker.extractor;
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/package-info.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/package-info.java
new file mode 100644
index 0000000..234a67c
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Support for Freemarker's Environment as a request.
+ */
+package org.apache.tiles.request.freemarker;
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/AttributeValueFreemarkerServlet.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/AttributeValueFreemarkerServlet.java
new file mode 100644
index 0000000..98c0bc9
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/AttributeValueFreemarkerServlet.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.request.freemarker.render;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.tiles.request.freemarker.servlet.SharedVariableLoaderFreemarkerServlet;
+
+/**
+ * Extends {@link SharedVariableLoaderFreemarkerServlet} to use the attribute value as the template name.
+ */
+public class AttributeValueFreemarkerServlet extends SharedVariableLoaderFreemarkerServlet {
+
+    /**
+     * The serial UID.
+     */
+    private static final long serialVersionUID = 5412169082301451211L;
+
+    /**
+     * Holds the value that should be used as the template name.
+     */
+    private ThreadLocal<String> valueHolder = new ThreadLocal<String>();
+
+    /**
+     * Sets the value to use as the template name.
+     *
+     * @param value The template name.
+     */
+    public void setValue(String value) {
+        valueHolder.set(value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String requestUrlToTemplatePath(HttpServletRequest request) {
+        return valueHolder.get();
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/FreemarkerRenderer.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/FreemarkerRenderer.java
new file mode 100644
index 0000000..e18dc1d
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/FreemarkerRenderer.java
@@ -0,0 +1,85 @@
+/*
+ * $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.request.freemarker.render;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.freemarker.FreemarkerRequestException;
+import org.apache.tiles.request.render.CannotRenderException;
+import org.apache.tiles.request.render.Renderer;
+import org.apache.tiles.request.servlet.ExternalWriterHttpServletResponse;
+import org.apache.tiles.request.servlet.ServletRequest;
+
+/**
+ * FreeMarker renderer for rendering FreeMarker templates as Tiles attributes.
+ * It is only usable under a Servlet environment, because it uses
+ * {@link AttributeValueFreemarkerServlet} internally to forward the request.<br/>
+ * To initialize it correctly, call {@link #setParameter(String, String)} for all the
+ * parameters that you want to set, and then call {@link #commit()}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerRenderer implements Renderer {
+
+    /**
+     * The servlet that is used to forward the request to.
+     */
+    private AttributeValueFreemarkerServlet servlet;
+
+    /**
+     * Constructor.
+     *
+     * @param servlet The servlet to use.
+     */
+    public FreemarkerRenderer(AttributeValueFreemarkerServlet servlet) {
+        this.servlet = servlet;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void render(String path, Request request) throws IOException {
+        if (path == null) {
+            throw new CannotRenderException("Cannot dispatch a null path");
+        }
+        ServletRequest servletRequest = org.apache.tiles.request.servlet.ServletUtil.getServletRequest(request);
+        HttpServletRequest httpRequest = servletRequest.getRequest();
+        HttpServletResponse httpResponse = servletRequest.getResponse();
+        servlet.setValue(path);
+        try {
+            servlet.doGet(httpRequest,
+                    new ExternalWriterHttpServletResponse(httpResponse,
+                            request.getPrintWriter()));
+        } catch (ServletException e) {
+            throw new FreemarkerRequestException("Exception when rendering a FreeMarker attribute", e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public boolean isRenderable(String path, Request request) {
+        return path != null && path.startsWith("/") && path.endsWith(".ftl");
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/FreemarkerRendererBuilder.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/FreemarkerRendererBuilder.java
new file mode 100644
index 0000000..dfb743f
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/FreemarkerRendererBuilder.java
@@ -0,0 +1,103 @@
+/*
+ * $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.request.freemarker.render;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.freemarker.FreemarkerRequestException;
+
+/**
+ * Builds instances of {@link FreemarkerRenderer}.
+ *
+ * @version $Rev$ $Date$
+ */
+public final class FreemarkerRendererBuilder {
+
+    /**
+     * The initialization parameters.
+     */
+    private Map<String, String> params = new HashMap<String, String>();
+
+    /**
+     * The application context.
+     */
+    private ApplicationContext applicationContext;
+
+    /**
+     * Constructor.
+     */
+    private FreemarkerRendererBuilder() {
+    }
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @return A new instance of the builder.
+     */
+    public static FreemarkerRendererBuilder createInstance() {
+        return new FreemarkerRendererBuilder();
+    }
+
+    /**
+     * Sets a parameter for the internal servlet.
+     *
+     * @param key The name of the parameter.
+     * @param value The value of the parameter.
+     * @return This object.
+     */
+    public FreemarkerRendererBuilder setParameter(String key, String value) {
+        params.put(key, value);
+        return this;
+    }
+
+    /**
+     * Sets the application context.
+     *
+     * @param applicationContext The application context.
+     * @return This object.
+     */
+    public FreemarkerRendererBuilder setApplicationContext(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+        return this;
+    }
+
+    /**
+     * Creates a new {@link FreemarkerRenderer} with the given configuration.
+     *
+     * @return A new Freemarker renderer.
+     */
+    public FreemarkerRenderer build() {
+        AttributeValueFreemarkerServlet servlet = new AttributeValueFreemarkerServlet();
+        try {
+            servlet.init(new InitParamsServletConfig(params, applicationContext));
+            return new FreemarkerRenderer(servlet);
+        } catch (ServletException e) {
+            throw new FreemarkerRequestException(
+                    "Cannot initialize internal servlet", e);
+        }
+
+    }
+
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/InitParamsServletConfig.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/InitParamsServletConfig.java
new file mode 100644
index 0000000..1599246
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/InitParamsServletConfig.java
@@ -0,0 +1,81 @@
+/*
+ * $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.request.freemarker.render;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+
+import org.apache.tiles.request.ApplicationContext;
+
+/**
+ * Implements {@link ServletConfig} to initialize the internal servlet using parameters
+ * set through {@link FreemarkerRenderer#setParameter(String, String)}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class InitParamsServletConfig implements ServletConfig {
+
+    /**
+     * The initialization parameters.
+     */
+    private Map<String, String> params = new HashMap<String, String>();
+
+    /**
+     * The application context.
+     */
+    private ApplicationContext applicationContext;
+
+    /**
+     * Constructor.
+     *
+     * @param params Configuration parameters.
+     * @param applicationContext The application context.
+     */
+    public InitParamsServletConfig(Map<String, String> params, ApplicationContext applicationContext) {
+        this.params = params;
+        this.applicationContext = applicationContext;
+    }
+
+    /** {@inheritDoc} */
+    public String getInitParameter(String name) {
+        return params.get(name);
+    }
+
+    /** {@inheritDoc} */
+    public Enumeration<String> getInitParameterNames() {
+        return Collections.enumeration(params.keySet());
+    }
+
+    /** {@inheritDoc} */
+    public ServletContext getServletContext() {
+        return org.apache.tiles.request.servlet.ServletUtil.getServletContext(applicationContext);
+    }
+
+    /** {@inheritDoc} */
+    public String getServletName() {
+        return "FreeMarker Attribute Renderer";
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/package-info.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/package-info.java
new file mode 100644
index 0000000..925f6a2
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/render/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Rendering of Freemarker templates.
+ */
+package org.apache.tiles.request.freemarker.render;
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/SharedVariableFactory.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/SharedVariableFactory.java
new file mode 100644
index 0000000..36cfc0c
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/SharedVariableFactory.java
@@ -0,0 +1,38 @@
+/*
+ * $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.request.freemarker.servlet;
+
+import freemarker.template.TemplateModel;
+
+/**
+ * It is an object that can create a shared variable, as a template model.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface SharedVariableFactory {
+
+    /**
+     * Creates a new shared variable.
+     *
+     * @return The shared variable.
+     */
+    TemplateModel create();
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/SharedVariableLoaderFreemarkerServlet.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/SharedVariableLoaderFreemarkerServlet.java
new file mode 100644
index 0000000..0401d43
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/SharedVariableLoaderFreemarkerServlet.java
@@ -0,0 +1,215 @@
+/*
+ * $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.request.freemarker.servlet;
+
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.tiles.request.reflect.ClassUtil;
+
+import freemarker.cache.TemplateLoader;
+import freemarker.ext.servlet.FreemarkerServlet;
+import freemarker.template.Configuration;
+
+/**
+ * Extends FreemarkerServlet to load Tiles directives as a shared variable.
+ *
+ * @version $Rev$ $Date$
+ */
+public class SharedVariableLoaderFreemarkerServlet extends FreemarkerServlet {
+
+    /**
+     * The serial UID.
+     */
+    private static final long serialVersionUID = 4301098067909854507L;
+
+    /**
+     * The init parameter under which the factories will be put. The value of the parameter
+     * must be a semicolon (;) separated list of couples, each member of the couple must
+     * be separated by commas (,).
+     */
+    public static final String CUSTOM_SHARED_VARIABLE_FACTORIES_INIT_PARAM =
+        "org.apache.tiles.request.freemarker.CUSTOM_SHARED_VARIABLE_FACTORIES";
+
+    /**
+     * Maps a name of a shared variable to its factory.
+     */
+    private Map<String, SharedVariableFactory> name2variableFactory =
+        new LinkedHashMap<String, SharedVariableFactory>();
+
+    @Override
+    public void init(ServletConfig config) throws ServletException {
+        String param = config.getInitParameter(CUSTOM_SHARED_VARIABLE_FACTORIES_INIT_PARAM);
+        if (param != null) {
+            String[] couples = param.split("\\s*;\\s*");
+            for (int i = 0; i < couples.length; i++) {
+                String[] couple = couples[i].split("\\s*,\\s*");
+                if (couple == null || couple.length != 2) {
+                    throw new ServletException(
+                            "Cannot parse custom shared variable partial init param: '"
+                                    + couples[i] + "'");
+                }
+                name2variableFactory.put(couple[0],
+                        (SharedVariableFactory) ClassUtil.instantiate(couple[1]));
+            }
+        }
+        super.init(new ExcludingParameterServletConfig(config));
+    }
+
+    /**
+     * Adds anew shared variable factory in a manual way.
+     *
+     * @param variableName The name of the shared variable.
+     * @param factory The shared variable factory.
+     */
+    public void addSharedVariableFactory(String variableName, SharedVariableFactory factory) {
+        name2variableFactory.put(variableName, factory);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected Configuration createConfiguration() {
+        Configuration configuration = super.createConfiguration();
+
+        for (Map.Entry<String, SharedVariableFactory> entry : name2variableFactory.entrySet()) {
+            configuration.setSharedVariable(entry.getKey(), entry.getValue().create());
+        }
+        return configuration;
+    }
+
+    /** {@inheritDoc} */
+
+    @Override
+    protected TemplateLoader createTemplateLoader(String templatePath) {
+        return new WebappClassTemplateLoader(getServletContext());
+    }
+
+    /**
+     * Servlet configuration that excludes some parameters. It is useful to adapt to
+     * FreemarkerServlet behaviour, because it gets angry if it sees some extra
+     * parameters that it does not recognize.
+     */
+    private class ExcludingParameterServletConfig implements ServletConfig {
+
+        /**
+         * The servlet configuration.
+         */
+        private ServletConfig config;
+
+        /**
+         * Constructor.
+         *
+         * @param config The servlet configuration.
+         */
+        public ExcludingParameterServletConfig(ServletConfig config) {
+            this.config = config;
+        }
+
+        @Override
+        public String getServletName() {
+            return config.getServletName();
+        }
+
+        @Override
+        public ServletContext getServletContext() {
+            return config.getServletContext();
+        }
+
+        @Override
+        public String getInitParameter(String name) {
+            if (CUSTOM_SHARED_VARIABLE_FACTORIES_INIT_PARAM.equals(name)) {
+                return null;
+            }
+            return config.getInitParameter(name);
+        }
+
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        @Override
+        public Enumeration getInitParameterNames() {
+            return new SkippingEnumeration(config.getInitParameterNames());
+        }
+
+    }
+
+    /**
+     * An enumeration that skip just
+     * {@link SharedVariableLoaderFreemarkerServlet#CUSTOM_SHARED_VARIABLE_FACTORIES_INIT_PARAM},
+     * again not to let the FreemarkerServlet be angry about it.
+     */
+    private static class SkippingEnumeration implements Enumeration<String> {
+
+        /**
+         * The original enumeration.
+         */
+        private Enumeration<String> enumeration;
+
+        /**
+         * The next element.
+         */
+        private String next = null;
+
+        /**
+         * Constructor.
+         *
+         * @param enumeration The original enumeration.
+         */
+        public SkippingEnumeration(Enumeration<String> enumeration) {
+            this.enumeration = enumeration;
+            updateNextElement();
+        }
+
+        @Override
+        public boolean hasMoreElements() {
+            return next != null;
+        }
+
+        @Override
+        public String nextElement() {
+            String retValue = next;
+            updateNextElement();
+            return retValue;
+        }
+
+        /**
+         * Updates the next element that will be passed.
+         */
+        private void updateNextElement() {
+            String value = null;
+            boolean done = false;
+            while (this.enumeration.hasMoreElements() && !done) {
+                value = this.enumeration.nextElement();
+                if (value.equals(CUSTOM_SHARED_VARIABLE_FACTORIES_INIT_PARAM)) {
+                    value = null;
+                } else {
+                    done = true;
+                }
+            }
+            next = value;
+        }
+
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/WebappClassTemplateLoader.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/WebappClassTemplateLoader.java
new file mode 100644
index 0000000..28ed069
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/WebappClassTemplateLoader.java
@@ -0,0 +1,86 @@
+/*
+ * $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.request.freemarker.servlet;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import javax.servlet.ServletContext;
+
+import freemarker.cache.ClassTemplateLoader;
+import freemarker.cache.TemplateLoader;
+import freemarker.cache.WebappTemplateLoader;
+
+/**
+ * Delegates loading templates using a {@link WebappTemplateLoader} and, if not
+ * found, a {@link ClassTemplateLoader}. The resources are loaded from the
+ * webapp root and from the classpath root.
+ *
+ * @version $Rev$ $Date$
+ */
+public class WebappClassTemplateLoader implements TemplateLoader {
+
+    /**
+     * The webapp template loader.
+     */
+    private WebappTemplateLoader webappTemplateLoader;
+
+    /**
+     * The webapp template loader.
+     */
+    private ClassTemplateLoader classTemplateLoader;
+
+    /**
+     * Constructor.
+     *
+     * @param servletContext The servlet context.
+     */
+    public WebappClassTemplateLoader(ServletContext servletContext) {
+        webappTemplateLoader = new WebappTemplateLoader(servletContext);
+        classTemplateLoader = new ClassTemplateLoader(getClass(), "/");
+    }
+
+    /** {@inheritDoc} */
+    public Object findTemplateSource(String name) throws IOException {
+        Object retValue = webappTemplateLoader.findTemplateSource(name);
+        if (retValue == null) {
+            retValue = classTemplateLoader.findTemplateSource(name);
+        }
+        return retValue;
+    }
+
+    /** {@inheritDoc} */
+    public void closeTemplateSource(Object templateSource) throws IOException {
+        webappTemplateLoader.closeTemplateSource(templateSource);
+    }
+
+    /** {@inheritDoc} */
+    public long getLastModified(Object templateSource) {
+        return webappTemplateLoader.getLastModified(templateSource);
+    }
+
+    /** {@inheritDoc} */
+    public Reader getReader(Object templateSource, String encoding)
+            throws IOException {
+        return webappTemplateLoader.getReader(templateSource, encoding);
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/package-info.java b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/package-info.java
new file mode 100644
index 0000000..c93d7b0
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/main/java/org/apache/tiles/request/freemarker/servlet/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Servlets for easy addition of shared variables.
+ */
+package org.apache.tiles.request.freemarker.servlet;
diff --git a/tiles-request/tiles-request-freemarker/src/site/site.xml b/tiles-request/tiles-request-freemarker/src/site/site.xml
new file mode 100644
index 0000000..8e92dd7
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Request Microframework">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Request Microframework"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/EnvironmentScopeMapTest.java b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/EnvironmentScopeMapTest.java
new file mode 100644
index 0000000..835768b
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/EnvironmentScopeMapTest.java
@@ -0,0 +1,99 @@
+/*
+ * $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.request.freemarker;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Set;
+
+import org.junit.Test;
+
+import freemarker.core.Environment;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateHashModel;
+import freemarker.template.TemplateHashModelEx;
+import freemarker.template.TemplateModelException;
+
+/**
+ * Tests {@link EnvironmentScopeMap}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class EnvironmentScopeMapTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.freemarker.EnvironmentScopeMap#keySet()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testKeySet() {
+        Template template = createMock(Template.class);
+        TemplateHashModel model = createMock(TemplateHashModel.class);
+        Configuration configuration = createMock(Configuration.class);
+        Set<String> names = createMock(Set.class);
+        Writer writer = new StringWriter();
+
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        expect(template.getConfiguration()).andReturn(configuration);
+        expect(configuration.getSharedVariableNames()).andReturn(names);
+
+        replay(template, model, configuration, names);
+        Environment env = new Environment(template, model, writer);
+        EnvironmentScopeMap map = new EnvironmentScopeMap(env);
+        assertEquals(names, map.keySet());
+        verify(template, model, configuration, names);
+    }
+
+
+    /**
+     * Test method for {@link org.apache.tiles.request.freemarker.EnvironmentScopeMap#keySet()}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @SuppressWarnings("unchecked")
+    @Test(expected = FreemarkerRequestException.class)
+    public void testKeySetException() throws TemplateModelException {
+        Template template = createMock(Template.class);
+        TemplateHashModelEx model = createMock(TemplateHashModelEx.class);
+        Configuration configuration = createMock(Configuration.class);
+        Set<String> names = createMock(Set.class);
+        Writer writer = new StringWriter();
+
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        expect(model.keys()).andThrow(new TemplateModelException());
+        expect(template.getConfiguration()).andReturn(configuration);
+        expect(configuration.getSharedVariableNames()).andReturn(names);
+
+        try {
+            replay(template, model, configuration, names);
+            Environment env = new Environment(template, model, writer);
+            EnvironmentScopeMap map = new EnvironmentScopeMap(env);
+            map.keySet();
+        } finally {
+            verify(template, model, configuration, names);
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/FreemarkerRequestExceptionTest.java b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/FreemarkerRequestExceptionTest.java
new file mode 100644
index 0000000..732d339
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/FreemarkerRequestExceptionTest.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.request.freemarker;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link FreemarkerRequestException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerRequestExceptionTest {
+
+    /**
+     * Test method for {@link FreemarkerRequestException#FreemarkerRequestException()}.
+     */
+    @Test
+    public void testFreemarkerRequestException() {
+        FreemarkerRequestException exception = new FreemarkerRequestException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link FreemarkerRequestException#FreemarkerRequestException(java.lang.String)}.
+     */
+    @Test
+    public void testFreemarkerRequestExceptionString() {
+        FreemarkerRequestException exception = new FreemarkerRequestException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link FreemarkerRequestException#FreemarkerRequestException(java.lang.Throwable)}.
+     */
+    @Test
+    public void testFreemarkerRequestExceptionThrowable() {
+        Throwable cause = new Throwable();
+        FreemarkerRequestException exception = new FreemarkerRequestException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for {@link FreemarkerRequestException#FreemarkerRequestException(String, Throwable)}.
+     */
+    @Test
+    public void testFreemarkerRequestExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        FreemarkerRequestException exception = new FreemarkerRequestException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/FreemarkerRequestTest.java b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/FreemarkerRequestTest.java
new file mode 100644
index 0000000..7a0de89
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/FreemarkerRequestTest.java
@@ -0,0 +1,261 @@
+/*
+ * $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.request.freemarker;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.DispatchRequest;
+import org.apache.tiles.request.servlet.ServletRequest;
+import org.junit.Before;
+import org.junit.Test;
+
+import freemarker.core.Environment;
+import freemarker.ext.servlet.HttpRequestHashModel;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateHashModel;
+import freemarker.template.TemplateModelException;
+
+/**
+ * Tests {@link FreemarkerRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerRequestTest {
+
+    /**
+     * The reuqest context to test.
+     */
+    private FreemarkerRequest context;
+
+    /**
+     * A string writer.
+     */
+    private StringWriter writer;
+
+    /**
+     * The FreeMarker environment.
+     */
+    private Environment env;
+
+    /**
+     * The locale object.
+     */
+    private Locale locale;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        Template template = createMock(Template.class);
+        TemplateHashModel model = createMock(TemplateHashModel.class);
+        writer = new StringWriter();
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        replay(template, model);
+        env = new Environment(template, model, writer);
+        locale = Locale.ITALY;
+        env.setLocale(locale);
+    }
+
+    /**
+     * Tests {@link FreemarkerRequest#createServletFreemarkerRequest(ApplicationContext, Environment)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test
+    public void testCreateServletFreemarkerRequest() throws TemplateModelException {
+        Template template = createMock(Template.class);
+        TemplateHashModel model = createMock(TemplateHashModel.class);
+        PrintWriter writer = new PrintWriter(new StringWriter());
+        HttpServletRequest httpRequest = createMock(HttpServletRequest.class);
+        HttpServletResponse httpResponse = createMock(HttpServletResponse.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+
+        replay(httpRequest, httpResponse, objectWrapper);
+        HttpRequestHashModel requestHashModel = new HttpRequestHashModel(httpRequest, httpResponse, objectWrapper);
+        expect(model.get("Request")).andReturn(requestHashModel);
+
+        replay(template, model, applicationContext);
+        Environment env = new Environment(template, model, writer);
+        Locale locale = Locale.ITALY;
+        env.setLocale(locale);
+
+        FreemarkerRequest request = FreemarkerRequest.createServletFreemarkerRequest(applicationContext, env);
+        ServletRequest servletRequest = (ServletRequest) request.getWrappedRequest();
+        assertEquals(httpRequest, servletRequest.getRequest());
+        assertEquals(httpResponse, servletRequest.getResponse());
+        verify(template, model, httpRequest, httpResponse, objectWrapper, applicationContext);
+    }
+
+    /**
+     * Tests {@link FreemarkerRequest#dispatch(String)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testDispatch() throws IOException {
+        String path = "this way";
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        Map<String, Object> requestScope = new HashMap<String, Object>();
+
+        enclosedRequest.include(path);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+        expect(enclosedRequest.getContext("request")).andReturn(requestScope);
+        replay(enclosedRequest, applicationContext);
+        context = new FreemarkerRequest(enclosedRequest, env);
+        context.dispatch(path);
+        verify(enclosedRequest, applicationContext);
+    }
+
+    /**
+     * Tests {@link FreemarkerRequest#getPageScope()}.
+     */
+    @Test
+    public void testGetPageScope() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+        replay(enclosedRequest);
+        context = new FreemarkerRequest(enclosedRequest, env);
+        assertTrue(context.getPageScope() instanceof EnvironmentScopeMap);
+        verify(enclosedRequest);
+    }
+
+    /**
+     * Tests {@link FreemarkerRequest#getNativeScopes()}.
+     */
+    @Test
+    public void testGetAvailableScopes() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+        replay(enclosedRequest);
+        context = new FreemarkerRequest(enclosedRequest, env);
+        assertArrayEquals(new String[] { "parent", "page" }, //
+                context.getAvailableScopes().toArray());
+        verify(enclosedRequest);
+    }
+
+    /**
+     * Tests {@link FreemarkerRequest#getRequestLocale()}.
+     */
+    @Test
+    public void testGetRequestLocale() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+        replay(enclosedRequest);
+        context = new FreemarkerRequest(enclosedRequest, env);
+        assertEquals(locale, context.getRequestLocale());
+        verify(enclosedRequest);
+    }
+
+    /**
+     * Tests {@link FreemarkerRequest#getRequest()}.
+     */
+    @Test
+    public void testGetRequest() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+        replay(enclosedRequest);
+        context = new FreemarkerRequest(enclosedRequest, env);
+        assertEquals(env, context.getEnvironment());
+        verify(enclosedRequest);
+    }
+
+    /**
+     * Tests {@link FreemarkerRequest#getResponse()}.
+     */
+    @Test
+    public void testGetResponse() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+        replay(enclosedRequest);
+        context = new FreemarkerRequest(enclosedRequest, env);
+        assertEquals(env, context.getEnvironment());
+        verify(enclosedRequest);
+    }
+
+    /**
+     * Tests {@link FreemarkerRequest#getPrintWriter()}.
+     */
+    @Test
+    public void testGetPrintWriter() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+        replay(enclosedRequest);
+        context = new FreemarkerRequest(enclosedRequest, env);
+        assertEquals(env, context.getEnvironment());
+        assertNotNull(context.getPrintWriter());
+        verify(enclosedRequest);
+    }
+
+    /**
+     * Tests {@link FreemarkerRequest#getPrintWriter()}.
+     */
+    @Test
+    public void testGetPrintWriterPrintWriter() {
+        Template template = createMock(Template.class);
+        TemplateHashModel model = createMock(TemplateHashModel.class);
+        PrintWriter writer = new PrintWriter(new StringWriter());
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        replay(template, model);
+        Environment env = new Environment(template, model, writer);
+        Locale locale = Locale.ITALY;
+        env.setLocale(locale);
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+        replay(enclosedRequest);
+        context = new FreemarkerRequest(enclosedRequest, env);
+        assertSame(writer, context.getPrintWriter());
+        verify(enclosedRequest, template, model);
+    }
+
+
+    /**
+     * Tests {@link FreemarkerRequest#getWriter()}.
+     */
+    @Test
+    public void testGetWriter() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+        replay(enclosedRequest);
+        context = new FreemarkerRequest(enclosedRequest, env);
+        assertEquals(env, context.getEnvironment());
+        assertNotNull(context.getWriter());
+        verify(enclosedRequest);
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/FreemarkerRequestUtilTest.java b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/FreemarkerRequestUtilTest.java
new file mode 100644
index 0000000..4d5b48f
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/FreemarkerRequestUtilTest.java
@@ -0,0 +1,209 @@
+/*
+ * $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.request.freemarker;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Locale;
+
+import javax.servlet.GenericServlet;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.tiles.request.ApplicationAccess;
+import org.apache.tiles.request.ApplicationContext;
+import org.junit.Before;
+import org.junit.Test;
+
+import freemarker.core.Environment;
+import freemarker.ext.servlet.HttpRequestHashModel;
+import freemarker.ext.servlet.ServletContextHashModel;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateHashModel;
+import freemarker.template.TemplateModelException;
+
+/**
+ * Tests {@link FreemarkerRequestUtil}.
+ *
+ */
+public class FreemarkerRequestUtilTest {
+
+    /**
+     * A string writer.
+     */
+    private StringWriter writer;
+
+    /**
+     * The FreeMarker environment.
+     */
+    private Environment env;
+
+    /**
+     * The locale object.
+     */
+    private Locale locale;
+
+    /**
+     * The template.
+     */
+    private Template template;
+
+    /**
+     * The template model.
+     */
+    private TemplateHashModel model;
+
+    /**
+     * Sets up the model.
+     */
+    @Before
+    public void setUp() {
+        template = createMock(Template.class);
+        model = createMock(TemplateHashModel.class);
+        writer = new StringWriter();
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+    }
+
+    /**
+     * Test method for {@link FreemarkerRequestUtil#getRequestHashModel(freemarker.core.Environment)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test
+    public void testGetRequestHashModel() throws TemplateModelException {
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+        HttpRequestHashModel requestModel = new HttpRequestHashModel(request, objectWrapper);
+
+        expect(model.get("Request")).andReturn(requestModel);
+
+        replay(template, model, request, objectWrapper);
+        env = new Environment(template, model, writer);
+        locale = Locale.ITALY;
+        env.setLocale(locale);
+        assertEquals(requestModel, FreemarkerRequestUtil.getRequestHashModel(env));
+        verify(template, model, request, objectWrapper);
+    }
+
+    /**
+     * Test method for {@link FreemarkerRequestUtil#getRequestHashModel(freemarker.core.Environment)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test(expected = NotAvailableFreemarkerServletException.class)
+    public void testGetRequestHashModelException() throws TemplateModelException {
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+
+        expect(model.get("Request")).andThrow(new TemplateModelException());
+
+        replay(template, model, request, objectWrapper);
+        try {
+            env = new Environment(template, model, writer);
+            locale = Locale.ITALY;
+            env.setLocale(locale);
+            FreemarkerRequestUtil.getRequestHashModel(env);
+        } finally {
+            verify(template, model, request, objectWrapper);
+        }
+    }
+
+
+    /**
+     * Test method for {@link FreemarkerRequestUtil
+     * #getServletContextHashModel(freemarker.core.Environment)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test
+    public void testGetServletContextHashModel() throws TemplateModelException {
+        GenericServlet servlet = createMock(GenericServlet.class);
+        ServletContext servletContext = createMock(ServletContext.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+        expect(servlet.getServletContext()).andReturn(servletContext);
+        replay(servlet, objectWrapper);
+        ServletContextHashModel servletContextModel = new ServletContextHashModel(servlet, objectWrapper);
+
+        expect(model.get("Application")).andReturn(servletContextModel);
+
+        replay(template, model, servletContext);
+        env = new Environment(template, model, writer);
+        locale = Locale.ITALY;
+        env.setLocale(locale);
+        assertEquals(servletContextModel, FreemarkerRequestUtil.getServletContextHashModel(env));
+        verify(template, model, servlet, servletContext, objectWrapper);
+    }
+
+    /**
+     * Test method for {@link FreemarkerRequestUtil
+     * #getServletContextHashModel(freemarker.core.Environment)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test(expected = NotAvailableFreemarkerServletException.class)
+    public void testGetServletContextHashModelException() throws TemplateModelException {
+        GenericServlet servlet = createMock(GenericServlet.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+        replay(servlet, objectWrapper);
+
+        expect(model.get("Application")).andThrow(new TemplateModelException());
+
+        replay(template, model);
+        try {
+            env = new Environment(template, model, writer);
+            locale = Locale.ITALY;
+            env.setLocale(locale);
+            FreemarkerRequestUtil.getServletContextHashModel(env);
+        } finally {
+            verify(template, model, servlet, objectWrapper);
+        }
+    }
+
+    /**
+     * Test method for {@link FreemarkerRequestUtil
+     * #getApplicationContext(Environment)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test
+    public void testGetApplicationContext() throws TemplateModelException {
+        GenericServlet servlet = createMock(GenericServlet.class);
+        ServletContext servletContext = createMock(ServletContext.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+
+        expect(servlet.getServletContext()).andReturn(servletContext).times(2);
+        expect(servletContext.getAttribute(ApplicationAccess
+                .APPLICATION_CONTEXT_ATTRIBUTE)).andReturn(applicationContext);
+
+        replay(servlet, objectWrapper);
+        ServletContextHashModel servletContextModel = new ServletContextHashModel(servlet, objectWrapper);
+
+        expect(model.get("Application")).andReturn(servletContextModel);
+
+        replay(template, model, servletContext);
+        env = new Environment(template, model, writer);
+        locale = Locale.ITALY;
+        env.setLocale(locale);
+        assertEquals(applicationContext, FreemarkerRequestUtil.getApplicationContext(env));
+        verify(template, model, servlet, servletContext, objectWrapper);
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/NotAvailableFreemarkerServletExceptionTest.java b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/NotAvailableFreemarkerServletExceptionTest.java
new file mode 100644
index 0000000..92d32c3
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/NotAvailableFreemarkerServletExceptionTest.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.request.freemarker;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link NotAvailableFreemarkerServletException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NotAvailableFreemarkerServletExceptionTest {
+
+    /**
+     * Test method for {@link NotAvailableFreemarkerServletException#NotAvailableFreemarkerServletException()}.
+     */
+    @Test
+    public void testNotAvailableFreemarkerServletException() {
+        NotAvailableFreemarkerServletException exception = new NotAvailableFreemarkerServletException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link NotAvailableFreemarkerServletException#NotAvailableFreemarkerServletException(String)}.
+     */
+    @Test
+    public void testNotAvailableFreemarkerServletExceptionString() {
+        NotAvailableFreemarkerServletException exception = new NotAvailableFreemarkerServletException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link NotAvailableFreemarkerServletException#NotAvailableFreemarkerServletException(Throwable)}.
+     */
+    @Test
+    public void testNotAvailableFreemarkerServletExceptionThrowable() {
+        Throwable cause = new Throwable();
+        NotAvailableFreemarkerServletException exception = new NotAvailableFreemarkerServletException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for
+     * {@link NotAvailableFreemarkerServletException#NotAvailableFreemarkerServletException(String, Throwable)}.
+     */
+    @Test
+    public void testNotAvailableFreemarkerServletExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        NotAvailableFreemarkerServletException exception =
+            new NotAvailableFreemarkerServletException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagExceptionTest.java b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagExceptionTest.java
new file mode 100644
index 0000000..aac061a
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagExceptionTest.java
@@ -0,0 +1,78 @@
+/*
+ * $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.request.freemarker.autotag;
+
+import static org.junit.Assert.*;
+
+import org.apache.tiles.request.freemarker.autotag.FreemarkerAutotagException;
+import org.junit.Test;
+
+/**
+ * Tests {@link FreemarkerAutotagException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerAutotagExceptionTest {
+
+    /**
+     * Test method for {@link FreemarkerAutotagException#FreemarkerAutotagException()}.
+     */
+    @Test
+    public void testFreemarkerAutotagException() {
+        FreemarkerAutotagException exception = new FreemarkerAutotagException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link FreemarkerAutotagException#FreemarkerAutotagException(java.lang.String)}.
+     */
+    @Test
+    public void testFreemarkerAutotagExceptionString() {
+        FreemarkerAutotagException exception = new FreemarkerAutotagException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link FreemarkerAutotagException#FreemarkerAutotagException(java.lang.Throwable)}.
+     */
+    @Test
+    public void testFreemarkerAutotagExceptionThrowable() {
+        Throwable cause = new Throwable();
+        FreemarkerAutotagException exception = new FreemarkerAutotagException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for {@link FreemarkerAutotagException#FreemarkerAutotagException(String, Throwable)}.
+     */
+    @Test
+    public void testFreemarkerAutotagExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        FreemarkerAutotagException exception = new FreemarkerAutotagException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagRuntimeTest.java b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagRuntimeTest.java
new file mode 100644
index 0000000..9a93b21
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerAutotagRuntimeTest.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.request.freemarker.autotag;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+import javax.servlet.GenericServlet;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.request.ApplicationAccess;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.freemarker.FreemarkerRequest;
+import org.apache.tiles.request.freemarker.autotag.FreemarkerAutotagRuntime;
+import org.apache.tiles.request.freemarker.autotag.FreemarkerModelBody;
+import org.junit.Test;
+import freemarker.core.Environment;
+import freemarker.core.Macro;
+import freemarker.ext.servlet.FreemarkerServlet;
+import freemarker.ext.servlet.HttpRequestHashModel;
+import freemarker.ext.servlet.ServletContextHashModel;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateHashModel;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateNumberModel;
+
+/**
+ * Tests {@link FreemarkerAutotagRuntime}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerAutotagRuntimeTest {
+
+    @Test
+    public void testCreateRequest() throws IOException, TemplateModelException{
+        @SuppressWarnings("unchecked")
+        Map<String, TemplateModel> params = createMock(Map.class);
+        Template template = createMock(Template.class);
+        TemplateHashModel rootDataModel = createMock(TemplateHashModel.class);
+        Writer out = createMock(Writer.class);
+        TemplateDirectiveBody body = createMock(TemplateDirectiveBody.class);
+        GenericServlet servlet = createMock(GenericServlet.class);
+        ObjectWrapper wrapper = createMock(ObjectWrapper.class);
+        ServletContext servletContext = createMock(ServletContext.class);
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        HttpServletRequest httpServletRequest = createMock(HttpServletRequest.class);
+        HttpServletResponse httpServletResponse = createMock(HttpServletResponse.class);
+
+        expect(template.getMacros()).andReturn(new HashMap<String, Macro>());
+        expect(servlet.getServletContext()).andReturn(servletContext)
+                .anyTimes();
+        expect(
+                servletContext
+                        .getAttribute(ApplicationAccess.APPLICATION_CONTEXT_ATTRIBUTE))
+                .andReturn(applicationContext);
+
+        replay(servlet, wrapper, servletContext, applicationContext,
+                httpServletRequest, httpServletResponse);
+        ServletContextHashModel servletContextHashModel = new ServletContextHashModel(
+                servlet, wrapper);
+        HttpRequestHashModel httpRequestHashModel = new HttpRequestHashModel(
+                httpServletRequest, httpServletResponse, wrapper);
+
+        expect(rootDataModel.get(FreemarkerServlet.KEY_APPLICATION)).andReturn(
+                servletContextHashModel);
+        expect(rootDataModel.get(FreemarkerServlet.KEY_REQUEST)).andReturn(
+                httpRequestHashModel);
+
+        replay(template, rootDataModel, out);
+        Environment env = new Environment(template, rootDataModel, out);
+
+        replay(params, body);
+        FreemarkerAutotagRuntime runtime = new FreemarkerAutotagRuntime();
+
+        runtime.execute(env, params, new TemplateModel[0], body);
+        Request request = runtime.createRequest();
+        assertTrue(request instanceof FreemarkerRequest);
+        verify(servlet, wrapper, servletContext, applicationContext,
+                httpServletRequest, httpServletResponse,
+                template, rootDataModel, out,
+                params, body);
+    }
+
+    @Test
+    public void testCreateModelBody() {
+        Template template = createMock(Template.class);
+        TemplateHashModel rootDataModel = createMock(TemplateHashModel.class);
+        Writer out = createMock(Writer.class);
+        expect(template.getMacros()).andReturn(new HashMap<String, Macro>());
+        replay(template, rootDataModel, out);
+        Environment env = new Environment(template, rootDataModel, out);
+        @SuppressWarnings("unchecked")
+        Map<String, TemplateModel> params = createMock(Map.class);
+        TemplateDirectiveBody body = createMock(TemplateDirectiveBody.class);
+        replay(params, body);
+        FreemarkerAutotagRuntime runtime = new FreemarkerAutotagRuntime();
+        runtime.execute(env, params, new TemplateModel[0], body);
+        ModelBody modelBody = runtime.createModelBody();
+        assertTrue(modelBody instanceof FreemarkerModelBody);
+        verify(template, rootDataModel, out, params, body);
+    }
+
+    @Test
+    public void testGetParameter() throws TemplateModelException {
+        Template template = createMock(Template.class);
+        TemplateHashModel rootDataModel = createMock(TemplateHashModel.class);
+        Writer out = createMock(Writer.class);
+        expect(template.getMacros()).andReturn(new HashMap<String, Macro>());
+        replay(template, rootDataModel, out);
+        Environment env = new Environment(template, rootDataModel, out);
+        TemplateNumberModel model = createMock(TemplateNumberModel.class);
+        expect(model.getAsNumber()).andReturn(new Integer(42)).anyTimes();
+        @SuppressWarnings("unchecked")
+        Map<String, TemplateModel> params = createMock(Map.class);
+        TemplateDirectiveBody body = createMock(TemplateDirectiveBody.class);
+        expect(params.get(eq("notnullParam"))).andReturn(model).anyTimes();
+        expect(params.get(eq("nullParam"))).andReturn(null).anyTimes();
+        replay(model, params, body);
+        FreemarkerAutotagRuntime runtime = new FreemarkerAutotagRuntime();
+        runtime.execute(env, params, new TemplateModel[0], body);
+        Object notnullParam = runtime.getParameter("notnullParam", null);
+        Object nullParam = runtime.getParameter("nullParam", null);
+        Object notnullParamDefault = runtime.getParameter("notnullParam", new Integer(24));
+        Object nullParamDefault = runtime.getParameter("nullParam", new Integer(24));
+        assertEquals(42, notnullParam);
+        assertEquals(null, nullParam);
+        assertEquals(42, notnullParamDefault);
+        assertEquals(24, nullParamDefault);
+        verify(template, rootDataModel, out, model, params, body);
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerModelBodyTest.java b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerModelBodyTest.java
new file mode 100644
index 0000000..30233d5
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerModelBodyTest.java
@@ -0,0 +1,95 @@
+/*
+ * $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.request.freemarker.autotag;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.tiles.request.freemarker.autotag.FreemarkerModelBody;
+import org.junit.Test;
+
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateException;
+
+/**
+ * Tests {@link FreemarkerModelBody}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerModelBodyTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.freemarker.autotag.FreemarkerModelBody#evaluate(java.io.Writer)}.
+     * @throws IOException If something goes wrong.
+     * @throws TemplateException If something goes wrong.
+     */
+    @Test
+    public void testEvaluateWriter() throws TemplateException, IOException {
+        TemplateDirectiveBody body = createMock(TemplateDirectiveBody.class);
+        Writer writer = createMock(Writer.class);
+
+        body.render(writer);
+
+        replay(body, writer);
+        FreemarkerModelBody modelBody = new FreemarkerModelBody(null, body);
+        modelBody.evaluate(writer);
+        verify(body, writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.freemarker.autotag.FreemarkerModelBody#evaluate(java.io.Writer)}.
+     * @throws IOException If something goes wrong.
+     * @throws TemplateException If something goes wrong.
+     */
+    @Test
+    public void testEvaluateWriterNull() throws TemplateException, IOException {
+        Writer writer = createMock(Writer.class);
+
+        replay(writer);
+        FreemarkerModelBody modelBody = new FreemarkerModelBody(null, null);
+        modelBody.evaluate(writer);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.freemarker.autotag.FreemarkerModelBody#evaluate(java.io.Writer)}.
+     * @throws IOException If something goes wrong.
+     * @throws TemplateException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testEvaluateWriterException() throws TemplateException, IOException {
+        TemplateDirectiveBody body = createMock(TemplateDirectiveBody.class);
+        Writer writer = createMock(Writer.class);
+
+        body.render(writer);
+        expectLastCall().andThrow(new TemplateException(null));
+
+        replay(body, writer);
+        try {
+            FreemarkerModelBody modelBody = new FreemarkerModelBody(null, body);
+            modelBody.evaluate(writer);
+        } finally {
+            verify(body, writer);
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerUtilTest.java b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerUtilTest.java
new file mode 100644
index 0000000..1841b23
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/autotag/FreemarkerUtilTest.java
@@ -0,0 +1,113 @@
+/*
+ * $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.request.freemarker.autotag;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.Writer;
+import java.util.HashMap;
+
+import org.apache.tiles.request.freemarker.autotag.FreemarkerAutotagException;
+import org.apache.tiles.request.freemarker.autotag.FreemarkerUtil;
+import org.junit.Test;
+
+import freemarker.core.Environment;
+import freemarker.core.Macro;
+import freemarker.template.Template;
+import freemarker.template.TemplateHashModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateNumberModel;
+
+/**
+ * Tests {@link FreemarkerUtil}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerUtilTest {
+
+    /**
+     * Test method for {@link FreemarkerUtil#getAsObject(TemplateModel, Object)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test
+    public void testGetAsObject() throws TemplateModelException {
+        TemplateNumberModel model = createMock(TemplateNumberModel.class);
+        Template template = createMock(Template.class);
+        TemplateHashModel rootDataModel = createMock(TemplateHashModel.class);
+        Writer out = createMock(Writer.class);
+
+        expect(model.getAsNumber()).andReturn(new Integer(42));
+        expect(template.getMacros()).andReturn(new HashMap<String, Macro>());
+
+        replay(template, rootDataModel, out);
+        new Environment(template, rootDataModel, out);
+
+        replay(model);
+        assertEquals(new Integer(42), FreemarkerUtil.getAsObject(model, new Integer(1)));
+        verify(template, rootDataModel, out, model);
+    }
+
+    /**
+     * Test method for {@link FreemarkerUtil#getAsObject(TemplateModel, Object)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test
+    public void testGetAsObjectDefault() throws TemplateModelException {
+        Template template = createMock(Template.class);
+        TemplateHashModel rootDataModel = createMock(TemplateHashModel.class);
+        Writer out = createMock(Writer.class);
+
+        expect(template.getMacros()).andReturn(new HashMap<String, Macro>());
+
+        replay(template, rootDataModel, out);
+        new Environment(template, rootDataModel, out);
+
+        assertEquals(new Integer(1), FreemarkerUtil.getAsObject(null, new Integer(1)));
+        verify(template, rootDataModel, out);
+    }
+
+    /**
+     * Test method for {@link FreemarkerUtil#getAsObject(TemplateModel, Object)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test(expected = FreemarkerAutotagException.class)
+    public void testGetAsObjectException() throws TemplateModelException {
+        TemplateNumberModel model = createMock(TemplateNumberModel.class);
+        Template template = createMock(Template.class);
+        TemplateHashModel rootDataModel = createMock(TemplateHashModel.class);
+        Writer out = createMock(Writer.class);
+
+        expect(model.getAsNumber()).andThrow(new TemplateModelException());
+        expect(template.getMacros()).andReturn(new HashMap<String, Macro>());
+
+        replay(template, rootDataModel, out);
+        new Environment(template, rootDataModel, out);
+
+        replay(model);
+        try {
+            assertEquals(new Integer(42), FreemarkerUtil.getAsObject(model, new Integer(1)));
+        } finally {
+            verify(template, rootDataModel, out, model);
+        }
+    }
+
+}
diff --git a/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/extractor/EnvironmentScopeExtractorTest.java b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/extractor/EnvironmentScopeExtractorTest.java
new file mode 100644
index 0000000..6a85415
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/extractor/EnvironmentScopeExtractorTest.java
@@ -0,0 +1,265 @@
+/*
+ * $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.request.freemarker.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.tiles.request.freemarker.FreemarkerRequestException;
+import org.junit.Test;
+
+import freemarker.core.Environment;
+import freemarker.template.Configuration;
+import freemarker.template.ObjectWrapper;
+import freemarker.template.Template;
+import freemarker.template.TemplateHashModel;
+import freemarker.template.TemplateHashModelEx;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateScalarModel;
+
+/**
+ * Tests {@link EnvironmentScopeExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class EnvironmentScopeExtractorTest {
+
+    /**
+     * Test method for {@link EnvironmentScopeExtractor#removeValue(java.lang.String)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test
+    public void testRemoveValue() throws TemplateModelException {
+        Template template = createMock(Template.class);
+        TemplateHashModel model = createMock(TemplateHashModel.class);
+        TemplateModel valueModel = createMock(TemplateModel.class);
+        Configuration configuration = createMock(Configuration.class);
+        Writer writer = new StringWriter();
+
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        expect(model.get("key")).andReturn(null);
+        expect(template.getConfiguration()).andReturn(configuration);
+        expect(configuration.getSharedVariable("key")).andReturn(null);
+
+        replay(template, model, valueModel, configuration);
+        Environment env = new Environment(template, model, writer);
+        env.setVariable("key", valueModel);
+        EnvironmentScopeExtractor extractor = new EnvironmentScopeExtractor(env);
+        extractor.removeValue("key");
+        assertNull(env.getVariable("key"));
+        verify(template, model, valueModel, configuration);
+    }
+
+    /**
+     * Test method for {@link EnvironmentScopeExtractor#getKeys()}.
+     */
+    @Test
+    public void testGetKeys() {
+        Template template = createMock(Template.class);
+        TemplateHashModel model = createMock(TemplateHashModel.class);
+        TemplateModel valueModel = createMock(TemplateModel.class);
+        Configuration configuration = createMock(Configuration.class);
+        Set<String> names = new HashSet<String>();
+        names.add("testGetKeys");
+        
+        Writer writer = new StringWriter();
+
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        expect(template.getConfiguration()).andReturn(configuration);
+        expect(configuration.getSharedVariableNames()).andReturn(names);
+
+        replay(template, model, valueModel, configuration);
+        Environment env = new Environment(template, model, writer);
+        EnvironmentScopeExtractor extractor = new EnvironmentScopeExtractor(env);
+        Enumeration<String> keys = extractor.getKeys();
+        assertEquals("testGetKeys", keys.nextElement());
+        assertFalse(keys.hasMoreElements());
+        verify(template, model, valueModel, configuration);
+    }
+
+    /**
+     * Test method for {@link EnvironmentScopeExtractor#getKeys()}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @SuppressWarnings("unchecked")
+    @Test(expected = FreemarkerRequestException.class)
+    public void testGetKeysException() throws TemplateModelException {
+        Template template = createMock(Template.class);
+        TemplateHashModelEx model = createMock(TemplateHashModelEx.class);
+        TemplateModel valueModel = createMock(TemplateModel.class);
+        Configuration configuration = createMock(Configuration.class);
+        Set<String> names = createMock(Set.class);
+        Iterator<String> namesIt = createMock(Iterator.class);
+        Writer writer = new StringWriter();
+
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        expect(model.keys()).andThrow(new TemplateModelException());
+        expect(template.getConfiguration()).andReturn(configuration);
+        expect(configuration.getSharedVariableNames()).andReturn(names);
+
+        replay(template, model, valueModel, configuration, names, namesIt);
+        try {
+            Environment env = new Environment(template, model, writer);
+            EnvironmentScopeExtractor extractor = new EnvironmentScopeExtractor(env);
+            extractor.getKeys();
+        } finally {
+            verify(template, model, valueModel, configuration, names, namesIt);
+        }
+    }
+
+    /**
+     * Test method for {@link EnvironmentScopeExtractor#getValue(java.lang.String)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test
+    public void testGetValue() throws TemplateModelException {
+        Template template = createMock(Template.class);
+        TemplateHashModel model = createMock(TemplateHashModel.class);
+        TemplateScalarModel valueModel = createMock(TemplateScalarModel.class);
+        Configuration configuration = createMock(Configuration.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+        Writer writer = new StringWriter();
+
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        expect(valueModel.getAsString()).andReturn("value");
+
+        replay(template, model, valueModel, configuration, objectWrapper);
+        Environment env = new Environment(template, model, writer);
+        env.setVariable("key", valueModel);
+        EnvironmentScopeExtractor extractor = new EnvironmentScopeExtractor(env);
+        assertEquals("value", extractor.getValue("key"));
+        verify(template, model, valueModel, configuration, objectWrapper);
+    }
+
+    /**
+     * Test method for {@link EnvironmentScopeExtractor#getValue(java.lang.String)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test
+    public void testGetValueNull() throws TemplateModelException {
+        Template template = createMock(Template.class);
+        TemplateHashModel model = createMock(TemplateHashModel.class);
+        TemplateScalarModel valueModel = createMock(TemplateScalarModel.class);
+        Configuration configuration = createMock(Configuration.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+        Writer writer = new StringWriter();
+
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        expect(model.get("key")).andReturn(null);
+        expect(template.getConfiguration()).andReturn(configuration);
+        expect(configuration.getSharedVariable("key")).andReturn(null);
+
+        replay(template, model, valueModel, configuration, objectWrapper);
+        Environment env = new Environment(template, model, writer);
+        EnvironmentScopeExtractor extractor = new EnvironmentScopeExtractor(env);
+        assertNull(extractor.getValue("key"));
+        verify(template, model, valueModel, configuration, objectWrapper);
+    }
+
+    /**
+     * Test method for {@link EnvironmentScopeExtractor#getValue(java.lang.String)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test(expected = FreemarkerRequestException.class)
+    public void testGetValueException() throws TemplateModelException {
+        Template template = createMock(Template.class);
+        TemplateHashModel model = createMock(TemplateHashModel.class);
+        TemplateScalarModel valueModel = createMock(TemplateScalarModel.class);
+        Configuration configuration = createMock(Configuration.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+        Writer writer = new StringWriter();
+
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        expect(model.get("key")).andThrow(new TemplateModelException());
+
+        replay(template, model, valueModel, configuration, objectWrapper);
+        try {
+            Environment env = new Environment(template, model, writer);
+            EnvironmentScopeExtractor extractor = new EnvironmentScopeExtractor(env);
+            extractor.getValue("key");
+        } finally {
+            verify(template, model, valueModel, configuration, objectWrapper);
+        }
+    }
+
+    /**
+     * Test method for {@link EnvironmentScopeExtractor#setValue(java.lang.String, java.lang.Object)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test
+    public void testSetValue() throws TemplateModelException {
+        Template template = createMock(Template.class);
+        TemplateHashModel model = createMock(TemplateHashModel.class);
+        TemplateModel valueModel = createMock(TemplateModel.class);
+        Configuration configuration = createMock(Configuration.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+        Writer writer = new StringWriter();
+
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        expect(template.getObjectWrapper()).andReturn(objectWrapper);
+        expect(objectWrapper.wrap("value")).andReturn(valueModel);
+
+        replay(template, model, valueModel, configuration, objectWrapper);
+        Environment env = new Environment(template, model, writer);
+        EnvironmentScopeExtractor extractor = new EnvironmentScopeExtractor(env);
+        extractor.setValue("key", "value");
+        assertEquals(valueModel, env.getVariable("key"));
+        verify(template, model, valueModel, configuration, objectWrapper);
+    }
+
+    /**
+     * Test method for {@link EnvironmentScopeExtractor#setValue(java.lang.String, java.lang.Object)}.
+     * @throws TemplateModelException If something goes wrong.
+     */
+    @Test(expected = FreemarkerRequestException.class)
+    public void testSetValueException() throws TemplateModelException {
+        Template template = createMock(Template.class);
+        TemplateHashModel model = createMock(TemplateHashModel.class);
+        TemplateModel valueModel = createMock(TemplateModel.class);
+        Configuration configuration = createMock(Configuration.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+        Writer writer = new StringWriter();
+
+        expect(template.getMacros()).andReturn(new HashMap<Object, Object>());
+        expect(template.getObjectWrapper()).andReturn(objectWrapper);
+        expect(objectWrapper.wrap("value")).andThrow(new TemplateModelException());
+
+        replay(template, model, valueModel, configuration, objectWrapper);
+        try {
+            Environment env = new Environment(template, model, writer);
+            EnvironmentScopeExtractor extractor = new EnvironmentScopeExtractor(env);
+            extractor.setValue("key", "value");
+            assertEquals(valueModel, env.getVariable("key"));
+        } finally {
+            verify(template, model, valueModel, configuration, objectWrapper);
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/render/FreemarkerRendererTest.java b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/render/FreemarkerRendererTest.java
new file mode 100644
index 0000000..9b4ee63
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/java/org/apache/tiles/request/freemarker/render/FreemarkerRendererTest.java
@@ -0,0 +1,238 @@
+/*
+ * $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.request.freemarker.render;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+
+import javax.servlet.GenericServlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.render.CannotRenderException;
+import org.apache.tiles.request.servlet.ServletApplicationContext;
+import org.apache.tiles.request.servlet.ServletRequest;
+import org.junit.Before;
+import org.junit.Test;
+
+import freemarker.ext.servlet.HttpRequestHashModel;
+import freemarker.ext.servlet.HttpRequestParametersHashModel;
+import freemarker.ext.servlet.ServletContextHashModel;
+import freemarker.template.ObjectWrapper;
+
+/**
+ * Tests {@link FreemarkerRenderer}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class FreemarkerRendererTest {
+
+    /**
+     * The attribute name of the application model.
+     */
+    private static final String ATTR_APPLICATION_MODEL =
+        ".freemarker.Application";
+
+    /**
+     * The attribute name of the JSP taglibs model.
+     */
+    private static final String ATTR_JSP_TAGLIBS_MODEL =
+        ".freemarker.JspTaglibs";
+
+    /**
+     * The attribute name of the request model.
+     */
+    private static final String ATTR_REQUEST_MODEL = ".freemarker.Request";
+
+    /**
+     * The attribute name of the request parameters model.
+     */
+    private static final String ATTR_REQUEST_PARAMETERS_MODEL =
+        ".freemarker.RequestParameters";
+
+    /**
+     * The renderer to test.
+     */
+    private FreemarkerRenderer renderer;
+
+    /**
+     * The application context.
+     */
+    private ApplicationContext applicationContext;
+
+    /**
+     * The servlet context.
+     */
+    private ServletContext servletContext;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        applicationContext = createMock(ServletApplicationContext.class);
+        servletContext = createMock(ServletContext.class);
+
+        expect(applicationContext.getContext()).andReturn(servletContext);
+
+        replay(applicationContext, servletContext);
+        renderer = FreemarkerRendererBuilder.createInstance()
+                .setApplicationContext(applicationContext)
+                .setParameter("TemplatePath", "/")
+                .setParameter("NoCache", "true")
+                .setParameter("ContentType", "text/html")
+                .setParameter("template_update_delay", "0")
+                .setParameter("default_encoding", "ISO-8859-1")
+                .setParameter("number_format", "0.##########").build();
+    }
+
+    /**
+     * Tests {@link FreemarkerRenderer#render(String, org.apache.tiles.request.Request)}.
+     * @throws IOException If something goes wrong.
+     * @throws ServletException If something goes wrong.
+     */
+    @Test
+    public void testWrite() throws IOException, ServletException {
+        ApplicationContext applicationContext = createMock(ServletApplicationContext.class);
+        ServletContext servletContext = createMock(ServletContext.class);
+        GenericServlet servlet = createMockBuilder(GenericServlet.class).createMock();
+        ServletConfig servletConfig = createMock(ServletConfig.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+
+        expect(servletConfig.getServletContext()).andReturn(servletContext);
+
+        replay(servlet, servletConfig);
+        servlet.init(servletConfig);
+        ServletContextHashModel servletContextHashModel = new ServletContextHashModel(servlet, objectWrapper);
+
+        expect(applicationContext.getContext()).andReturn(servletContext).anyTimes();
+        expect(servletContext.getRealPath(isA(String.class))).andReturn(null).anyTimes();
+        URL resource = getClass().getResource("/test.ftl");
+        expect(servletContext.getResource(isA(String.class))).andReturn(resource).anyTimes();
+        expect(servletContext.getAttribute(ATTR_APPLICATION_MODEL)).andReturn(servletContextHashModel);
+        expect(servletContext.getAttribute(ATTR_JSP_TAGLIBS_MODEL)).andReturn(null);
+
+        replay(applicationContext, servletContext, objectWrapper);
+
+        FreemarkerRenderer renderer = FreemarkerRendererBuilder
+                .createInstance().setApplicationContext(applicationContext)
+                .setParameter("TemplatePath", "/")
+                .setParameter("NoCache", "true")
+                .setParameter("ContentType", "text/html")
+                .setParameter("template_update_delay", "0")
+                .setParameter("default_encoding", "ISO-8859-1")
+                .setParameter("number_format", "0.##########").build();
+
+        ServletRequest request = createMock(ServletRequest.class);
+        HttpServletRequest httpRequest = createMock(HttpServletRequest.class);
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(stringWriter);
+        HttpRequestHashModel requestModel = new HttpRequestHashModel(httpRequest, response, objectWrapper);
+        HttpRequestParametersHashModel parametersModel = new HttpRequestParametersHashModel(httpRequest);
+
+        expect(request.getRequest()).andReturn(httpRequest);
+        expect(request.getResponse()).andReturn(response);
+        expect(request.getPrintWriter()).andReturn(printWriter);
+        expect(httpRequest.getSession(false)).andReturn(null);
+        expect(httpRequest.getAttribute(ATTR_REQUEST_MODEL)).andReturn(requestModel);
+        expect(httpRequest.getAttribute(ATTR_REQUEST_PARAMETERS_MODEL)).andReturn(parametersModel);
+        response.setContentType("text/html; charset=ISO-8859-1");
+        response.setHeader(eq("Cache-Control"), isA(String.class));
+        response.setHeader("Pragma", "no-cache");
+        response.setHeader(eq("Expires"), isA(String.class));
+
+        replay(request, httpRequest, response);
+        renderer.render("hello", request);
+        stringWriter.close();
+        assertTrue(stringWriter.toString().startsWith("Hello!"));
+        verify(applicationContext, servletContext, request, httpRequest,
+                response, servlet, servletConfig, objectWrapper);
+    }
+
+    /**
+     * Tests {@link FreemarkerRenderer#render(String, org.apache.tiles.request.Request)}.
+     * @throws IOException If something goes wrong.
+     * @throws ServletException If something goes wrong.
+     */
+    @Test(expected = CannotRenderException.class)
+    public void testRenderException1() throws IOException, ServletException {
+        ApplicationContext applicationContext = createMock(ServletApplicationContext.class);
+        ServletContext servletContext = createMock(ServletContext.class);
+        GenericServlet servlet = createMockBuilder(GenericServlet.class).createMock();
+        ServletConfig servletConfig = createMock(ServletConfig.class);
+        ObjectWrapper objectWrapper = createMock(ObjectWrapper.class);
+
+        replay(servlet, servletConfig);
+        servlet.init(servletConfig);
+
+        expect(applicationContext.getContext()).andReturn(servletContext).anyTimes();
+        expect(servletContext.getRealPath(isA(String.class))).andReturn(null).anyTimes();
+        URL resource = getClass().getResource("/test.ftl");
+        expect(servletContext.getResource(isA(String.class))).andReturn(resource).anyTimes();
+
+        replay(applicationContext, servletContext, objectWrapper);
+
+        FreemarkerRenderer renderer = FreemarkerRendererBuilder
+                .createInstance().setApplicationContext(applicationContext)
+                .setParameter("TemplatePath", "/")
+                .setParameter("NoCache", "true")
+                .setParameter("ContentType", "text/html")
+                .setParameter("template_update_delay", "0")
+                .setParameter("default_encoding", "ISO-8859-1")
+                .setParameter("number_format", "0.##########").build();
+
+        ServletRequest request = createMock(ServletRequest.class);
+
+        replay(request);
+        try {
+            renderer.render(null, request);
+        } finally {
+            verify(applicationContext, servletContext, request, servlet,
+                    servletConfig, objectWrapper);
+        }
+    }
+
+    /**
+     * Test method for
+     * {@link FreemarkerRenderer
+     * #isRenderable(Object, org.apache.tiles.Attribute, org.apache.tiles.context.TilesRequestContext)}
+     * .
+     */
+    @Test
+    public void testIsRenderable() {
+        assertTrue(renderer.isRenderable("/my/template.ftl", null));
+        assertFalse(renderer.isRenderable("my/template.ftl", null));
+        assertFalse(renderer.isRenderable("/my/template.jsp", null));
+        verify(applicationContext, servletContext);
+    }
+
+}
diff --git a/tiles-request/tiles-request-freemarker/src/test/resources/test.ftl b/tiles-request/tiles-request-freemarker/src/test/resources/test.ftl
new file mode 100644
index 0000000..3397cec
--- /dev/null
+++ b/tiles-request/tiles-request-freemarker/src/test/resources/test.ftl
@@ -0,0 +1,25 @@
+<#--
+/*
+ * $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.
+ *
+ */
+-->
+Hello!
+
diff --git a/tiles-request/tiles-request-jsp/pom.xml b/tiles-request/tiles-request-jsp/pom.xml
new file mode 100644
index 0000000..a726060
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<artifactId>tiles-request</artifactId>
+		<groupId>org.apache.tiles</groupId>
+		<version>1.0-SNAPSHOT</version>
+	</parent>
+	<groupId>org.apache.tiles</groupId>
+	<artifactId>tiles-request-jsp</artifactId>
+	<version>1.0-SNAPSHOT</version>
+	<name>Tiles Request - JSP support</name>
+	<description>JSP implementation of the Tiles Request framework.</description>
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.tiles</groupId>
+			<artifactId>tiles-request-api</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.tiles</groupId>
+			<artifactId>tiles-request-servlet</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-jdk14</artifactId>
+			<optional>true</optional>
+		</dependency>
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>servlet-api</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>javax.servlet.jsp</groupId>
+			<artifactId>jsp-api</artifactId>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.easymock</groupId>
+			<artifactId>easymock</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.easymock</groupId>
+			<artifactId>easymockclassextension</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.shale</groupId>
+			<artifactId>shale-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.tiles</groupId>
+			<artifactId>tiles-autotag-core-runtime</artifactId>
+			<optional>true</optional>
+		</dependency>
+	</dependencies>
+</project>
diff --git a/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/JspPrintWriterAdapter.java b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/JspPrintWriterAdapter.java
new file mode 100644
index 0000000..9f9ec4f
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/JspPrintWriterAdapter.java
@@ -0,0 +1,388 @@
+/*
+ * $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.request.jsp;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.jsp.JspWriter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Adapts a {@link JspWriter} to a {@link PrintWriter}, swallowing {@link IOException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class JspPrintWriterAdapter extends PrintWriter {
+
+    /**
+     * The JSP writer.
+     */
+    private JspWriter writer;
+
+    /**
+     * The logging object.
+     */
+    private final Logger log = LoggerFactory.getLogger(this.getClass());
+
+    /**
+     * Constructor.
+     *
+     * @param writer The JSP writer.
+     */
+    public JspPrintWriterAdapter(JspWriter writer) {
+        super(writer);
+        this.writer = writer;
+    }
+
+    /**
+     * Returns the original JSP writer.
+     *
+     * @return The JSP writer.
+     */
+    public JspWriter getJspWriter() {
+        return writer;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public PrintWriter append(char c) {
+        try {
+            writer.append(c);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public PrintWriter append(CharSequence csq, int start, int end) {
+        try {
+            writer.append(csq, start, end);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public PrintWriter append(CharSequence csq) {
+        try {
+            writer.append(csq);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void close() {
+        try {
+            writer.close();
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void flush() {
+        try {
+            writer.flush();
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void print(boolean b) {
+        try {
+            writer.print(b);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void print(char c) {
+        try {
+            writer.print(c);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void print(char[] s) {
+        try {
+            writer.print(s);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void print(double d) {
+        try {
+            writer.print(d);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void print(float f) {
+        try {
+            writer.print(f);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void print(int i) {
+        try {
+            writer.print(i);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void print(long l) {
+        try {
+            writer.print(l);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void print(Object obj) {
+        try {
+            writer.print(obj);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void print(String s) {
+        try {
+            writer.print(s);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void println() {
+        try {
+            writer.println();
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void println(boolean x) {
+        try {
+            writer.println(x);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void println(char x) {
+        try {
+            writer.println(x);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void println(char[] x) {
+        try {
+            writer.println(x);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void println(double x) {
+        try {
+            writer.println(x);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void println(float x) {
+        try {
+            writer.println(x);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void println(int x) {
+        try {
+            writer.println(x);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void println(long x) {
+        try {
+            writer.println(x);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void println(Object x) {
+        try {
+            writer.println(x);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void println(String x) {
+        try {
+            writer.println(x);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(char[] buf, int off, int len) {
+        try {
+            writer.write(buf, off, len);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(char[] buf) {
+        try {
+            writer.write(buf);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(int c) {
+        try {
+            writer.write(c);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(String s, int off, int len) {
+        try {
+            writer.write(s, off, len);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void write(String s) {
+        try {
+            writer.write(s);
+        } catch (IOException e) {
+            log.error("Error when writing in JspWriter", e);
+            setError();
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/JspRequest.java b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/JspRequest.java
new file mode 100644
index 0000000..ab00ef3
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/JspRequest.java
@@ -0,0 +1,215 @@
+/*
+ * $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.request.jsp;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.tiles.request.AbstractViewRequest;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.DispatchRequest;
+import org.apache.tiles.request.collection.ScopeMap;
+import org.apache.tiles.request.jsp.extractor.ScopeExtractor;
+import org.apache.tiles.request.jsp.extractor.SessionScopeExtractor;
+import org.apache.tiles.request.servlet.ServletRequest;
+import org.apache.tiles.request.servlet.ServletUtil;
+
+/**
+ * Context implementation used for executing tiles within a
+ * jsp tag library.
+ *
+ * @version $Rev$ $Date$
+ */
+public class JspRequest extends AbstractViewRequest {
+
+    /**
+     * The native available scopes.
+     */
+    private static final List<String> SCOPES
+            = Collections.unmodifiableList(Arrays.asList("page", "request", "session", "application"));
+
+    /**
+     * The current page context.
+     */
+    private PageContext pageContext;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of page scope
+     * attributes.</p>
+     */
+    private Map<String, Object> pageScope = null;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of request scope
+     * attributes.</p>
+     */
+    private Map<String, Object> requestScope = null;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of session scope
+     * attributes.</p>
+     */
+    private Map<String, Object> sessionScope = null;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of application scope
+     * attributes.</p>
+     */
+    private Map<String, Object> applicationScope = null;
+
+    /**
+     * Creates a JSP request.
+     *
+     * @param applicationContext The application context.
+     * @param pageContext The page context.
+     * @return A new JSP request.
+     */
+    public static JspRequest createServletJspRequest(ApplicationContext applicationContext, PageContext pageContext) {
+        return new JspRequest(new ServletRequest(
+                applicationContext, (HttpServletRequest) pageContext
+                        .getRequest(), (HttpServletResponse) pageContext
+                        .getResponse()), pageContext);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param enclosedRequest The request that is wrapped here.
+     * @param pageContext The page context to use.
+     */
+    public JspRequest(DispatchRequest enclosedRequest,
+            PageContext pageContext) {
+        super(enclosedRequest);
+        this.pageContext = pageContext;
+    }
+
+    @Override
+    public List<String> getAvailableScopes() {
+        return SCOPES;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doInclude(String path) throws IOException {
+        try {
+            pageContext.include(path, false);
+        } catch (ServletException e) {
+            throw ServletUtil.wrapServletException(e, "JSPException including path '"
+                    + path + "'.");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public PrintWriter getPrintWriter() {
+        return new JspPrintWriterAdapter(pageContext.getOut());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Writer getWriter() {
+        return pageContext.getOut();
+    }
+
+    /**
+     * Returns the page scope.
+     *
+     * @return The page scope.
+     */
+    public Map<String, Object> getPageScope() {
+        if ((pageScope == null) && (pageContext != null)) {
+            pageScope = new ScopeMap(new ScopeExtractor(pageContext,
+                    PageContext.PAGE_SCOPE));
+        }
+        return (pageScope);
+    }
+
+    /**
+     * Returns the request scope.
+     *
+     * @return The request scope.
+     */
+    public Map<String, Object> getRequestScope() {
+        if ((requestScope == null) && (pageContext != null)) {
+            requestScope = new ScopeMap(new ScopeExtractor(pageContext,
+                    PageContext.REQUEST_SCOPE));
+        }
+        return (requestScope);
+    }
+
+    /**
+     * Returns the session scope.
+     *
+     * @return The session scope.
+     */
+    public Map<String, Object> getSessionScope() {
+        if ((sessionScope == null) && (pageContext != null)) {
+            sessionScope = new ScopeMap(new SessionScopeExtractor(pageContext));
+        }
+        return (sessionScope);
+    }
+
+    /**
+     * Returns the application scope.
+     *
+     * @return The application scope.
+     */
+    public Map<String, Object> getApplicationScope() {
+        if ((applicationScope == null) && (pageContext != null)) {
+            applicationScope = new ScopeMap(new ScopeExtractor(pageContext,
+                    PageContext.APPLICATION_SCOPE));
+        }
+        return (applicationScope);
+    }
+
+    /**
+     * Returns the page context that originated the request.
+     *
+     * @return The page context.
+     */
+    public PageContext getPageContext() {
+        return pageContext;
+    }
+
+    @Override
+    public Map<String, Object> getContext(String scope) {
+        if("page".equals(scope)){
+            return getPageScope();
+        }else if("request".equals(scope)){
+            return getRequestScope();
+        }else if("session".equals(scope)){
+            return getSessionScope();
+        }else if("application".equals(scope)){
+            return getApplicationScope();
+        }
+        throw new IllegalArgumentException(scope + " does not exist. Call getAvailableScopes() first to check.");
+    }
+}
diff --git a/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/JspUtil.java b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/JspUtil.java
new file mode 100644
index 0000000..2083f10
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/JspUtil.java
@@ -0,0 +1,55 @@
+/*
+ * $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.request.jsp;
+
+import javax.servlet.jsp.JspContext;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.tiles.request.ApplicationAccess;
+import org.apache.tiles.request.ApplicationContext;
+
+/**
+ * JSP utilities for JSP requests and related.
+ *
+ * @version $Rev$ $Date$
+ */
+public final class JspUtil {
+
+    /**
+     * Constructor.
+     */
+    private JspUtil() {
+    }
+
+    /**
+     * Returns the application context. It must be
+     * first saved creating an {@link ApplicationContext} and using
+     * {@link org.apache.tiles.request.ApplicationAccess#register(ApplicationContext)}.
+     *
+     * @param jspContext The JSP context.
+     * @return The application context.
+     */
+    public static ApplicationContext getApplicationContext(JspContext jspContext) {
+        return (ApplicationContext) jspContext.getAttribute(
+                ApplicationAccess.APPLICATION_CONTEXT_ATTRIBUTE,
+                PageContext.APPLICATION_SCOPE);
+    }
+}
diff --git a/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/autotag/JspAutotagRuntime.java b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/autotag/JspAutotagRuntime.java
new file mode 100644
index 0000000..9b72230
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/autotag/JspAutotagRuntime.java
@@ -0,0 +1,61 @@
+/*
+ * $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.request.jsp.autotag;
+
+import javax.servlet.jsp.JspContext;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.SimpleTagSupport;
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.jsp.JspRequest;
+
+/**
+ * A Runtime for implementing JSP tag libraries.
+ */
+public class JspAutotagRuntime extends SimpleTagSupport implements AutotagRuntime {
+    /** {@inheritDoc} */
+    @Override
+    public void doTag() {
+        // do nothing like the parent implementation, 
+        // but don't throw exceptions either
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Request createRequest() {
+        JspContext pageContext = getJspContext();
+        return JspRequest.createServletJspRequest(org.apache.tiles.request.jsp.JspUtil.getApplicationContext(pageContext),
+                                                  (PageContext) pageContext);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ModelBody createModelBody() {
+        return new JspModelBody(getJspBody(), getJspContext());
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getParameter(String name, Object defaultValue) {
+        throw new UnsupportedOperationException("the parameters are injected into the tag itself, no need to fetch them");
+    }
+}
diff --git a/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/autotag/JspModelBody.java b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/autotag/JspModelBody.java
new file mode 100644
index 0000000..8918786
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/autotag/JspModelBody.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.request.jsp.autotag;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import javax.servlet.jsp.JspContext;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.JspFragment;
+
+import org.apache.tiles.autotag.core.runtime.AbstractModelBody;
+
+/**
+ * The body abstraction in a JSP tag.
+ *
+ * @version $Rev$ $Date$
+ */
+public class JspModelBody extends AbstractModelBody {
+
+    /**
+     * The real body.
+     */
+    private JspFragment jspFragment;
+
+    /**
+     * Constructor.
+     *
+     * @param jspFragment The real body.
+     * @param jspContext The page context.
+     */
+    public JspModelBody(JspFragment jspFragment, JspContext jspContext) {
+        super(jspContext.getOut());
+        this.jspFragment = jspFragment;
+    }
+
+    @Override
+    public void evaluate(Writer writer) throws IOException {
+        if (jspFragment == null) {
+            return;
+        }
+
+        try {
+            jspFragment.invoke(writer);
+        } catch (JspException e) {
+            throw new IOException("JspException when evaluating the body", e);
+        }
+    }
+
+}
diff --git a/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/autotag/package-info.java b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/autotag/package-info.java
new file mode 100644
index 0000000..08adc1b
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/autotag/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Runtime part of Autotag support for JavaServer pages.
+ */
+package org.apache.tiles.request.jsp.autotag;
diff --git a/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/extractor/ScopeExtractor.java b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/extractor/ScopeExtractor.java
new file mode 100644
index 0000000..68ae799
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/extractor/ScopeExtractor.java
@@ -0,0 +1,76 @@
+/*
+ * $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.request.jsp.extractor;
+
+import java.util.Enumeration;
+
+import javax.servlet.jsp.JspContext;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+
+/**
+ * Extracts attributes from a numbered scope from {@link JspContext}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ScopeExtractor implements AttributeExtractor {
+
+    /**
+     * The JSP context.
+     */
+    private JspContext context;
+
+    /**
+     * The scope number to use.
+     */
+    private int scope;
+
+    /**
+     * Constructor.
+     *
+     * @param context The JSP context.
+     * @param scope The scope number.
+     */
+    public ScopeExtractor(JspContext context, int scope) {
+        this.context = context;
+        this.scope = scope;
+    }
+
+    @Override
+    public void removeValue(String name) {
+        context.removeAttribute(name, scope);
+    }
+
+    @Override
+    public Enumeration<String> getKeys() {
+        return context.getAttributeNamesInScope(scope);
+    }
+
+    @Override
+    public Object getValue(String key) {
+        return context.getAttribute(key, scope);
+    }
+
+    @Override
+    public void setValue(String key, Object value) {
+        context.setAttribute(key, value, scope);
+    }
+}
diff --git a/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/extractor/SessionScopeExtractor.java b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/extractor/SessionScopeExtractor.java
new file mode 100644
index 0000000..359a840
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/extractor/SessionScopeExtractor.java
@@ -0,0 +1,81 @@
+/*
+ * $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.request.jsp.extractor;
+
+import java.util.Enumeration;
+
+import javax.servlet.jsp.PageContext;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+
+/**
+ * Extracts attributes from session scope from {@link PageContext}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class SessionScopeExtractor implements AttributeExtractor {
+
+    /**
+     * The page context.
+     */
+    private PageContext context;
+
+    /**
+     * Constructor.
+     *
+     * @param context The page context.
+     */
+    public SessionScopeExtractor(PageContext context) {
+        this.context = context;
+    }
+
+    @Override
+    public void removeValue(String name) {
+        if (context.getSession() == null) {
+            return;
+        }
+        context.removeAttribute(name, PageContext.SESSION_SCOPE);
+    }
+
+    @Override
+    public Enumeration<String> getKeys() {
+        if (context.getSession() == null) {
+            return null;
+        }
+        return context.getAttributeNamesInScope(PageContext.SESSION_SCOPE);
+    }
+
+    @Override
+    public Object getValue(String key) {
+        if (context.getSession() == null) {
+            return null;
+        }
+        return context.getAttribute(key, PageContext.SESSION_SCOPE);
+    }
+
+    @Override
+    public void setValue(String key, Object value) {
+        if (context.getSession() == null) {
+            return;
+        }
+        context.setAttribute(key, value, PageContext.SESSION_SCOPE);
+    }
+}
diff --git a/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/extractor/package-info.java b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/extractor/package-info.java
new file mode 100644
index 0000000..0e2d9cb
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/extractor/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Extractors to get scopes from {@link javax.servlet.jsp.PageContext}.
+ */
+package org.apache.tiles.request.jsp.extractor;
diff --git a/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/package-info.java b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/package-info.java
new file mode 100644
index 0000000..edf2b21
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/main/java/org/apache/tiles/request/jsp/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Support of Tiles request in a JSP environment.
+ */
+package org.apache.tiles.request.jsp;
diff --git a/tiles-request/tiles-request-jsp/src/site/site.xml b/tiles-request/tiles-request-jsp/src/site/site.xml
new file mode 100644
index 0000000..8e92dd7
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Request Microframework">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Request Microframework"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/JspPrintWriterAdapterTest.java b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/JspPrintWriterAdapterTest.java
new file mode 100644
index 0000000..be059a9
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/JspPrintWriterAdapterTest.java
@@ -0,0 +1,971 @@
+/*
+ * $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.request.jsp;
+
+import static org.easymock.classextension.EasyMock.*;
+
+import java.io.IOException;
+
+import javax.servlet.jsp.JspWriter;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests {@link JspPrintWriterAdapter}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class JspPrintWriterAdapterTest extends TestCase {
+
+    /**
+     * The string length.
+     */
+    private static final int STRING_LENGTH = 10;
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#write(int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testWriteInt() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.write(1);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.write(1);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#write(char[])}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testWriteCharArray() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.write(aryEq(result.toCharArray()));
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.write(result.toCharArray());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#write(char[], int, int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testWriteCharArrayIntInt() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.write(aryEq(result.toCharArray()), eq(0),
+                eq(STRING_LENGTH));
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.write(result.toCharArray(), 0, STRING_LENGTH);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#flush()}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testFlush() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.flush();
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#close()}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testClose() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.close();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.close();
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(boolean)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintBoolean() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print(true);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(true);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(char)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintChar() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print('c');
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print('c');
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintInt() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print(1);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(1);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(long)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintLong() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print(1L);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(1L);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(float)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintFloat() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print(1f);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(1f);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(double)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintDouble() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print(1d);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(1d);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(char[])}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintCharArray() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.print(aryEq(result.toCharArray()));
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(result.toCharArray());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println()}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintln() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println();
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(boolean)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnBoolean() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println(true);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(true);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(char)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnChar() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println('c');
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println('c');
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnInt() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println(1);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(1);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(long)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnLong() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println(1L);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(1L);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(float)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnFloat() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println(1f);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(1f);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(double)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnDouble() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println(1d);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(1d);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(char[])}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnCharArray() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.println(aryEq(result.toCharArray()));
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(result.toCharArray());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#getJspWriter()}.
+     */
+    public void testGetJspWriter() {
+        JspWriter writer = createMock(JspWriter.class);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        assertEquals(writer, adapter.getJspWriter());
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#append(char)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testAppendChar() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        expect(writer.append('c')).andReturn(writer);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        assertEquals(adapter, adapter.append('c'));
+        verify(writer);
+    }
+
+    /**
+     * Test method for
+     * {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#append(java.lang.CharSequence, int, int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testAppendCharSequenceIntInt() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        CharSequence sequence = createMock(CharSequence.class);
+        expect(writer.append(sequence, 0, STRING_LENGTH)).andReturn(writer);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        assertEquals(adapter, adapter.append(sequence, 0, STRING_LENGTH));
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#append(java.lang.CharSequence)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testAppendCharSequence() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        CharSequence sequence = createMock(CharSequence.class);
+        expect(writer.append(sequence)).andReturn(writer);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        assertEquals(adapter, adapter.append(sequence));
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(java.lang.Object)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintObject() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        Object obj = createMock(Object.class);
+        writer.print(obj);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(obj);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(java.lang.String)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintString() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print("this is a string");
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print("this is a string");
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(java.lang.Object)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnObject() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        Object obj = createMock(Object.class);
+        writer.println(obj);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(obj);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(java.lang.String)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnString() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println("this is a string");
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println("this is a string");
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#write(java.lang.String, int, int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testWriteStringIntInt() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.write(result, 0, STRING_LENGTH);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.write(result, 0, STRING_LENGTH);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#write(java.lang.String)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testWriteString() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.write(result);
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.write(result);
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#write(int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testWriteIntEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.write(1);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.write(1);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#write(char[])}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testWriteCharArrayEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.write(aryEq(result.toCharArray()));
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.write(result.toCharArray());
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#write(char[], int, int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testWriteCharArrayIntIntEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.write(aryEq(result.toCharArray()), eq(0),
+                eq(STRING_LENGTH));
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.write(result.toCharArray(), 0, STRING_LENGTH);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#flush()}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testFlushEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.flush();
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.flush();
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#close()}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testCloseEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.close();
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.close();
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(boolean)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintBooleanEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print(true);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(true);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(char)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintCharEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print('c');
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print('c');
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintIntEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print(1);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(1);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(long)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintLongEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print(1L);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(1L);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(float)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintFloatEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print(1f);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(1f);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(double)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintDoubleEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print(1d);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(1d);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(char[])}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintCharArrayEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.print(aryEq(result.toCharArray()));
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(result.toCharArray());
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println()}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println();
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println();
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(boolean)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnBooleanEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println(true);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(true);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(char)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnCharEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println('c');
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println('c');
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnIntEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println(1);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(1);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(long)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnLongEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println(1L);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(1L);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(float)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnFloatEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println(1f);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(1f);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(double)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnDoubleEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println(1d);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(1d);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(char[])}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnCharArrayEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.println(aryEq(result.toCharArray()));
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(result.toCharArray());
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#append(char)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testAppendCharEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        expect(writer.append('c')).andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        assertEquals(adapter, adapter.append('c'));
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for
+     * {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#append(java.lang.CharSequence, int, int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testAppendCharSequenceIntIntEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        CharSequence sequence = createMock(CharSequence.class);
+        expect(writer.append(sequence, 0, STRING_LENGTH)).andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        assertEquals(adapter, adapter.append(sequence, 0, STRING_LENGTH));
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#append(java.lang.CharSequence)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testAppendCharSequenceEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        CharSequence sequence = createMock(CharSequence.class);
+        expect(writer.append(sequence)).andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        assertEquals(adapter, adapter.append(sequence));
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(java.lang.Object)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintObjectEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        Object obj = createMock(Object.class);
+        writer.print(obj);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print(obj);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#print(java.lang.String)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintStringEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.print("this is a string");
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.print("this is a string");
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(java.lang.Object)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnObjectEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        Object obj = createMock(Object.class);
+        writer.println(obj);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println(obj);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#println(java.lang.String)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testPrintlnStringEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        writer.println("this is a string");
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.println("this is a string");
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#write(java.lang.String, int, int)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testWriteStringIntIntEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.write(result, 0, STRING_LENGTH);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.write(result, 0, STRING_LENGTH);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspPrintWriterAdapter#write(java.lang.String)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testWriteStringEx() throws IOException {
+        JspWriter writer = createMock(JspWriter.class);
+        String result = "this is a test";
+        writer.write(result);
+        expectLastCall().andThrow(new IOException());
+        writer.flush();
+        JspPrintWriterAdapter adapter = new JspPrintWriterAdapter(writer);
+        replay(writer);
+        adapter.write(result);
+        assertTrue(adapter.checkError());
+        verify(writer);
+    }
+}
diff --git a/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/JspRequestTest.java b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/JspRequestTest.java
new file mode 100644
index 0000000..14c9a47
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/JspRequestTest.java
@@ -0,0 +1,201 @@
+/*
+ * $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.request.jsp;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.DispatchRequest;
+import org.apache.tiles.request.collection.ScopeMap;
+import org.apache.tiles.request.servlet.ServletRequest;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link JspRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class JspRequestTest {
+
+    /**
+     * The enclosed request.
+     */
+    private DispatchRequest enclosedRequest;
+
+    /**
+     * The page context.
+     */
+    private PageContext context;
+
+    /**
+     * The request to test.
+     */
+    private JspRequest request;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        enclosedRequest = createMock(DispatchRequest.class);
+        context = createMock(PageContext.class);
+        request = new JspRequest(enclosedRequest, context);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspRequest#getWriter()}.
+     */
+    @Test
+    public void testGetWriter() {
+        JspWriter writer = createMock(JspWriter.class);
+
+        expect(context.getOut()).andReturn(writer);
+
+        replay(context, enclosedRequest, writer);
+        assertEquals(writer, request.getWriter());
+        verify(context, enclosedRequest, writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspRequest#getPrintWriter()}.
+     */
+    @Test
+    public void testGetPrintWriter() {
+        JspWriter writer = createMock(JspWriter.class);
+
+        expect(context.getOut()).andReturn(writer);
+
+        replay(context, enclosedRequest, writer);
+        assertEquals(writer, ((JspPrintWriterAdapter) request.getPrintWriter())
+                .getJspWriter());
+        verify(context, enclosedRequest, writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspRequest#doInclude(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws ServletException If something goes wrong.
+     */
+    @Test
+    public void testDoInclude() throws ServletException, IOException {
+        context.include("/my/path", false);
+
+        replay(context, enclosedRequest);
+        request.doInclude("/my/path");
+        verify(context, enclosedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspRequest#doInclude(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws ServletException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testDoIncludeException() throws ServletException, IOException {
+        context.include("/my/path", false);
+        expectLastCall().andThrow(new ServletException());
+
+        replay(context, enclosedRequest);
+        request.doInclude("/my/path");
+        verify(context, enclosedRequest);
+    }
+
+    /**
+     * Test method for {@link JspRequest#createServletJspRequest(ApplicationContext, PageContext)}.
+     */
+    @Test
+    public void testCreateServletJspRequest() {
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+        HttpServletResponse servletResponse = createMock(HttpServletResponse.class);
+
+        expect(context.getRequest()).andReturn(servletRequest);
+        expect(context.getResponse()).andReturn(servletResponse);
+
+        replay(context, applicationContext, servletRequest, servletResponse);
+        JspRequest request = JspRequest.createServletJspRequest(applicationContext, context);
+        ServletRequest wrappedRequest = (ServletRequest) request.getWrappedRequest();
+        assertEquals(servletRequest, wrappedRequest.getRequest());
+        assertEquals(servletResponse, wrappedRequest.getResponse());
+        verify(context, applicationContext, servletRequest, servletResponse);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspRequest#getPageScope()}.
+     */
+    @Test
+    public void testGetPageScope() {
+        replay(context, enclosedRequest);
+        assertTrue(request.getPageScope() instanceof ScopeMap);
+        verify(context, enclosedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspRequest#getRequestScope()}.
+     */
+    @Test
+    public void testGetRequestScope() {
+        replay(context, enclosedRequest);
+        assertTrue(request.getRequestScope() instanceof ScopeMap);
+        verify(context, enclosedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspRequest#getSessionScope()}.
+     */
+    @Test
+    public void testGetSessionScope() {
+        replay(context, enclosedRequest);
+        assertTrue(request.getSessionScope() instanceof ScopeMap);
+        verify(context, enclosedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspRequest#getApplicationScope()}.
+     */
+    @Test
+    public void testGetApplicationScope() {
+        replay(context, enclosedRequest);
+        assertTrue(request.getApplicationScope() instanceof ScopeMap);
+        verify(context, enclosedRequest);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspRequest#getPageContext()}.
+     */
+    @Test
+    public void testGetPageContext() {
+        replay(context, enclosedRequest);
+        assertEquals(context, request.getPageContext());
+        verify(context, enclosedRequest);
+    }
+}
diff --git a/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/JspUtilTest.java b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/JspUtilTest.java
new file mode 100644
index 0000000..ee90a40
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/JspUtilTest.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.request.jsp;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import javax.servlet.jsp.JspContext;
+import javax.servlet.jsp.PageContext;
+
+import org.apache.tiles.request.ApplicationAccess;
+import org.apache.tiles.request.ApplicationContext;
+import org.junit.Test;
+
+/**
+ * Tests {@link JspUtil}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class JspUtilTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.JspUtil#getApplicationContext(javax.servlet.jsp.JspContext)}.
+     */
+    @Test
+    public void testGetApplicationContext() {
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        JspContext jspContext = createMock(JspContext.class);
+
+        expect(jspContext.getAttribute(ApplicationAccess
+                .APPLICATION_CONTEXT_ATTRIBUTE, PageContext.APPLICATION_SCOPE))
+                .andReturn(applicationContext);
+
+        replay(applicationContext, jspContext);
+        assertEquals(applicationContext, JspUtil.getApplicationContext(jspContext));
+        verify(applicationContext, jspContext);
+    }
+}
diff --git a/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/autotag/JspAutotagRuntimeTest.java b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/autotag/JspAutotagRuntimeTest.java
new file mode 100644
index 0000000..53baf2c
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/autotag/JspAutotagRuntimeTest.java
@@ -0,0 +1,100 @@
+/*
+ * $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.request.jsp.autotag;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.JspContext;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.JspFragment;
+import javax.servlet.jsp.tagext.JspTag;
+
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.request.ApplicationAccess;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.jsp.JspRequest;
+import org.apache.tiles.request.jsp.autotag.JspAutotagRuntime;
+import org.apache.tiles.request.jsp.autotag.JspModelBody;
+import org.junit.Test;
+
+public class JspAutotagRuntimeTest {
+    @Test
+    public void testCreateRequest() {
+        JspFragment jspBody = createMock(JspFragment.class);
+        PageContext pageContext = createMock(PageContext.class);
+        JspTag parent = createMock(JspTag.class);
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        HttpServletRequest httpServletRequest = createMock(HttpServletRequest.class);
+        HttpServletResponse httpServletResponse = createMock(HttpServletResponse.class);
+        expect(pageContext.getAttribute(
+                ApplicationAccess.APPLICATION_CONTEXT_ATTRIBUTE,
+                PageContext.APPLICATION_SCOPE)).andReturn(applicationContext);
+        expect(pageContext.getRequest()).andReturn(httpServletRequest);
+        expect(pageContext.getResponse()).andReturn(httpServletResponse);
+        replay(jspBody, pageContext, parent, applicationContext, httpServletRequest, httpServletResponse);
+        JspAutotagRuntime runtime = new JspAutotagRuntime();
+        runtime.setJspBody(jspBody);
+        runtime.setJspContext(pageContext);
+        runtime.setParent(parent);
+        runtime.doTag();
+        Request jspRequest = runtime.createRequest();
+        assertTrue(jspRequest instanceof JspRequest);
+        verify(jspBody, pageContext, parent, applicationContext, httpServletRequest, httpServletResponse);
+    }
+
+    @Test
+    public void testCreateModelBody() {
+        JspFragment jspBody = createMock(JspFragment.class);
+        JspContext jspContext = createMock(JspContext.class);
+        JspTag parent = createMock(JspTag.class);
+        JspWriter writer = createMock(JspWriter.class);
+        expect(jspContext.getOut()).andReturn(writer);
+        replay(jspBody, jspContext, parent, writer);
+        JspAutotagRuntime runtime = new JspAutotagRuntime();
+        runtime.setJspBody(jspBody);
+        runtime.setJspContext(jspContext);
+        runtime.setParent(parent);
+        runtime.doTag();
+        ModelBody jspModelBody = runtime.createModelBody();
+        assertTrue(jspModelBody instanceof JspModelBody);
+        verify(jspBody, jspContext, parent, writer);
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetParameter() {
+        JspFragment jspBody = createMock(JspFragment.class);
+        JspContext jspContext = createMock(JspContext.class);
+        JspTag parent = createMock(JspTag.class);
+        replay(jspBody, jspContext, parent);
+        JspAutotagRuntime runtime = new JspAutotagRuntime();
+        runtime.setJspBody(jspBody);
+        runtime.setJspContext(jspContext);
+        runtime.setParent(parent);
+        runtime.doTag();
+        runtime.getParameter("test", null);
+        verify(jspBody, jspContext, parent);
+    }
+}
diff --git a/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/autotag/JspModelBodyTest.java b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/autotag/JspModelBodyTest.java
new file mode 100644
index 0000000..f00cb24
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/autotag/JspModelBodyTest.java
@@ -0,0 +1,104 @@
+/*
+ * $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.request.jsp.autotag;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.tagext.JspFragment;
+
+import org.apache.tiles.request.jsp.autotag.JspModelBody;
+import org.junit.Test;
+
+/**
+ * Tests {@link JspModelBody}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class JspModelBodyTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.autotag.freemarker.runtime.JspModelBody#evaluate(java.io.Writer)}.
+     * @throws IOException If something goes wrong.
+     * @throws JspException If something goes wrong.
+     */
+    @Test
+    public void testEvaluateWriter() throws JspException, IOException {
+        JspFragment body = createMock(JspFragment.class);
+        PageContext pageContext = createMock(PageContext.class);
+        JspWriter writer = createMock(JspWriter.class);
+
+        expect(pageContext.getOut()).andReturn(null);
+        body.invoke(writer);
+
+        replay(body, pageContext, writer);
+        JspModelBody modelBody = new JspModelBody(body, pageContext);
+        modelBody.evaluate(writer);
+        verify(body, pageContext, writer);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.autotag.freemarker.runtime.JspModelBody#evaluate(java.io.Writer)}.
+     * @throws IOException If something goes wrong.
+     * @throws JspException If something goes wrong.
+     */
+    @Test
+    public void testEvaluateWriterNull() throws JspException, IOException {
+        PageContext pageContext = createMock(PageContext.class);
+        Writer writer = createMock(Writer.class);
+
+        expect(pageContext.getOut()).andReturn(null);
+
+        replay(writer, pageContext);
+        JspModelBody modelBody = new JspModelBody(null, pageContext);
+        modelBody.evaluate(writer);
+        verify(writer, pageContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.autotag.freemarker.runtime.JspModelBody#evaluate(java.io.Writer)}.
+     * @throws IOException If something goes wrong.
+     * @throws JspException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testEvaluateWriterException() throws JspException, IOException {
+        PageContext pageContext = createMock(PageContext.class);
+        JspFragment body = createMock(JspFragment.class);
+        JspWriter writer = createMock(JspWriter.class);
+
+        expect(pageContext.getOut()).andReturn(null);
+        body.invoke(writer);
+        expectLastCall().andThrow(new JspException());
+
+        replay(body, pageContext, writer);
+        try {
+            JspModelBody modelBody = new JspModelBody(body, pageContext);
+            modelBody.evaluate(writer);
+        } finally {
+            verify(body, pageContext, writer);
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/extractor/ScopeExtractorTest.java b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/extractor/ScopeExtractorTest.java
new file mode 100644
index 0000000..465e8a8
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/extractor/ScopeExtractorTest.java
@@ -0,0 +1,110 @@
+/*
+ * $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.request.jsp.extractor;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.servlet.jsp.JspContext;
+import javax.servlet.jsp.PageContext;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ScopeExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ScopeExtractorTest {
+
+    /**
+     * The JSP context.
+     */
+    private JspContext context;
+
+    /**
+     * The extractor to test.
+     */
+    private ScopeExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        context = createMock(JspContext.class);
+        extractor = new ScopeExtractor(context, PageContext.PAGE_SCOPE);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.extractor.ScopeExtractor#removeValue(java.lang.String)}.
+     */
+    @Test
+    public void testRemoveValue() {
+        context.removeAttribute("key", PageContext.PAGE_SCOPE);
+
+        replay(context);
+        extractor.removeValue("key");
+        verify(context);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.extractor.ScopeExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        expect(context.getAttributeNamesInScope(PageContext.PAGE_SCOPE)).andReturn(keys);
+
+        replay(context);
+        assertEquals(keys, extractor.getKeys());
+        verify(context);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.extractor.ScopeExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(context.getAttribute("key", PageContext.PAGE_SCOPE)).andReturn("value");
+
+        replay(context);
+        assertEquals("value", extractor.getValue("key"));
+        verify(context);
+    }
+
+    /**
+     * Test method for {@link ScopeExtractor#setValue(String, Object)}.
+     */
+    @Test
+    public void testSetValue() {
+        context.setAttribute("key", "value", PageContext.PAGE_SCOPE);
+
+        replay(context);
+        extractor.setValue("key", "value");
+        verify(context);
+    }
+
+}
diff --git a/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/extractor/SessionScopeExtractorTest.java b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/extractor/SessionScopeExtractorTest.java
new file mode 100644
index 0000000..6aed2d4
--- /dev/null
+++ b/tiles-request/tiles-request-jsp/src/test/java/org/apache/tiles/request/jsp/extractor/SessionScopeExtractorTest.java
@@ -0,0 +1,169 @@
+/*
+ * $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.request.jsp.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.PageContext;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link SessionScopeExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class SessionScopeExtractorTest {
+
+    /**
+     * The page context.
+     */
+    private PageContext context;
+
+    /**
+     * The session.
+     */
+    private HttpSession session;
+
+    /**
+     * The extracto to test.
+     */
+    private SessionScopeExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        context = createMock(PageContext.class);
+        session = createMock(HttpSession.class);
+        extractor = new SessionScopeExtractor(context);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.extractor.ScopeExtractor#removeValue(java.lang.String)}.
+     */
+    @Test
+    public void testRemoveValue() {
+        expect(context.getSession()).andReturn(session);
+        context.removeAttribute("key", PageContext.SESSION_SCOPE);
+
+        replay(context, session);
+        extractor.removeValue("key");
+        verify(context, session);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.extractor.ScopeExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        expect(context.getSession()).andReturn(session);
+        Enumeration<String> keys = createMock(Enumeration.class);
+        expect(context.getAttributeNamesInScope(PageContext.SESSION_SCOPE)).andReturn(keys);
+
+        replay(context, session);
+        assertEquals(keys, extractor.getKeys());
+        verify(context, session);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.extractor.ScopeExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(context.getSession()).andReturn(session);
+        expect(context.getAttribute("key", PageContext.SESSION_SCOPE)).andReturn("value");
+
+       replay(context, session);
+       assertEquals("value", extractor.getValue("key"));
+       verify(context, session);
+    }
+
+    /**
+     * Test method for {@link ScopeExtractor#setValue(String, Object)}.
+     */
+    @Test
+    public void testSetValue() {
+        expect(context.getSession()).andReturn(session);
+        context.setAttribute("key", "value", PageContext.SESSION_SCOPE);
+
+        replay(context, session);
+        extractor.setValue("key", "value");
+        verify(context, session);
+    }
+
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.extractor.ScopeExtractor#removeValue(java.lang.String)}.
+     */
+    @Test
+    public void testRemoveValueNoSession() {
+        expect(context.getSession()).andReturn(null);
+
+        replay(context, session);
+        extractor.removeValue("key");
+        verify(context, session);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.extractor.ScopeExtractor#getKeys()}.
+     */
+    @Test
+    public void testGetKeysNoSession() {
+        expect(context.getSession()).andReturn(null);
+
+        replay(context, session);
+        assertNull(extractor.getKeys());
+        verify(context, session);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.jsp.extractor.ScopeExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValueNoSession() {
+       expect(context.getSession()).andReturn(null);
+
+       replay(context, session);
+       assertNull(extractor.getValue("key"));
+       verify(context, session);
+    }
+
+    /**
+     * Test method for {@link ScopeExtractor#setValue(String, Object)}.
+     */
+    @Test
+    public void testSetValueNoSession() {
+        expect(context.getSession()).andReturn(null);
+
+        replay(context, session);
+        extractor.setValue("key", "value");
+        verify(context, session);
+    }
+}
diff --git a/tiles-request/tiles-request-mustache/pom.xml b/tiles-request/tiles-request-mustache/pom.xml
new file mode 100644
index 0000000..c114b37
--- /dev/null
+++ b/tiles-request/tiles-request-mustache/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>tiles-request</artifactId>
+    <groupId>org.apache.tiles</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.tiles</groupId>
+  <artifactId>tiles-request-mustache</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Tiles request - Mustache support</name>
+  <description>Tiles request implementation for Mustache templates.</description>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.tiles</groupId>
+      <artifactId>tiles-request-api</artifactId>
+    </dependency>
+    <dependency>
+        <groupId>com.github.spullara.mustache.java</groupId>
+        <artifactId>core</artifactId>
+    </dependency>
+    <dependency>
+        <groupId>com.github.spullara.mustache.java</groupId>
+        <artifactId>builder</artifactId>
+    </dependency>
+    <dependency>
+    	<groupId>junit</groupId>
+    	<artifactId>junit</artifactId>
+    	<scope>test</scope>
+    </dependency>
+    <dependency>
+    	<groupId>org.easymock</groupId>
+    	<artifactId>easymock</artifactId>
+    	<scope>test</scope>
+    </dependency>
+    <dependency>
+    	<groupId>org.easymock</groupId>
+    	<artifactId>easymockclassextension</artifactId>
+    	<scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/MustacheRenderer.java b/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/MustacheRenderer.java
new file mode 100644
index 0000000..5c286f2
--- /dev/null
+++ b/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/MustacheRenderer.java
@@ -0,0 +1,98 @@
+/*
+ * $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.request.mustache;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.sampullara.mustache.MustacheBuilder;
+import com.sampullara.mustache.MustacheException;
+import com.sampullara.mustache.Scope;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.ApplicationResource;
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.render.CannotRenderException;
+import org.apache.tiles.request.render.Renderer;
+
+/**
+ * The Mustache-specific renderer.
+ *
+ * @version $Rev: 1215006 $ $Date: 2011-12-16 01:30:41 +0100 (Fri, 16 Dec 2011) $
+ */
+public final class MustacheRenderer implements Renderer {
+
+    private Pattern acceptPattern;
+
+    @Override
+    public void render(String path, Request request) throws IOException {
+        if (path == null) {
+            throw new CannotRenderException("Cannot dispatch a null path");
+        }
+
+        try{
+            new MustacheBuilder()
+                    .build(new BufferedReader(new InputStreamReader(getResourceStream(request, path))), path)
+                    .execute(request.getWriter(), buildScope(request));
+
+        }catch(MustacheException ex){
+            throw new IOException("failed to MustacheRenderer.render(" + path + ",request)", ex);
+        }
+    }
+
+    private static InputStream getResourceStream(Request request, String path) throws IOException {
+        final ApplicationContext applicationContext = request.getApplicationContext();
+        final ApplicationResource resource = applicationContext.getResource(path);
+        return resource.getInputStream();
+    }
+
+    private static Scope buildScope(Request request){
+        Scope scope = null;
+        List<String> availableScopes = request.getAvailableScopes();
+        for(int i = availableScopes.size() -1; i >= 0; --i){
+            scope = null == scope
+                    ? new Scope(request.getContext(availableScopes.get(i)))
+                    : new Scope(request.getContext(availableScopes.get(i)), scope);
+        }
+        return scope;
+    }
+
+    //@Override
+    public boolean isRenderable(String path, Request request) {
+        if (path == null) {
+            return false;
+        }
+        if (acceptPattern != null) {
+            final Matcher matcher = acceptPattern.matcher(path);
+            return matcher.matches();
+        }
+        return true;
+    }
+
+    public void setAcceptPattern(Pattern acceptPattern) {
+        this.acceptPattern = acceptPattern;
+    }
+}
diff --git a/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/MustacheScopeExtractor.java b/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/MustacheScopeExtractor.java
new file mode 100644
index 0000000..2c839e2
--- /dev/null
+++ b/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/MustacheScopeExtractor.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Apache Software Foundation.
+ *
+ * Licensed 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.request.mustache;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Set;
+
+import com.sampullara.mustache.Scope;
+import org.apache.tiles.request.attribute.AttributeExtractor;
+
+
+public final class MustacheScopeExtractor  implements AttributeExtractor {
+    private final Scope scope;
+
+    public MustacheScopeExtractor(Scope scope){
+        this.scope = scope;
+    }
+
+    @Override
+    public void removeValue(String key) {
+        scope.remove(key);
+    }
+
+    @Override
+    public Enumeration<String> getKeys() {
+        return (Enumeration<String>) Collections.enumeration((Set<?>)scope.keySet());
+    }
+
+    @Override
+    public Object getValue(String key) {
+        return scope.get(key);
+    }
+
+    @Override
+    public void setValue(String key, Object value) {
+        scope.put(value, value);
+    }
+}
\ No newline at end of file
diff --git a/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/MustacheScopeMap.java b/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/MustacheScopeMap.java
new file mode 100644
index 0000000..f323588
--- /dev/null
+++ b/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/MustacheScopeMap.java
@@ -0,0 +1,75 @@
+/*
+ * $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.request.mustache;
+
+import java.util.Set;
+
+import com.sampullara.mustache.Scope;
+import org.apache.tiles.request.collection.ScopeMap;
+
+
+final class MustacheScopeMap extends ScopeMap {
+
+    /**
+     * The request object to use.
+     */
+    private Scope scope = null;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request object to use.
+     */
+    public MustacheScopeMap(Scope request) {
+        super(new MustacheScopeExtractor(request));
+        this.scope = request;
+    }
+
+    @Override
+    public Object remove(Object key) {
+        return scope.remove(key);
+    }
+
+    @Override
+    public Object put(String key, Object value) {
+        return scope.put(key, value);
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return scope.containsKey(key);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return size() < 1;
+    }
+
+    @Override
+    public Set<String> keySet() {
+        return (Set<String>)(Set<?>)scope.keySet();
+    }
+
+    @Override
+    public int size() {
+        return scope.keySet().size();
+    }
+}
diff --git a/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/package-info.java b/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/package-info.java
new file mode 100644
index 0000000..7422cdf
--- /dev/null
+++ b/tiles-request/tiles-request-mustache/src/main/java/org/apache/tiles/request/mustache/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Support of Tiles request for Mustache.
+ */
+package org.apache.tiles.request.mustache;
diff --git a/tiles-request/tiles-request-mustache/src/site/site.xml b/tiles-request/tiles-request-mustache/src/site/site.xml
new file mode 100644
index 0000000..8e92dd7
--- /dev/null
+++ b/tiles-request/tiles-request-mustache/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Request Microframework">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Request Microframework"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-request/tiles-request-mustache/src/test/java/org/apache/tiles/request/mustache/MustacheRendererTest.java b/tiles-request/tiles-request-mustache/src/test/java/org/apache/tiles/request/mustache/MustacheRendererTest.java
new file mode 100644
index 0000000..fd920f7
--- /dev/null
+++ b/tiles-request/tiles-request-mustache/src/test/java/org/apache/tiles/request/mustache/MustacheRendererTest.java
@@ -0,0 +1,112 @@
+/*
+ * $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.request.mustache;
+
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.ApplicationResource;
+import org.apache.tiles.request.mustache.MustacheRenderer;
+import org.apache.tiles.request.render.CannotRenderException;
+import org.apache.tiles.request.render.Renderer;
+import org.apache.tiles.request.Request;
+import org.junit.Test;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests {@link MustacheRenderer}.
+ *
+ * @version $Rev: 1066788 $ $Date: 2011-02-03 11:49:11 +0000 (Thu, 03 Feb 2011) $
+ */
+public final class MustacheRendererTest {
+
+    /**
+     * Tests {@link MustacheRenderer#render(String, org.apache.tiles.request.Request)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testRender() throws IOException {
+        Request request = createMock(Request.class);
+        Writer writer = createMock(Writer.class);
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        ApplicationResource applicationResource = createMock(ApplicationResource.class);
+        expect(applicationResource.getInputStream()).andReturn(getClass().getResource("/test.html").openStream());
+
+        Map<String,Object> context = Collections.singletonMap("testKey", (Object)"test value");
+
+        expect(request.getApplicationContext()).andReturn(applicationContext);
+        expect(applicationContext.getResource(isA(String.class))).andReturn(applicationResource).anyTimes();
+        expect(request.getAvailableScopes()).andReturn(Arrays.asList("request", "session", "application"));
+        expect(request.getContext("request")).andReturn(context);
+        expect(request.getContext("session")).andReturn(Collections.<String,Object>emptyMap());
+        expect(request.getContext("application")).andReturn(Collections.<String,Object>emptyMap());
+        expect(request.getWriter()).andReturn(writer);
+        writer.write("test template with test value");
+        writer.flush();
+
+        replay(request, applicationContext, applicationResource, writer);
+        Renderer renderer = new MustacheRenderer();
+        renderer.render("/test.html", request);
+        verify(request, applicationContext, applicationResource, writer);
+    }
+
+    /**
+     * Tests {@link MustacheRenderer#render(String, org.apache.tiles.request.Request)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test(expected = CannotRenderException.class)
+    public void testRenderException() throws IOException {
+        Request request = createMock(Request.class);
+        replay(request);
+        Renderer renderer = new MustacheRenderer();
+        try {
+            renderer.render(null, request);
+        } finally {
+            verify(request);
+        }
+    }
+
+    /**
+     * Test method for
+     * {@link MustacheRenderer#isRenderable(String, org.apache.tiles.request.Request)}
+     * .
+     */
+    @Test
+    public void testIsRenderable() {
+        MustacheRenderer renderer = new MustacheRenderer();
+        final Pattern pattern = Pattern.compile("/.*");
+        renderer.setAcceptPattern(pattern);
+
+        assertTrue(renderer.isRenderable("/my/template.html", null));
+        assertTrue(renderer.isRenderable("/my/template.any", null));
+        assertFalse(renderer.isRenderable("my/template.html", null));
+        assertFalse(renderer.isRenderable(null, null));
+    }
+}
diff --git a/tiles-request/tiles-request-mustache/src/test/resources/test.html b/tiles-request/tiles-request-mustache/src/test/resources/test.html
new file mode 100644
index 0000000..594cbb0
--- /dev/null
+++ b/tiles-request/tiles-request-mustache/src/test/resources/test.html
@@ -0,0 +1,20 @@
+{{!
+  ! $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.
+}}test template with {{testKey}}
\ No newline at end of file
diff --git a/tiles-request/tiles-request-portlet-wildcard/pom.xml b/tiles-request/tiles-request-portlet-wildcard/pom.xml
new file mode 100644
index 0000000..075b485
--- /dev/null
+++ b/tiles-request/tiles-request-portlet-wildcard/pom.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0"?>
+<!--
+/*
+ * $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.
+ */
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <parent>
+    <groupId>org.apache.tiles</groupId>
+    <artifactId>tiles-request</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>tiles-request-portlet-wildcard</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>jar</packaging>
+  <name>Tiles - Wildcard file loading in Portlets</name>
+  <description>Tiles Wildcard file loading in Portlets: Allows to load resources using wildcards.</description>
+
+  <properties>
+  	<tiles.osgi.symbolicName>org.apache.tiles.portlet.wildcard</tiles.osgi.symbolicName>
+  </properties>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <excludes>
+          <exclude>LICENSE.txt</exclude>
+          <exclude>NOTICE.txt</exclude>
+        </excludes>
+      </resource>
+      <resource>
+        <directory>src/main/resources</directory>
+        <includes>
+          <include>LICENSE.txt</include>
+          <include>NOTICE.txt</include>
+        </includes>
+        <targetPath>META-INF</targetPath>
+      </resource>
+    </resources>
+
+    <plugins>
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifest>
+            </manifest>
+          </archive>
+        </configuration>
+      </plugin>
+    </plugins>
+
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.tiles</groupId>
+      <artifactId>tiles-request-portlet</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>jcl-over-slf4j</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-jdk14</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-web</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-webmvc-portlet</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.portlet</groupId>
+      <artifactId>portlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymockclassextension</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/tiles-request/tiles-request-portlet-wildcard/src/main/java/org/apache/tiles/request/portlet/wildcard/WildcardPortletApplicationContext.java b/tiles-request/tiles-request-portlet-wildcard/src/main/java/org/apache/tiles/request/portlet/wildcard/WildcardPortletApplicationContext.java
new file mode 100644
index 0000000..c178932
--- /dev/null
+++ b/tiles-request/tiles-request-portlet-wildcard/src/main/java/org/apache/tiles/request/portlet/wildcard/WildcardPortletApplicationContext.java
@@ -0,0 +1,116 @@
+/*
+ * $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.request.portlet.wildcard;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Locale;
+
+import javax.portlet.PortletContext;
+
+import org.apache.tiles.request.ApplicationResource;
+import org.apache.tiles.request.locale.URLApplicationResource;
+import org.apache.tiles.request.portlet.PortletApplicationContext;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.web.portlet.context.PortletContextResourcePatternResolver;
+
+/**
+ * Portlet-based implementation of the TilesApplicationContext interface that
+ * can resolve resources even using wildcards.
+ *
+ * @version $Rev$ $Date$
+ */
+public class WildcardPortletApplicationContext extends PortletApplicationContext {
+
+    /**
+     * The pattern resolver.
+     */
+    protected ResourcePatternResolver resolver;
+
+    /**
+     * Constructor.
+     *
+     * @param portletContext The portlet context.
+     */
+    public WildcardPortletApplicationContext(PortletContext portletContext) {
+        super(portletContext);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void initialize(PortletContext context) {
+        super.initialize(context);
+
+        resolver = new PortletContextResourcePatternResolver(context);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ApplicationResource getResource(String localePath) {
+        ApplicationResource retValue = null;
+        Collection<ApplicationResource> resourceSet = getResources(localePath);
+        if (resourceSet != null && !resourceSet.isEmpty()) {
+            retValue = resourceSet.iterator().next();
+        }
+        return retValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ApplicationResource getResource(ApplicationResource base, Locale locale) {
+        ApplicationResource retValue = null;
+        Collection<ApplicationResource> resourceSet = getResources(base.getLocalePath(locale));
+        if (resourceSet != null && !resourceSet.isEmpty()) {
+            retValue = resourceSet.iterator().next();
+        }
+        return retValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<ApplicationResource> getResources(String path) {
+        Resource[] resources;
+        try {
+            resources = resolver.getResources(path);
+        } catch (IOException e) {
+            return Collections.<ApplicationResource> emptyList();
+        }
+        Collection<ApplicationResource> resourceList = new ArrayList<ApplicationResource>();
+        if (resources != null && resources.length > 0) {
+            for (int i = 0; i < resources.length; i++) {
+                URL url;
+                try {
+                    url = resources[i].getURL();
+                    resourceList.add(new URLApplicationResource(url.toExternalForm(), url));
+                } catch (IOException e) {
+                    // shouldn't happen with the kind of resources we're using
+                    throw new IllegalArgumentException("no URL for " + resources[i].toString(), e);
+                }
+            }
+        }
+        return resourceList;
+    }
+}
diff --git a/tiles-request/tiles-request-portlet-wildcard/src/main/java/org/apache/tiles/request/portlet/wildcard/package-info.java b/tiles-request/tiles-request-portlet-wildcard/src/main/java/org/apache/tiles/request/portlet/wildcard/package-info.java
new file mode 100644
index 0000000..45ca43b
--- /dev/null
+++ b/tiles-request/tiles-request-portlet-wildcard/src/main/java/org/apache/tiles/request/portlet/wildcard/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Classes and interfaces that allow to access the various contexts from a portlet
+ * application. These classes can manage resource getting through the use of
+ * Spring-like wildcards patterns.
+ */
+package org.apache.tiles.request.portlet.wildcard;
diff --git a/tiles-request/tiles-request-portlet-wildcard/src/site/site.xml b/tiles-request/tiles-request-portlet-wildcard/src/site/site.xml
new file mode 100644
index 0000000..8e92dd7
--- /dev/null
+++ b/tiles-request/tiles-request-portlet-wildcard/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Request Microframework">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Request Microframework"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-request/tiles-request-portlet-wildcard/src/test/java/org/apache/tiles/request/portlet/wildcard/WildcardPortletApplicationContextTest.java b/tiles-request/tiles-request-portlet-wildcard/src/test/java/org/apache/tiles/request/portlet/wildcard/WildcardPortletApplicationContextTest.java
new file mode 100644
index 0000000..1bf84f5
--- /dev/null
+++ b/tiles-request/tiles-request-portlet-wildcard/src/test/java/org/apache/tiles/request/portlet/wildcard/WildcardPortletApplicationContextTest.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.request.portlet.wildcard;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.portlet.PortletContext;
+
+import junit.framework.TestCase;
+
+import org.apache.tiles.request.locale.URLApplicationResource;
+import org.easymock.EasyMock;
+
+
+/**
+ * Tests {@link WildcardPortletApplicationContext}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class WildcardPortletApplicationContextTest extends TestCase {
+
+    /**
+     * Number of properties container inside the test.properties file.
+     */
+    private static final int TEST_PROPERTIES_SIZE = 3;
+
+    /**
+     * Number of test classes.
+     */
+    private static final int TEST_TEST_SIZE = 1;
+
+    /**
+     * The root Tiles application context.
+     */
+    private PortletContext portletContext;
+
+    /**
+     * The enhanced Tiles application context.
+     */
+    private WildcardPortletApplicationContext context;
+
+    /**
+     * The original class loader.
+     */
+    private ClassLoader original;
+
+    /** {@inheritDoc} */
+    @Override
+    public void setUp() {
+        portletContext = EasyMock.createMock(PortletContext.class);
+        original = Thread.currentThread().getContextClassLoader();
+        try {
+            Thread.currentThread().setContextClassLoader(new MockClassLoader());
+        } catch (MalformedURLException e) {
+            throw new RuntimeException("Error when using the mock classloader");
+        }
+        context = new WildcardPortletApplicationContext(portletContext);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void tearDown() {
+        Thread.currentThread().setContextClassLoader(original);
+    }
+
+    /**
+     * Tests resource getting.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testGetResources() throws IOException {
+        String url = "test.properties";
+        HashSet<URL> set = new HashSet<URL>();
+        URL u = new URL("file://tiles/test.properties");
+        set.add(u);
+        EasyMock.expect(portletContext.getResource("/" + url)).andReturn(u)
+                .anyTimes();
+        File dir = new File(".");
+        EasyMock.expect(portletContext.getResource("/WEB-INF/")).andReturn(
+                dir.toURI().toURL());
+        URL pomUrl = new URL("file://tiles/pom.xml");
+        EasyMock.expect(portletContext.getResource("/WEB-INF/pom.xml"))
+                .andReturn(pomUrl);
+        Set<String> elementSet = new HashSet<String>();
+        elementSet.add("/WEB-INF/pom.xml");
+        EasyMock.expect(portletContext.getResourcePaths("/WEB-INF/")).andReturn(elementSet);
+        EasyMock.replay(portletContext);
+
+        assertEquals(new URLApplicationResource(u.toExternalForm(), u), context.getResource("/" + url));
+        assertEquals(new URLApplicationResource(pomUrl.toExternalForm(), pomUrl), context.getResource("/WEB-INF/*.xml"));
+        assertEquals(TEST_PROPERTIES_SIZE, context.getResources(
+                "classpath*:/test.properties").size());
+
+        assertEquals(TEST_TEST_SIZE, context.getResources(
+                "classpath*:/org/apache/tiles/request/portlet/wildcard/*Test.class").size());
+        EasyMock.verify(portletContext);
+    }
+
+    /**
+     * An mock class loader.
+     */
+    public class MockClassLoader extends ClassLoader {
+
+        /**
+         * A vector of resources.
+         */
+        private Vector<URL> testPropertiesResources;
+
+        /**
+         * Constructor.
+         *
+         * @throws MalformedURLException If the URL is not valid (that should
+         * not happen).
+         */
+        public MockClassLoader() throws MalformedURLException {
+            testPropertiesResources = new Vector<URL>();
+            testPropertiesResources.add(new URL("file://tiles/test/test.properties"));
+            testPropertiesResources.add(new URL("file://tiles/two/test.properties"));
+            testPropertiesResources.add(new URL("file://tiles/three/test.properties"));
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Enumeration<URL> findResources(String path) throws IOException {
+            Enumeration<URL> retValue = null;
+            if ("test.properties".equals(path)) {
+                retValue = testPropertiesResources.elements();
+            } else {
+                retValue = super.findResources(path);
+            }
+
+            return retValue;
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/pom.xml b/tiles-request/tiles-request-portlet/pom.xml
new file mode 100644
index 0000000..6941891
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/pom.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0"?>
+<!--
+/*
+ * $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.
+ */
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <parent>
+    <groupId>org.apache.tiles</groupId>
+    <artifactId>tiles-request</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>tiles-request-portlet</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>jar</packaging>
+  <name>Tiles - Portlet support</name>
+  <description>Tiles portlet support, to enable use of Tiles inside a Portlet container.
+  </description>
+
+  <properties>
+      <tiles.osgi.symbolicName>org.apache.tiles.portlet</tiles.osgi.symbolicName>
+  </properties>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <excludes>
+          <exclude>LICENSE.txt</exclude>
+          <exclude>NOTICE.txt</exclude>
+        </excludes>
+      </resource>
+      <resource>
+        <directory>src/main/resources</directory>
+        <includes>
+          <include>LICENSE.txt</include>
+          <include>NOTICE.txt</include>
+        </includes>
+        <targetPath>META-INF</targetPath>
+      </resource>
+    </resources>
+
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.tiles</groupId>
+      <artifactId>tiles-request-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.tiles</groupId>
+      <artifactId>tiles-request-servlet</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-jdk14</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.portlet</groupId>
+      <artifactId>portlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymockclassextension</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.shale</groupId>
+      <artifactId>shale-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+  </dependencies>
+
+</project>
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/ActionPortletRequest.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/ActionPortletRequest.java
new file mode 100644
index 0000000..e927788
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/ActionPortletRequest.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.request.portlet;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletContext;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.portlet.delegate.StateAwareRequestDelegate;
+import org.apache.tiles.request.portlet.delegate.StateAwareResponseDelegate;
+
+/**
+ * Portlet request for an {@link ActionRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ActionPortletRequest extends PortletRequest {
+
+    /**
+     * Constructor.
+     *
+     * @param applicationContext The application context.
+     * @param context The portlet context.
+     * @param request The portlet request.
+     * @param response The portlet response.
+     */
+    public ActionPortletRequest(ApplicationContext applicationContext,
+            PortletContext context, ActionRequest request, ActionResponse response) {
+        super(applicationContext, context, request, response,
+                new StateAwareRequestDelegate(request, response),
+                new StateAwareResponseDelegate());
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/EventPortletRequest.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/EventPortletRequest.java
new file mode 100644
index 0000000..0855375
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/EventPortletRequest.java
@@ -0,0 +1,53 @@
+/*
+ * $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.request.portlet;
+
+import javax.portlet.EventRequest;
+import javax.portlet.EventResponse;
+import javax.portlet.PortletContext;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.portlet.delegate.StateAwareRequestDelegate;
+import org.apache.tiles.request.portlet.delegate.StateAwareResponseDelegate;
+
+/**
+ * Portlet request for an {@link EventRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class EventPortletRequest extends PortletRequest {
+
+    /**
+     * Constructor.
+     *
+     * @param applicationContext The application context.
+     * @param context The portlet context.
+     * @param request The portlet request.
+     * @param response The portlet response.
+     */
+    public EventPortletRequest(ApplicationContext applicationContext,
+            PortletContext context, EventRequest request,
+            EventResponse response) {
+        super(applicationContext, context, request, response,
+                new StateAwareRequestDelegate(request, response),
+                new StateAwareResponseDelegate());
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/PortletApplicationContext.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/PortletApplicationContext.java
new file mode 100644
index 0000000..95bb31a
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/PortletApplicationContext.java
@@ -0,0 +1,152 @@
+/*
+ * $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.request.portlet;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.portlet.PortletContext;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.ApplicationResource;
+import org.apache.tiles.request.collection.ReadOnlyEnumerationMap;
+import org.apache.tiles.request.collection.ScopeMap;
+import org.apache.tiles.request.locale.URLApplicationResource;
+import org.apache.tiles.request.portlet.extractor.ApplicationScopeExtractor;
+import org.apache.tiles.request.portlet.extractor.InitParameterExtractor;
+
+/**
+ * Portlet-based TilesApplicationContext implementation.
+ *
+ * @version $Rev$ $Date$
+ */
+public class PortletApplicationContext implements ApplicationContext {
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of application scope
+     * attributes.</p>
+     */
+    private Map<String, Object> applicationScope = null;
+
+    /**
+     * <p>The <code>PortletContext</code> for this web application.</p>
+     */
+    protected PortletContext context = null;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of context initialization
+     * parameters.</p>
+     */
+    private Map<String, String> initParam = null;
+
+    /**
+     * Creates a new instance of PortletTilesApplicationContext.
+     *
+     * @param context The portlet context to use.
+     */
+    public PortletApplicationContext(PortletContext context) {
+        initialize(context);
+    }
+
+    /** {@inheritDoc} */
+    public Object getContext() {
+        return context;
+    }
+
+    /**
+     * <p>Initialize (or reinitialize) this {@link PortletApplicationContext} instance
+     * for the specified Portlet API objects.</p>
+     *
+     * @param context The <code>PortletContext</code> for this web application
+     */
+    public void initialize(PortletContext context) {
+
+        // Save the specified Portlet API object references
+        this.context = context;
+
+    }
+
+    /**
+     * <p>Return the {@link PortletContext} for this context.</p>
+     *
+     * @return The original portlet context.
+     */
+    public PortletContext getPortletContext() {
+        return (this.context);
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, Object> getApplicationScope() {
+        if ((applicationScope == null) && (context != null)) {
+            applicationScope = new ScopeMap(new ApplicationScopeExtractor(context));
+        }
+        return (applicationScope);
+
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String> getInitParams() {
+        if ((initParam == null) && (context != null)) {
+            initParam = new ReadOnlyEnumerationMap<String>(new InitParameterExtractor(context));
+        }
+        return (initParam);
+
+    }
+
+    /** {@inheritDoc} */
+    public ApplicationResource getResource(String localePath) {
+        try {
+            URL url = context.getResource(localePath);
+            if (url != null) {
+                return new URLApplicationResource(localePath, url);
+            } else {
+                return null;
+            }
+        } catch (MalformedURLException e) {
+            return null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public ApplicationResource getResource(ApplicationResource base, Locale locale) {
+        try {
+            URL url = context.getResource(base.getLocalePath(locale));
+            if (url != null) {
+                return new URLApplicationResource(base.getPath(), locale, url);
+            } else {
+                return null;
+            }
+        } catch (MalformedURLException e) {
+            return null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Collection<ApplicationResource> getResources(String path) {
+        ArrayList<ApplicationResource> resources = new ArrayList<ApplicationResource>();
+        resources.add(getResource(path));
+        return resources;
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/PortletRequest.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/PortletRequest.java
new file mode 100644
index 0000000..fa13cc3
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/PortletRequest.java
@@ -0,0 +1,337 @@
+/*
+ * $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.request.portlet;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.portlet.PortletContext;
+import javax.portlet.PortletException;
+import javax.portlet.PortletRequestDispatcher;
+import javax.portlet.PortletResponse;
+import javax.portlet.PortletSession;
+
+import org.apache.tiles.request.AbstractClientRequest;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.attribute.Addable;
+import org.apache.tiles.request.collection.HeaderValuesMap;
+import org.apache.tiles.request.collection.ReadOnlyEnumerationMap;
+import org.apache.tiles.request.collection.ScopeMap;
+import org.apache.tiles.request.portlet.delegate.RequestDelegate;
+import org.apache.tiles.request.portlet.delegate.ResponseDelegate;
+import org.apache.tiles.request.portlet.extractor.HeaderExtractor;
+import org.apache.tiles.request.portlet.extractor.RequestScopeExtractor;
+import org.apache.tiles.request.portlet.extractor.SessionScopeExtractor;
+
+/**
+ * Portlet-based TilesApplicationContext implementation.
+ *
+ * @version $Rev$ $Date$
+ */
+public class PortletRequest extends AbstractClientRequest {
+
+    /**
+     * The native available scopes.
+     */
+    private static final List<String> SCOPES
+            = Collections.unmodifiableList(Arrays.asList("request", "portletSession", "session", "application"));
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of header name-value
+     * combinations (immutable).</p>
+     */
+    private Map<String, String> header = null;
+
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of header name-value
+     * combinations (write-only).</p>
+     */
+    private Addable<String> responseHeaders = null;
+
+
+    /**
+     * <p>The lazily instantitated <code>Map</code> of header name-values
+     * combinations (immutable).</p>
+     */
+    private Map<String, String[]> headerValues = null;
+
+    /**
+     * The <code>PortletContext</code> for this application.
+     */
+    protected PortletContext context = null;
+
+    /**
+     * <p>The <code>PortletRequest</code> for this request.</p>
+     */
+    protected javax.portlet.PortletRequest request = null;
+
+    /**
+     * The delegate to get information about parameters.
+     */
+    protected RequestDelegate requestDelegate;
+
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of request scope
+     * attributes.</p>
+     */
+    private Map<String, Object> requestScope = null;
+
+
+    /**
+     * <p>The <code>PortletResponse</code> for this request.</p>
+     */
+    protected PortletResponse response = null;
+
+    /**
+     * The delegate to get information from a response (output stream, writer, etc.).
+     */
+    protected ResponseDelegate responseDelegate;
+
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of session scope
+     * attributes.</p>
+     */
+    private Map<String, Object> sessionScope = null;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of portlet session scope
+     * attributes.</p>
+     */
+    private Map<String, Object> portletSessionScope = null;
+
+
+    /**
+     * Creates a new instance of PortletTilesRequestContext.
+     *
+     * @param applicationContext The Tiles application context.
+     * @param context The portlet context to use.
+     * @param request The request object to use.
+     * @param response The response object to use.
+     * @param requestDelegate The request delegate.
+     * @param responseDelegate The response delegate.
+     */
+    public PortletRequest(ApplicationContext applicationContext,
+            PortletContext context, javax.portlet.PortletRequest request,
+            PortletResponse response, RequestDelegate requestDelegate, ResponseDelegate responseDelegate) {
+        super(applicationContext);
+
+        // Save the specified Portlet API object references
+        this.context = context;
+        this.request = request;
+        this.response = response;
+        this.requestDelegate = requestDelegate;
+        this.responseDelegate = responseDelegate;
+    }
+
+    /**
+     * <p>Return the {@link PortletRequest} for this context.</p>
+     *
+     * @return The used portlet request.
+     */
+    public javax.portlet.PortletRequest getRequest() {
+        return (this.request);
+    }
+
+    /**
+     * <p>Return the {@link PortletResponse} for this context.</p>
+     *
+     * @return The used portlet response.
+     */
+    public PortletResponse getResponse() {
+        return (this.response);
+    }
+
+    /**
+     * Returns the portlet context.
+     *
+     * @return The portlet context.
+     */
+    public PortletContext getPortletContext() {
+        return context;
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String> getHeader() {
+        if ((header == null) && (request != null)) {
+            header = new ReadOnlyEnumerationMap<String>(new HeaderExtractor(request, null));
+        }
+        return (header);
+    }
+
+    /** {@inheritDoc} */
+    public Addable<String> getResponseHeaders() {
+        if ((responseHeaders == null) && (request != null)) {
+            responseHeaders = new HeaderExtractor(null, response);
+        }
+        return (responseHeaders);
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String[]> getHeaderValues() {
+        if ((headerValues == null) && (request != null)) {
+            headerValues = new HeaderValuesMap(new HeaderExtractor(request, response));
+        }
+        return (headerValues);
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, Object> getRequestScope() {
+        if ((requestScope == null) && (request != null)) {
+            requestScope = new ScopeMap(new RequestScopeExtractor(request));
+        }
+        return (requestScope);
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, Object> getSessionScope() {
+        if ((sessionScope == null) && (request != null)) {
+            sessionScope = new ScopeMap(new SessionScopeExtractor(request,
+                    PortletSession.APPLICATION_SCOPE));
+        }
+        return (sessionScope);
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, Object> getPortletSessionScope() {
+        if ((portletSessionScope == null) && (request != null)) {
+            portletSessionScope = new ScopeMap(new SessionScopeExtractor(
+                    request, PortletSession.APPLICATION_SCOPE));
+        }
+        return (portletSessionScope);
+    }
+
+    @Override
+    public List<String> getAvailableScopes() {
+        return SCOPES;
+    }
+
+    /** {@inheritDoc} */
+    public Locale getRequestLocale() {
+        return request.getLocale();
+    }
+
+    @Override
+    public Map<String, String> getParam() {
+        return requestDelegate.getParam();
+    }
+
+    @Override
+    public Map<String, String[]> getParamValues() {
+        return requestDelegate.getParamValues();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isUserInRole(String role) {
+        return request.isUserInRole(role);
+    }
+
+    @Override
+    public OutputStream getOutputStream() throws IOException {
+        return responseDelegate.getOutputStream();
+    }
+
+    @Override
+    public PrintWriter getPrintWriter() throws IOException {
+        return responseDelegate.getPrintWriter();
+    }
+
+    @Override
+    public Writer getWriter() throws IOException {
+        return responseDelegate.getWriter();
+    }
+
+    @Override
+    public boolean isResponseCommitted() {
+        return responseDelegate.isResponseCommitted();
+    }
+
+    @Override
+    public void setContentType(String contentType) {
+        responseDelegate.setContentType(contentType);
+    }
+
+    /** {@inheritDoc} */
+    public void doForward(String path) throws IOException {
+        if (responseDelegate.isResponseCommitted()) {
+            doInclude(path);
+            return;
+        }
+
+        try {
+            PortletRequestDispatcher rd = getPortletContext()
+                    .getRequestDispatcher(path);
+
+            if (rd == null) {
+                throw new IOException(
+                        "No portlet request dispatcher returned for path '"
+                                + path + "'");
+            }
+
+            rd.forward(request, response);
+        } catch (PortletException e) {
+            throw new IOException("PortletException while including path '"
+                    + path + "'.", e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void doInclude(String path) throws IOException {
+        try {
+            PortletRequestDispatcher rd = getPortletContext()
+                    .getRequestDispatcher(path);
+
+            if (rd == null) {
+                throw new IOException(
+                        "No portlet request dispatcher returned for path '"
+                                + path + "'");
+            }
+
+            rd.include(request, response);
+        } catch (PortletException e) {
+            throw new IOException("PortletException while including path '"
+                    + path + "'.", e);
+        }
+    }
+
+    @Override
+    public Map<String, Object> getContext(String scope) {
+        if("request".equals(scope)){
+            return getRequestScope();
+        }else if("application".equals(scope)){
+            return getApplicationScope();
+        }else if("portletSession".equals(scope)){
+            return getPortletSessionScope();
+        }else if("application".equals(scope)){
+            return getApplicationScope();
+        }
+        throw new IllegalArgumentException(scope + " does not exist. Call getAvailableScopes() first to check.");
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/RenderPortletRequest.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/RenderPortletRequest.java
new file mode 100644
index 0000000..0eb5117
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/RenderPortletRequest.java
@@ -0,0 +1,53 @@
+/*
+ * $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.request.portlet;
+
+import javax.portlet.PortletContext;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.portlet.delegate.MimeResponseDelegate;
+import org.apache.tiles.request.portlet.delegate.PortletRequestDelegate;
+
+/**
+ * Portlet request for a {@link RenderRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RenderPortletRequest extends PortletRequest {
+
+    /**
+     * Constructor.
+     *
+     * @param applicationContext The application context.
+     * @param context The portlet context.
+     * @param request The portlet request.
+     * @param response The portlet response.
+     */
+    public RenderPortletRequest(ApplicationContext applicationContext,
+            PortletContext context, RenderRequest request,
+            RenderResponse response) {
+        super(applicationContext, context, request, response,
+                new PortletRequestDelegate(request), new MimeResponseDelegate(
+                        response));
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/ResourcePortletRequest.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/ResourcePortletRequest.java
new file mode 100644
index 0000000..7870287
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/ResourcePortletRequest.java
@@ -0,0 +1,53 @@
+/*
+ * $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.request.portlet;
+
+import javax.portlet.PortletContext;
+import javax.portlet.ResourceRequest;
+import javax.portlet.ResourceResponse;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.portlet.delegate.MimeResponseDelegate;
+import org.apache.tiles.request.portlet.delegate.PortletRequestDelegate;
+
+/**
+ * Portlet request for a {@link ResourceRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ResourcePortletRequest extends PortletRequest {
+
+    /**
+     * Constructor.
+     *
+     * @param applicationContext The application context.
+     * @param context The portlet context.
+     * @param request The portlet request.
+     * @param response The portlet response.
+     */
+    public ResourcePortletRequest(ApplicationContext applicationContext,
+            PortletContext context, ResourceRequest request,
+            ResourceResponse response) {
+        super(applicationContext, context, request, response,
+                new PortletRequestDelegate(request), new MimeResponseDelegate(
+                        response));
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/MimeResponseDelegate.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/MimeResponseDelegate.java
new file mode 100644
index 0000000..f9b6ac4
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/MimeResponseDelegate.java
@@ -0,0 +1,75 @@
+/*
+ * $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.request.portlet.delegate;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import javax.portlet.MimeResponse;
+
+/**
+ * Response delegate in case of {@link MimeResponse}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class MimeResponseDelegate implements ResponseDelegate {
+
+    /**
+     * The response.
+     */
+    private MimeResponse response;
+
+    /**
+     * Constructor.
+     *
+     * @param response The response.
+     */
+    public MimeResponseDelegate(MimeResponse response) {
+        this.response = response;
+    }
+
+    /** {@inheritDoc} */
+    public OutputStream getOutputStream() throws IOException {
+        return response.getPortletOutputStream();
+    }
+
+    /** {@inheritDoc} */
+    public PrintWriter getPrintWriter() throws IOException {
+        return response.getWriter();
+    }
+
+    /** {@inheritDoc} */
+    public Writer getWriter() throws IOException {
+        return response.getWriter();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isResponseCommitted() {
+        return response.isCommitted();
+    }
+
+    /** {@inheritDoc} */
+    public void setContentType(String contentType) {
+        response.setContentType(contentType);
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/PortletRequestDelegate.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/PortletRequestDelegate.java
new file mode 100644
index 0000000..cb7ff69
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/PortletRequestDelegate.java
@@ -0,0 +1,78 @@
+/*
+ * $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.request.portlet.delegate;
+
+import java.util.Map;
+
+import javax.portlet.PortletRequest;
+
+import org.apache.tiles.request.collection.ReadOnlyEnumerationMap;
+import org.apache.tiles.request.portlet.extractor.ParameterExtractor;
+
+/**
+ * Request delegate in case of simple Portlet request.
+ *
+ * @version $Rev$ $Date$
+ */
+public class PortletRequestDelegate implements RequestDelegate {
+
+    /**
+     * The request.
+     */
+    private PortletRequest request;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of request
+     * parameter name-value.</p>
+     */
+    private Map<String, String> param = null;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of request
+     * parameter name-values.</p>
+     */
+    private Map<String, String[]> paramValues = null;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request.
+     */
+    public PortletRequestDelegate(PortletRequest request) {
+        this.request = request;
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String> getParam() {
+        if ((param == null) && (request != null)) {
+            param = new ReadOnlyEnumerationMap<String>(new ParameterExtractor(request));
+        }
+        return (param);
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String[]> getParamValues() {
+        if ((paramValues == null) && (request != null)) {
+            paramValues = request.getParameterMap();
+        }
+        return (paramValues);
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/RequestDelegate.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/RequestDelegate.java
new file mode 100644
index 0000000..e7b9e97
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/RequestDelegate.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.request.portlet.delegate;
+
+import java.util.Map;
+
+/**
+ * Exposes the parameters of a portlet request, if available.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface RequestDelegate {
+
+    /**
+     * The parameters, as single values.
+     *
+     * @return The parameters.
+     */
+    Map<String, String> getParam();
+
+    /**
+     * The parameters, with values as array of strings.
+     *
+     * @return The parameters.
+     */
+    Map<String, String[]> getParamValues();
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/ResponseDelegate.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/ResponseDelegate.java
new file mode 100644
index 0000000..b7afb5a
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/ResponseDelegate.java
@@ -0,0 +1,72 @@
+/*
+ * $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.request.portlet.delegate;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * Exposes features of a response, if they are available.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface ResponseDelegate {
+
+    /**
+     * Returns the output stream.
+     *
+     * @return The output stream.
+     * @throws IOException If the underlying response causes a problem.
+     */
+    OutputStream getOutputStream() throws IOException;
+
+    /**
+     * Returns the print writer.
+     *
+     * @return The print writer.
+     * @throws IOException If the underlying response causes a problem.
+     */
+    PrintWriter getPrintWriter() throws IOException;
+
+    /**
+     * Returns the writer.
+     *
+     * @return The writer.
+     * @throws IOException If the underlying response causes a problem.
+     */
+    Writer getWriter() throws IOException;
+
+    /**
+     * Sets the content type of the response.
+     *
+     * @param contentType The content type.
+     */
+    void setContentType(String contentType);
+
+    /**
+     * Checks if the response is committed.
+     *
+     * @return <code>true</code> if the response is committed.
+     */
+    boolean isResponseCommitted();
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/StateAwareParameterMap.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/StateAwareParameterMap.java
new file mode 100644
index 0000000..1930815
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/StateAwareParameterMap.java
@@ -0,0 +1,115 @@
+/*
+ * $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.request.portlet.delegate;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Parameter map to be used when the response is a {@link javax.portlet.StateAwareResponse}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StateAwareParameterMap implements Map<String, String[]> {
+
+    /**
+     * The request parameter map.
+     */
+    private Map<String, String[]> requestMap;
+
+    /**
+     * The response parameter map.
+     */
+    private Map<String, String[]> responseMap;
+
+    /**
+     * Constructor.
+     *
+     * @param requestMap The request parameter map.
+     * @param responseMap The response parameter map.
+     */
+    public StateAwareParameterMap(Map<String, String[]> requestMap,
+            Map<String, String[]> responseMap) {
+        this.requestMap = requestMap;
+        this.responseMap = responseMap;
+    }
+
+    @Override
+    public void clear() {
+        responseMap.clear();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return requestMap.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return requestMap.containsValue(value);
+    }
+
+    @Override
+    public Set<java.util.Map.Entry<String, String[]>> entrySet() {
+        return requestMap.entrySet();
+    }
+
+    @Override
+    public String[] get(Object key) {
+        return requestMap.get(key);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return requestMap.isEmpty();
+    }
+
+    @Override
+    public Set<String> keySet() {
+        return requestMap.keySet();
+    }
+
+    @Override
+    public String[] put(String key, String[] value) {
+        return responseMap.put(key, value);
+    }
+
+    @Override
+    public void putAll(Map<? extends String, ? extends String[]> m) {
+        responseMap.putAll(m);
+    }
+
+    @Override
+    public String[] remove(Object key) {
+        return responseMap.remove(key);
+    }
+
+    @Override
+    public int size() {
+        return requestMap.size();
+    }
+
+    @Override
+    public Collection<String[]> values() {
+        return requestMap.values();
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/StateAwareRequestDelegate.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/StateAwareRequestDelegate.java
new file mode 100644
index 0000000..854c0cc
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/StateAwareRequestDelegate.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.request.portlet.delegate;
+
+import java.util.Map;
+
+import javax.portlet.PortletRequest;
+import javax.portlet.StateAwareResponse;
+
+import org.apache.tiles.request.collection.AddableParameterMap;
+import org.apache.tiles.request.portlet.extractor.StateAwareParameterExtractor;
+
+/**
+ * Exposes parameters getting them from a portlet reques and allowing to be put
+ * into a {@link StateAwareResponse}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StateAwareRequestDelegate implements RequestDelegate {
+
+    /**
+     * The request.
+     */
+    private PortletRequest request;
+
+    /**
+     * The response.
+     */
+    private StateAwareResponse response;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request.
+     * @param response The response.
+     */
+    public StateAwareRequestDelegate(PortletRequest request,
+            StateAwareResponse response) {
+        this.request = request;
+        this.response = response;
+    }
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of request
+     * parameter name-value.</p>
+     */
+    private Map<String, String> param = null;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of request
+     * parameter name-values.</p>
+     */
+    private Map<String, String[]> paramValues = null;
+
+    /** {@inheritDoc} */
+    public Map<String, String> getParam() {
+        if ((param == null) && (request != null)) {
+            param = new AddableParameterMap(new StateAwareParameterExtractor(
+                    request, response));
+        }
+        return (param);
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String[]> getParamValues() {
+        if ((paramValues == null) && (request != null)) {
+            paramValues = new StateAwareParameterMap(request.getParameterMap(),
+                    response.getRenderParameterMap());
+        }
+        return (paramValues);
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/StateAwareResponseDelegate.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/StateAwareResponseDelegate.java
new file mode 100644
index 0000000..0130b01
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/StateAwareResponseDelegate.java
@@ -0,0 +1,63 @@
+/*
+ * $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.request.portlet.delegate;
+
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * A state aware response does not allow to access to the output stream and similar,
+ * so it is, essentially, a feature blocker.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StateAwareResponseDelegate implements ResponseDelegate {
+
+    @Override
+    public OutputStream getOutputStream() {
+        throw new UnsupportedOperationException(
+                "No outputstream available for state-aware response");
+    }
+
+    @Override
+    public PrintWriter getPrintWriter() {
+        throw new UnsupportedOperationException(
+                "No outputstream available for state-aware response");
+    }
+
+    @Override
+    public Writer getWriter() {
+        throw new UnsupportedOperationException(
+                "No outputstream available for state-aware response");
+    }
+
+    @Override
+    public boolean isResponseCommitted() {
+        return false;
+    }
+
+    @Override
+    public void setContentType(String contentType) {
+        throw new UnsupportedOperationException(
+                "No outputstream available for state-aware response");
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/package-info.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/package-info.java
new file mode 100644
index 0000000..5080dc3
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/delegate/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Delegations to map all the different types of request and responses.
+ */
+package org.apache.tiles.request.portlet.delegate;
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/ApplicationScopeExtractor.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/ApplicationScopeExtractor.java
new file mode 100644
index 0000000..b167908
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/ApplicationScopeExtractor.java
@@ -0,0 +1,69 @@
+/*
+ * $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.request.portlet.extractor;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletContext;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+
+/**
+ * Extracts attributes from portlet application scope.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ApplicationScopeExtractor implements AttributeExtractor {
+
+    /**
+     * The portlet context.
+     */
+    private PortletContext context;
+
+    /**
+     * Constructor.
+     *
+     * @param context The portlet context.
+     */
+    public ApplicationScopeExtractor(PortletContext context) {
+        this.context = context;
+    }
+
+    @Override
+    public void setValue(String name, Object value) {
+        context.setAttribute(name, value);
+    }
+
+    @Override
+    public void removeValue(String name) {
+        context.removeAttribute(name);
+    }
+
+    @Override
+    public Enumeration<String> getKeys() {
+        return context.getAttributeNames();
+    }
+
+    @Override
+    public Object getValue(String key) {
+        return context.getAttribute(key);
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/HeaderExtractor.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/HeaderExtractor.java
new file mode 100644
index 0000000..52485d2
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/HeaderExtractor.java
@@ -0,0 +1,78 @@
+/*
+ * $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.request.portlet.extractor;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletResponse;
+
+import org.apache.tiles.request.attribute.EnumeratedValuesExtractor;
+
+/**
+ * Extracts and puts headers in portlet requests and responses.
+ *
+ * @version $Rev$ $Date$
+ */
+public class HeaderExtractor implements EnumeratedValuesExtractor {
+
+    /**
+     * The request.
+     */
+    private PortletRequest request;
+
+    /**
+     * The response.
+     */
+    private PortletResponse response;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request.
+     * @param response The response.
+     */
+    public HeaderExtractor(PortletRequest request,
+            PortletResponse response) {
+        this.request = request;
+        this.response = response;
+    }
+
+    @Override
+    public Enumeration<String> getKeys() {
+        return request.getPropertyNames();
+   }
+
+    @Override
+    public String getValue(String key) {
+        return request.getProperty(key);
+    }
+
+    @Override
+    public Enumeration<String> getValues(String key) {
+        return request.getProperties(key);
+    }
+
+    @Override
+    public void setValue(String key, String value) {
+        response.setProperty(key, value);
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/InitParameterExtractor.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/InitParameterExtractor.java
new file mode 100644
index 0000000..dca552b
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/InitParameterExtractor.java
@@ -0,0 +1,60 @@
+/*
+ * $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.request.portlet.extractor;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletContext;
+
+import org.apache.tiles.request.attribute.HasKeys;
+
+/**
+ * Extracts init parameters from a portlet context.
+ *
+ * @version $Rev$ $Date$
+ */
+public class InitParameterExtractor implements HasKeys<String> {
+
+    /**
+     * The portlet context.
+     */
+    private PortletContext context;
+
+    /**
+     * Constructor.
+     *
+     * @param context The portlet context.
+     */
+    public InitParameterExtractor(PortletContext context) {
+        this.context = context;
+    }
+
+    @Override
+    public Enumeration<String> getKeys() {
+        return context.getInitParameterNames();
+    }
+
+    @Override
+    public String getValue(String key) {
+        return context.getInitParameter(key);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/ParameterExtractor.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/ParameterExtractor.java
new file mode 100644
index 0000000..3a95931
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/ParameterExtractor.java
@@ -0,0 +1,59 @@
+/*
+ * $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.request.portlet.extractor;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletRequest;
+
+import org.apache.tiles.request.attribute.HasKeys;
+
+/**
+ * Extracts parameters from a portlet request.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ParameterExtractor implements HasKeys<String> {
+
+    /**
+     * The portlet request.
+     */
+    private PortletRequest request;
+
+    /**
+     * Constructor.
+     *
+     * @param request The portlet request.
+     */
+    public ParameterExtractor(PortletRequest request) {
+        this.request = request;
+    }
+
+    @Override
+    public Enumeration<String> getKeys() {
+        return request.getParameterNames();
+    }
+
+    @Override
+    public String getValue(String key) {
+        return request.getParameter(key);
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/RequestScopeExtractor.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/RequestScopeExtractor.java
new file mode 100644
index 0000000..7b74cfa
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/RequestScopeExtractor.java
@@ -0,0 +1,69 @@
+/*
+ * $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.request.portlet.extractor;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletRequest;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+
+/**
+ * Extracts attributes from request scope of a portlet request.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RequestScopeExtractor implements AttributeExtractor {
+
+    /**
+     * The portlet request.
+     */
+    private PortletRequest request;
+
+    /**
+     * Constructor.
+     *
+     * @param request The portlet request.
+     */
+    public RequestScopeExtractor(PortletRequest request) {
+        this.request = request;
+    }
+
+    @Override
+    public void setValue(String name, Object value) {
+        request.setAttribute(name, value);
+    }
+
+    @Override
+    public void removeValue(String name) {
+        request.removeAttribute(name);
+    }
+
+    @Override
+    public Enumeration<String> getKeys() {
+        return request.getAttributeNames();
+    }
+
+    @Override
+    public Object getValue(String key) {
+        return request.getAttribute(key);
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/SessionScopeExtractor.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/SessionScopeExtractor.java
new file mode 100644
index 0000000..5f32ef5
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/SessionScopeExtractor.java
@@ -0,0 +1,93 @@
+/*
+ * $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.request.portlet.extractor;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletSession;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+
+/**
+ * Extracts attributes from the session scope of a portlet request.
+ *
+ * @version $Rev$ $Date$
+ */
+public class SessionScopeExtractor implements AttributeExtractor {
+
+    /**
+     * The portlet request.
+     */
+    private PortletRequest request;
+
+    /**
+     * The subscope (application or portlet).
+     */
+    private int scope;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request.
+     * @param scope The subscope (application or portlet).
+     */
+    public SessionScopeExtractor(PortletRequest request, int scope) {
+        this.request = request;
+        if (scope != PortletSession.APPLICATION_SCOPE
+                && scope != PortletSession.PORTLET_SCOPE) {
+            throw new IllegalArgumentException(
+                    "The scope must be either APPLICATION_SCOPE or PORTLET_SCOPE");
+        }
+        this.scope = scope;
+    }
+
+    @Override
+    public void setValue(String name, Object value) {
+        request.getPortletSession().setAttribute(name, value, scope);
+    }
+
+    @Override
+    public void removeValue(String name) {
+        PortletSession session = request.getPortletSession(false);
+        if (session != null) {
+            session.removeAttribute(name, scope);
+        }
+    }
+
+    @Override
+    public Enumeration<String> getKeys() {
+        PortletSession session = request.getPortletSession(false);
+        if (session != null) {
+            return session.getAttributeNames(scope);
+        }
+        return null;
+    }
+
+    @Override
+    public Object getValue(String key) {
+        PortletSession session = request.getPortletSession(false);
+        if (session != null) {
+            return session.getAttribute(key, scope);
+        }
+        return null;
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/StateAwareParameterExtractor.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/StateAwareParameterExtractor.java
new file mode 100644
index 0000000..d1606c4
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/StateAwareParameterExtractor.java
@@ -0,0 +1,55 @@
+/*
+ * $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.request.portlet.extractor;
+
+import javax.portlet.PortletRequest;
+import javax.portlet.StateAwareResponse;
+
+import org.apache.tiles.request.attribute.HasAddableKeys;
+
+/**
+ * Extracts parameters from a request and allows putting render parameters in a state aware response.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StateAwareParameterExtractor extends ParameterExtractor implements HasAddableKeys<String> {
+
+    /**
+     * The portlet response.
+     */
+    private StateAwareResponse response;
+
+    /**
+     * Constructor.
+     *
+     * @param request The portlet request.
+     * @param response The portlet response.
+     */
+    public StateAwareParameterExtractor(PortletRequest request, StateAwareResponse response) {
+        super(request);
+        this.response = response;
+    }
+
+    @Override
+    public void setValue(String key, String value) {
+        response.setRenderParameter(key, value);
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/package-info.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/package-info.java
new file mode 100644
index 0000000..a37ccbb
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/extractor/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Extractors to get scopes from Portlet requests.
+ */
+package org.apache.tiles.request.portlet.extractor;
diff --git a/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/package-info.java b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/package-info.java
new file mode 100644
index 0000000..b9eb0d1
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/main/java/org/apache/tiles/request/portlet/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Support of Tiles requests to portlets.
+ */
+package org.apache.tiles.request.portlet;
diff --git a/tiles-request/tiles-request-portlet/src/site/site.xml b/tiles-request/tiles-request-portlet/src/site/site.xml
new file mode 100644
index 0000000..d52171a
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $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.
+ */
+-->
+<project name="Apache - Request Microframework">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Request Microframework"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/ActionPortletRequestTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/ActionPortletRequestTest.java
new file mode 100644
index 0000000..629f45f
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/ActionPortletRequestTest.java
@@ -0,0 +1,72 @@
+/*
+ * $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.request.portlet;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Field;
+
+import javax.portlet.ActionRequest;
+import javax.portlet.ActionResponse;
+import javax.portlet.PortletContext;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.portlet.delegate.StateAwareRequestDelegate;
+import org.apache.tiles.request.portlet.delegate.StateAwareResponseDelegate;
+import org.junit.Test;
+
+/**
+ * Tests {@link ActionPortletRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ActionPortletRequestTest {
+
+    /**
+     * Test method for
+     * {@link ActionPortletRequest#ActionPortletRequest(ApplicationContext,
+     * PortletContext, ActionRequest, ActionResponse)}.
+     * @throws NoSuchFieldException If something goes wrong.
+     * @throws SecurityException If something goes wrong.
+     * @throws IllegalAccessException If something goes wrong.
+     * @throws IllegalArgumentException If something goes wrong.
+     */
+    @Test
+    public void testActionPortletRequest() throws NoSuchFieldException,
+            IllegalAccessException    {
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        PortletContext portletContext = createMock(PortletContext.class);
+        ActionRequest request = createMock(ActionRequest.class);
+        ActionResponse response = createMock(ActionResponse.class);
+
+        replay(applicationContext, portletContext, request, response);
+        ActionPortletRequest req = new ActionPortletRequest(applicationContext,
+                portletContext, request, response);
+        Class<? extends ActionPortletRequest> clazz = req.getClass();
+        Field field = clazz.getSuperclass().getDeclaredField("requestDelegate");
+        assertTrue(field.get(req) instanceof StateAwareRequestDelegate);
+        field = clazz.getSuperclass().getDeclaredField("responseDelegate");
+        assertTrue(field.get(req) instanceof StateAwareResponseDelegate);
+        verify(applicationContext, portletContext, request, response);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/EventPortletRequestTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/EventPortletRequestTest.java
new file mode 100644
index 0000000..998cb02
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/EventPortletRequestTest.java
@@ -0,0 +1,70 @@
+/*
+ * $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.request.portlet;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Field;
+
+import javax.portlet.EventRequest;
+import javax.portlet.EventResponse;
+import javax.portlet.PortletContext;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.portlet.delegate.StateAwareRequestDelegate;
+import org.apache.tiles.request.portlet.delegate.StateAwareResponseDelegate;
+import org.junit.Test;
+
+/**
+ * Tests {@link EventPortletRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class EventPortletRequestTest {
+
+    /**
+     * Test method for
+     * {@link EventPortletRequest#EventPortletRequest(ApplicationContext, PortletContext, EventRequest, EventResponse)}.
+     * @throws NoSuchFieldException If something goes wrong.
+     * @throws SecurityException If something goes wrong.
+     * @throws IllegalAccessException If something goes wrong.
+     * @throws IllegalArgumentException If something goes wrong.
+     */
+    @Test
+    public void testEventPortletRequest() throws NoSuchFieldException, IllegalAccessException {
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        PortletContext portletContext = createMock(PortletContext.class);
+        EventRequest request = createMock(EventRequest.class);
+        EventResponse response = createMock(EventResponse.class);
+
+        replay(applicationContext, portletContext, request, response);
+        EventPortletRequest req = new EventPortletRequest(applicationContext,
+                portletContext, request, response);
+        Class<? extends EventPortletRequest> clazz = req.getClass();
+        Field field = clazz.getSuperclass().getDeclaredField("requestDelegate");
+        assertTrue(field.get(req) instanceof StateAwareRequestDelegate);
+        field = clazz.getSuperclass().getDeclaredField("responseDelegate");
+        assertTrue(field.get(req) instanceof StateAwareResponseDelegate);
+        verify(applicationContext, portletContext, request, response);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/PortletApplicationContextTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/PortletApplicationContextTest.java
new file mode 100644
index 0000000..0c0aad6
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/PortletApplicationContextTest.java
@@ -0,0 +1,151 @@
+/*
+ * $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.request.portlet;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Locale;
+
+import javax.portlet.PortletContext;
+
+import org.apache.tiles.request.ApplicationResource;
+import org.apache.tiles.request.collection.ReadOnlyEnumerationMap;
+import org.apache.tiles.request.collection.ScopeMap;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link PortletApplicationContext}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class PortletApplicationContextTest {
+
+    /**
+     * The portlet context.
+     */
+    private PortletContext portletContext;
+
+    /**
+     * The application context.
+     */
+    private PortletApplicationContext context;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        portletContext = createMock(PortletContext.class);
+        context = new PortletApplicationContext(portletContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletApplicationContext#getContext()}.
+     */
+    @Test
+    public void testGetContext() {
+        replay(portletContext);
+        assertEquals(portletContext, context.getContext());
+        verify(portletContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletApplicationContext#getPortletContext()}.
+     */
+    @Test
+    public void testGetPortletContext() {
+        replay(portletContext);
+        assertEquals(portletContext, context.getPortletContext());
+        verify(portletContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletApplicationContext#getApplicationScope()}.
+     */
+    @Test
+    public void testGetApplicationScope() {
+        replay(portletContext);
+        assertTrue(context.getApplicationScope() instanceof ScopeMap);
+        verify(portletContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletApplicationContext#getInitParams()}.
+     */
+    @Test
+    public void testGetInitParams() {
+        replay(portletContext);
+        assertTrue(context.getInitParams() instanceof ReadOnlyEnumerationMap);
+        verify(portletContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletApplicationContext#getResource(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetResource() throws IOException {
+        URL url = new URL("file:///portletContext/my/path.html");
+        url = new URL(url.toExternalForm()); // normalize it
+        URL urlFr = new URL("file:///portletContext/my/path_fr.html");
+        urlFr = new URL(urlFr.toExternalForm()); // normalize it
+        expect(portletContext.getResource("/my/path.html")).andReturn(url);
+        expect(portletContext.getResource("/my/path_fr.html")).andReturn(urlFr);
+        expect(portletContext.getResource("/null/path.html")).andReturn(null);
+
+        replay(portletContext);
+        ApplicationResource resource = context.getResource("/my/path.html");
+        assertNotNull(resource);
+        assertEquals(resource.getLocalePath(), "/my/path.html");
+        assertEquals(resource.getPath(), "/my/path.html");
+        assertEquals(Locale.ROOT, resource.getLocale());
+        ApplicationResource resourceFr = context.getResource(resource, Locale.FRENCH);
+        assertNotNull(resourceFr);
+        assertEquals("/my/path_fr.html", resourceFr.getLocalePath());
+        assertEquals("/my/path.html", resourceFr.getPath());
+        assertEquals(Locale.FRENCH, resourceFr.getLocale());
+        ApplicationResource nullResource = context.getResource("/null/path.html");
+        assertNull(nullResource);
+        verify(portletContext);
+    }
+
+    /**
+     * Test method for {@link PortletApplicationContext#getResources(String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetResources() throws IOException {
+        URL url = new URL("file:///portletContext/my/path.html");
+        expect(portletContext.getResource("/my/path")).andReturn(url);
+
+        replay(portletContext);
+        Collection<ApplicationResource> resources = context.getResources("/my/path");
+        assertEquals(1, resources.size());
+        assertEquals(resources.iterator().next().getLocalePath(), "/my/path");
+        verify(portletContext);
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/PortletRequestTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/PortletRequestTest.java
new file mode 100644
index 0000000..f9d2581
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/PortletRequestTest.java
@@ -0,0 +1,430 @@
+/*
+ * $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.request.portlet;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.portlet.PortletContext;
+import javax.portlet.PortletException;
+import javax.portlet.PortletRequestDispatcher;
+import javax.portlet.PortletResponse;
+import javax.servlet.ServletOutputStream;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.collection.HeaderValuesMap;
+import org.apache.tiles.request.collection.ReadOnlyEnumerationMap;
+import org.apache.tiles.request.collection.ScopeMap;
+import org.apache.tiles.request.portlet.delegate.RequestDelegate;
+import org.apache.tiles.request.portlet.delegate.ResponseDelegate;
+import org.apache.tiles.request.portlet.extractor.HeaderExtractor;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link PortletRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class PortletRequestTest {
+
+    /**
+     * The application context.
+     */
+    private ApplicationContext applicationContext;
+
+    /**
+     * The portlet context.
+     */
+    private PortletContext portletContext;
+
+    /**
+     * The request.
+     */
+    private javax.portlet.PortletRequest request;
+
+    /**
+     * The response.
+     */
+    private PortletResponse response;
+
+    /**
+     * The request to test.
+     */
+    private PortletRequest req;
+
+    /**
+     * The request delegate.
+     */
+    private RequestDelegate requestDelegate;
+
+    /**
+     * The response delegate.
+     */
+    private ResponseDelegate responseDelegate;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        applicationContext = createMock(ApplicationContext.class);
+        portletContext = createMock(PortletContext.class);
+        request = createMock(javax.portlet.PortletRequest.class);
+        response = createMock(PortletResponse.class);
+        requestDelegate = createMock(RequestDelegate.class);
+        responseDelegate = createMock(ResponseDelegate.class);
+        req = new PortletRequest(applicationContext, portletContext, request,
+                response, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#doForward(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws PortletException If something goes wrong.
+     */
+    @Test
+    public void testDoForward() throws PortletException, IOException {
+        PortletRequestDispatcher rd = createMock(PortletRequestDispatcher.class);
+
+        expect(responseDelegate.isResponseCommitted()).andReturn(false);
+        expect(portletContext.getRequestDispatcher("/my/path")).andReturn(rd);
+        rd.forward(request, response);
+
+        replay(applicationContext, portletContext, request, response, rd);
+        req.doForward("/my/path");
+        verify(applicationContext, portletContext, request, response, rd);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#doForward(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testDoForwardNoDispatcher() throws IOException {
+        expect(responseDelegate.isResponseCommitted()).andReturn(false);
+        expect(portletContext.getRequestDispatcher("/my/path")).andReturn(null);
+
+        replay(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+        try {
+            req.doForward("/my/path");
+        } finally {
+            verify(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+        }
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#doForward(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws PortletException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testDoForwardPortletException() throws PortletException, IOException {
+        PortletRequestDispatcher rd = createMock(PortletRequestDispatcher.class);
+
+        expect(responseDelegate.isResponseCommitted()).andReturn(false);
+        expect(portletContext.getRequestDispatcher("/my/path")).andReturn(rd);
+        rd.forward(request, response);
+        expectLastCall().andThrow(new PortletException());
+
+        replay(applicationContext, request, response, rd, portletContext, requestDelegate, responseDelegate);
+        try {
+            req.doForward("/my/path");
+        } finally {
+            verify(applicationContext, request, response, rd, portletContext, requestDelegate, responseDelegate);
+        }
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#doForward(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws PortletException If something goes wrong.
+     */
+    @Test
+    public void testDoForwardInclude() throws PortletException, IOException {
+        PortletRequestDispatcher rd = createMock(PortletRequestDispatcher.class);
+
+        expect(responseDelegate.isResponseCommitted()).andReturn(true);
+        expect(portletContext.getRequestDispatcher("/my/path")).andReturn(rd);
+        rd.include(request, response);
+
+        replay(applicationContext, request, response, rd, portletContext, requestDelegate, responseDelegate);
+        req.doForward("/my/path");
+        verify(applicationContext, request, response, rd, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#doInclude(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws PortletException If something goes wrong.
+     */
+    @Test
+    public void testDoInclude() throws IOException, PortletException {
+        PortletRequestDispatcher rd = createMock(PortletRequestDispatcher.class);
+
+        expect(portletContext.getRequestDispatcher("/my/path")).andReturn(rd);
+        rd.include(request, response);
+
+        replay(applicationContext, request, response, rd, portletContext, requestDelegate, responseDelegate);
+        req.doInclude("/my/path");
+        verify(applicationContext, request, response, rd, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#doInclude(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testDoIncludeNoDispatcher() throws IOException {
+        expect(portletContext.getRequestDispatcher("/my/path")).andReturn(null);
+
+        replay(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+        try {
+            req.doInclude("/my/path");
+        } finally {
+            verify(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+        }
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#doInclude(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws PortletException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testDoIncludePortletException() throws IOException, PortletException {
+        PortletRequestDispatcher rd = createMock(PortletRequestDispatcher.class);
+
+        expect(portletContext.getRequestDispatcher("/my/path")).andReturn(rd);
+        rd.include(request, response);
+        expectLastCall().andThrow(new PortletException());
+
+        replay(applicationContext, request, response, rd, portletContext, requestDelegate, responseDelegate);
+        try {
+            req.doInclude("/my/path");
+        } finally {
+            verify(applicationContext, request, response, rd, portletContext, requestDelegate, responseDelegate);
+        }
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getHeader()}.
+     */
+    @Test
+    public void testGetHeader() {
+        assertTrue(req.getHeader() instanceof ReadOnlyEnumerationMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getResponseHeaders()}.
+     */
+    @Test
+    public void testGetResponseHeaders() {
+        assertTrue(req.getResponseHeaders() instanceof HeaderExtractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getHeaderValues()}.
+     */
+    @Test
+    public void testGetHeaderValues() {
+        assertTrue(req.getHeaderValues() instanceof HeaderValuesMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getParam()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetParam() {
+        Map<String, String> map = createMock(Map.class);
+
+        expect(requestDelegate.getParam()).andReturn(map);
+
+        replay(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+        assertEquals(map, req.getParam());
+        verify(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getParamValues()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetParamValues() {
+        Map<String, String[]> paramMap = createMock(Map.class);
+
+        expect(requestDelegate.getParamValues()).andReturn(paramMap);
+
+        replay(applicationContext, request, response, paramMap, portletContext, requestDelegate, responseDelegate);
+        assertEquals(paramMap, req.getParamValues());
+        verify(applicationContext, request, response, paramMap, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getRequestScope()}.
+     */
+    @Test
+    public void testGetRequestScope() {
+        assertTrue(req.getRequestScope() instanceof ScopeMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getSessionScope()}.
+     */
+    @Test
+    public void testGetSessionScope() {
+        assertTrue(req.getSessionScope() instanceof ScopeMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getPortletSessionScope()}.
+     */
+    @Test
+    public void testGetPortletSessionScope() {
+        assertTrue(req.getPortletSessionScope() instanceof ScopeMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getOutputStream()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetOutputStream() throws IOException {
+        ServletOutputStream os = createMock(ServletOutputStream.class);
+
+        expect(responseDelegate.getOutputStream()).andReturn(os);
+
+        replay(applicationContext, request, response, os, portletContext, requestDelegate, responseDelegate);
+        assertEquals(req.getOutputStream(), os);
+        verify(applicationContext, request, response, os, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getWriter()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetWriter() throws IOException {
+        PrintWriter os = createMock(PrintWriter.class);
+
+        expect(responseDelegate.getWriter()).andReturn(os);
+
+        replay(applicationContext, request, response, os, portletContext, requestDelegate, responseDelegate);
+        assertEquals(req.getWriter(), os);
+        verify(applicationContext, request, response, os, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getPrintWriter()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetPrintWriter() throws IOException {
+        PrintWriter os = createMock(PrintWriter.class);
+
+        expect(responseDelegate.getPrintWriter()).andReturn(os);
+
+        replay(applicationContext, request, response, os, portletContext, requestDelegate, responseDelegate);
+        assertEquals(req.getPrintWriter(), os);
+        verify(applicationContext, request, response, os, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#isResponseCommitted()}.
+     */
+    @Test
+    public void testIsResponseCommitted() {
+        expect(responseDelegate.isResponseCommitted()).andReturn(true);
+
+        replay(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+        assertTrue(req.isResponseCommitted());
+        verify(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#setContentType(java.lang.String)}.
+     */
+    @Test
+    public void testSetContentType() {
+        responseDelegate.setContentType("text/html");
+
+        replay(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+        req.setContentType("text/html");
+        verify(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getRequestLocale()}.
+     */
+    @Test
+    public void testGetRequestLocale() {
+        Locale locale = Locale.ITALY;
+
+        expect(request.getLocale()).andReturn(locale);
+
+        replay(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+        assertEquals(locale, req.getRequestLocale());
+        verify(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getRequestObjects()}.
+     */
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getRequest()}.
+     */
+    @Test
+    public void testGetRequest() {
+        replay(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+        assertEquals(request, req.getRequest());
+        verify(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#getResponse()}.
+     */
+    @Test
+    public void testGetResponse() {
+        replay(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+        assertEquals(response, req.getResponse());
+        verify(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.PortletRequest#isUserInRole(java.lang.String)}.
+     */
+    @Test
+    public void testIsUserInRole() {
+        expect(request.isUserInRole("myrole")).andReturn(true);
+
+        replay(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+        assertTrue(req.isUserInRole("myrole"));
+        verify(applicationContext, request, response, portletContext, requestDelegate, responseDelegate);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/RenderPortletRequestTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/RenderPortletRequestTest.java
new file mode 100644
index 0000000..b88983f
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/RenderPortletRequestTest.java
@@ -0,0 +1,71 @@
+/*
+ * $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.request.portlet;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Field;
+
+import javax.portlet.PortletContext;
+import javax.portlet.RenderRequest;
+import javax.portlet.RenderResponse;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.portlet.delegate.MimeResponseDelegate;
+import org.apache.tiles.request.portlet.delegate.PortletRequestDelegate;
+import org.junit.Test;
+
+/**
+ * Tests {@link RenderPortletRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RenderPortletRequestTest {
+
+    /**
+     * Test method for
+     * {@link RenderPortletRequest#RenderPortletRequest(ApplicationContext, PortletContext,
+     * RenderRequest, RenderResponse)}.
+     * @throws NoSuchFieldException If something goes wrong.
+     * @throws SecurityException If something goes wrong.
+     * @throws IllegalAccessException If something goes wrong.
+     * @throws IllegalArgumentException If something goes wrong.
+     */
+    @Test
+    public void testRenderPortletRequest() throws NoSuchFieldException, IllegalAccessException {
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        PortletContext portletContext = createMock(PortletContext.class);
+        RenderRequest request = createMock(RenderRequest.class);
+        RenderResponse response = createMock(RenderResponse.class);
+
+        replay(applicationContext, portletContext, request, response);
+        RenderPortletRequest req = new RenderPortletRequest(applicationContext,
+                portletContext, request, response);
+        Class<? extends RenderPortletRequest> clazz = req.getClass();
+        Field field = clazz.getSuperclass().getDeclaredField("requestDelegate");
+        assertTrue(field.get(req) instanceof PortletRequestDelegate);
+        field = clazz.getSuperclass().getDeclaredField("responseDelegate");
+        assertTrue(field.get(req) instanceof MimeResponseDelegate);
+        verify(applicationContext, portletContext, request, response);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/ResourcePortletRequestTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/ResourcePortletRequestTest.java
new file mode 100644
index 0000000..130cb5d
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/ResourcePortletRequestTest.java
@@ -0,0 +1,71 @@
+/*
+ * $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.request.portlet;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.lang.reflect.Field;
+
+import javax.portlet.PortletContext;
+import javax.portlet.ResourceRequest;
+import javax.portlet.ResourceResponse;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.portlet.delegate.MimeResponseDelegate;
+import org.apache.tiles.request.portlet.delegate.PortletRequestDelegate;
+import org.junit.Test;
+
+/**
+ * Tests {@link ResourcePortletRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ResourcePortletRequestTest {
+
+    /**
+     * Test method for
+     * {@link ResourcePortletRequest#ResourcePortletRequest(ApplicationContext, PortletContext,
+     * ResourceRequest, ResourceResponse)}.
+     * @throws NoSuchFieldException If something goes wrong.
+     * @throws SecurityException If something goes wrong.
+     * @throws IllegalAccessException If something goes wrong.
+     * @throws IllegalArgumentException If something goes wrong.
+     */
+    @Test
+    public void testResourcePortletRequest() throws NoSuchFieldException, IllegalAccessException {
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+        PortletContext portletContext = createMock(PortletContext.class);
+        ResourceRequest request = createMock(ResourceRequest.class);
+        ResourceResponse response = createMock(ResourceResponse.class);
+
+        replay(applicationContext, portletContext, request, response);
+        ResourcePortletRequest req = new ResourcePortletRequest(applicationContext,
+                portletContext, request, response);
+        Class<? extends ResourcePortletRequest> clazz = req.getClass();
+        Field field = clazz.getSuperclass().getDeclaredField("requestDelegate");
+        assertTrue(field.get(req) instanceof PortletRequestDelegate);
+        field = clazz.getSuperclass().getDeclaredField("responseDelegate");
+        assertTrue(field.get(req) instanceof MimeResponseDelegate);
+        verify(applicationContext, portletContext, request, response);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/MimeResponseDelegateTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/MimeResponseDelegateTest.java
new file mode 100644
index 0000000..f16525f
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/MimeResponseDelegateTest.java
@@ -0,0 +1,130 @@
+/*
+ * $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.request.portlet.delegate;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+import javax.portlet.MimeResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link MimeResponseDelegate}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class MimeResponseDelegateTest {
+
+    /**
+     * The response.
+     */
+    private MimeResponse response;
+
+    /**
+     * The delegate to test.
+     */
+    private MimeResponseDelegate delegate;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        response = createMock(MimeResponse.class);
+        delegate = new MimeResponseDelegate(response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.MimeResponseDelegate#getOutputStream()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetOutputStream() throws IOException {
+        OutputStream os = createMock(OutputStream.class);
+
+        expect(response.getPortletOutputStream()).andReturn(os);
+
+        replay(response, os);
+        assertEquals(os, delegate.getOutputStream());
+        verify(response, os);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.MimeResponseDelegate#getPrintWriter()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetPrintWriter() throws IOException {
+        PrintWriter os = createMock(PrintWriter.class);
+
+        expect(response.getWriter()).andReturn(os);
+
+        replay(response, os);
+        assertEquals(os, delegate.getPrintWriter());
+        verify(response, os);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.MimeResponseDelegate#getWriter()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetWriter() throws IOException {
+        PrintWriter os = createMock(PrintWriter.class);
+
+        expect(response.getWriter()).andReturn(os);
+
+        replay(response, os);
+        assertEquals(os, delegate.getWriter());
+        verify(response, os);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.MimeResponseDelegate#isResponseCommitted()}.
+     */
+    @Test
+    public void testIsResponseCommitted() {
+        expect(response.isCommitted()).andReturn(true);
+
+        replay(response);
+        assertTrue(delegate.isResponseCommitted());
+        verify(response);
+    }
+
+    /**
+     * Test method for {@link MimeResponseDelegate#setContentType(String)}.
+     */
+    @Test
+    public void testSetContentType() {
+        response.setContentType("text/html");
+
+        replay(response);
+        delegate.setContentType("text/html");
+        verify(response);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/PortletRequestDelegateTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/PortletRequestDelegateTest.java
new file mode 100644
index 0000000..14da05c
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/PortletRequestDelegateTest.java
@@ -0,0 +1,85 @@
+/*
+ * $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.request.portlet.delegate;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Map;
+
+import javax.portlet.PortletRequest;
+
+import org.apache.tiles.request.collection.ReadOnlyEnumerationMap;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link PortletRequestDelegate}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class PortletRequestDelegateTest {
+
+    /**
+     * The request.
+     */
+    private PortletRequest request;
+
+    /**
+     * The delegate to test.
+     */
+    private PortletRequestDelegate delegate;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(PortletRequest.class);
+        delegate = new PortletRequestDelegate(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.PortletRequestDelegate#getParam()}.
+     */
+    @Test
+    public void testGetParam() {
+        replay(request);
+        assertTrue(delegate.getParam() instanceof ReadOnlyEnumerationMap);
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.PortletRequestDelegate#getParamValues()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetParamValues() {
+        Map<String, String[]> params = createMock(Map.class);
+
+        expect(request.getParameterMap()).andReturn(params);
+
+        replay(request, params);
+        assertEquals(params, delegate.getParamValues());
+        verify(request, params);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/StateAwareParameterMapTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/StateAwareParameterMapTest.java
new file mode 100644
index 0000000..2439b44
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/StateAwareParameterMapTest.java
@@ -0,0 +1,226 @@
+/*
+ * $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.request.portlet.delegate;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link StateAwareParameterMap}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StateAwareParameterMapTest {
+
+    /**
+     * The request map.
+     */
+    private Map<String, String[]> requestMap;
+
+    /**
+     * The response map.
+     */
+    private Map<String, String[]> responseMap;
+
+    /**
+     * The map to test.
+     */
+    private StateAwareParameterMap map;
+
+    /**
+     * Sets up the test.
+     */
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        requestMap = createMock(Map.class);
+        responseMap = createMock(Map.class);
+        map = new StateAwareParameterMap(requestMap, responseMap);
+
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareParameterMap#clear()}.
+     */
+    @Test
+    public void testClear() {
+        responseMap.clear();
+
+        replay(requestMap, responseMap);
+        map.clear();
+        verify(requestMap, responseMap);
+    }
+
+    /**
+     * Test method for {@link StateAwareParameterMap#containsKey(Object)}.
+     */
+    @Test
+    public void testContainsKey() {
+        expect(requestMap.containsKey("key")).andReturn(true);
+
+        replay(requestMap, responseMap);
+        assertTrue(map.containsKey("key"));
+        verify(requestMap, responseMap);
+    }
+
+    /**
+     * Test method for {@link StateAwareParameterMap#containsValue(Object)}.
+     */
+    @Test
+    public void testContainsValue() {
+        String[] values = new String[] {"value1", "value2"};
+        expect(requestMap.containsValue(values)).andReturn(true);
+
+        replay(requestMap, responseMap);
+        assertTrue(map.containsValue(values));
+        verify(requestMap, responseMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareParameterMap#entrySet()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testEntrySet() {
+        Set<Map.Entry<String, String[]>> entrySet = createMock(Set.class);
+
+        expect(requestMap.entrySet()).andReturn(entrySet);
+
+        replay(requestMap, responseMap, entrySet);
+        assertEquals(entrySet, map.entrySet());
+        verify(requestMap, responseMap, entrySet);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareParameterMap#get(java.lang.Object)}.
+     */
+    @Test
+    public void testGet() {
+        String[] values = new String[] {"value1", "value2"};
+        expect(requestMap.get("key")).andReturn(values);
+
+        replay(requestMap, responseMap);
+        assertArrayEquals(values, map.get("key"));
+        verify(requestMap, responseMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareParameterMap#isEmpty()}.
+     */
+    @Test
+    public void testIsEmpty() {
+        expect(requestMap.isEmpty()).andReturn(false);
+
+        replay(requestMap, responseMap);
+        assertFalse(map.isEmpty());
+        verify(requestMap, responseMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareParameterMap#keySet()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testKeySet() {
+        Set<String> keySet = createMock(Set.class);
+
+        expect(requestMap.keySet()).andReturn(keySet);
+
+        replay(requestMap, responseMap, keySet);
+        assertEquals(keySet, map.keySet());
+        verify(requestMap, responseMap, keySet);
+    }
+
+    /**
+     * Test method for {@link StateAwareParameterMap#put(String, String[])}.
+     */
+    @Test
+    public void testPut() {
+        String[] values = new String[] {"value1", "value2"};
+        expect(responseMap.put(eq("key"), aryEq(values))).andReturn(null);
+
+        replay(requestMap, responseMap);
+        assertNull(map.put("key", values));
+        verify(requestMap, responseMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareParameterMap#putAll(java.util.Map)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testPutAll() {
+        Map<String, String[]> entries = createMock(Map.class);
+        responseMap.putAll(entries);
+
+        replay(requestMap, responseMap, entries);
+        map.putAll(entries);
+        verify(requestMap, responseMap, entries);
+    }
+
+    /**
+     * Test method for {@link StateAwareParameterMap#remove(Object)}.
+     */
+    @Test
+    public void testRemove() {
+        String[] values = new String[] {"value1", "value2"};
+        expect(responseMap.remove("key")).andReturn(values);
+
+        replay(requestMap, responseMap);
+        assertArrayEquals(values, map.remove("key"));
+        verify(requestMap, responseMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareParameterMap#size()}.
+     */
+    @Test
+    public void testSize() {
+        expect(requestMap.size()).andReturn(1);
+
+        replay(requestMap, responseMap);
+        assertEquals(1, map.size());
+        verify(requestMap, responseMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareParameterMap#values()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testValues() {
+        Collection<String[]> values = createMock(Collection.class);
+
+        expect(requestMap.values()).andReturn(values);
+
+        replay(requestMap, responseMap, values);
+        assertEquals(values, map.values());
+        verify(requestMap, responseMap, values);
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/StateAwareRequestDelegateTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/StateAwareRequestDelegateTest.java
new file mode 100644
index 0000000..a9ba958
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/StateAwareRequestDelegateTest.java
@@ -0,0 +1,94 @@
+/*
+ * $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.request.portlet.delegate;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Map;
+
+import javax.portlet.PortletRequest;
+import javax.portlet.StateAwareResponse;
+
+import org.apache.tiles.request.collection.AddableParameterMap;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link StateAwareRequestDelegate}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StateAwareRequestDelegateTest {
+
+    /**
+     * The request.
+     */
+    private PortletRequest request;
+
+    /**
+     * The response.
+     */
+    private StateAwareResponse response;
+
+    /**
+     * The delegate to test.
+     */
+    private StateAwareRequestDelegate delegate;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(PortletRequest.class);
+        response = createMock(StateAwareResponse.class);
+        delegate = new StateAwareRequestDelegate(request, response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareRequestDelegate#getParam()}.
+     */
+    @Test
+    public void testGetParam() {
+        replay(request);
+        assertTrue(delegate.getParam() instanceof AddableParameterMap);
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareRequestDelegate#getParamValues()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetParamValues() {
+        Map<String, String[]> requestMap = createMock(Map.class);
+        Map<String, String[]> responseMap = createMock(Map.class);
+
+        expect(request.getParameterMap()).andReturn(requestMap);
+        expect(response.getRenderParameterMap()).andReturn(responseMap);
+
+        replay(request);
+        assertTrue(delegate.getParamValues() instanceof StateAwareParameterMap);
+        verify(request);
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/StateAwareResponseDelegateTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/StateAwareResponseDelegateTest.java
new file mode 100644
index 0000000..d8a6b0d
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/delegate/StateAwareResponseDelegateTest.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.request.portlet.delegate;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link StateAwareResponseDelegate}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StateAwareResponseDelegateTest {
+
+    /**
+     * The delegate to test.
+     */
+    private StateAwareResponseDelegate delegate;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        delegate = new StateAwareResponseDelegate();
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareResponseDelegate#getOutputStream()}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetOutputStream() {
+        delegate.getOutputStream();
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareResponseDelegate#getPrintWriter()}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetPrintWriter() {
+        delegate.getPrintWriter();
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.delegate.StateAwareResponseDelegate#getWriter()}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testGetWriter() {
+        delegate.getWriter();
+    }
+
+    /**
+     * Test method for {@link StateAwareResponseDelegate#isResponseCommitted()}.
+     */
+    @Test
+    public void testIsResponseCommitted() {
+        assertFalse(delegate.isResponseCommitted());
+    }
+
+    /**
+     * Test method for {@link StateAwareResponseDelegate#setContentType(java.lang.String)}.
+     */
+    @Test(expected = UnsupportedOperationException.class)
+    public void testSetContentType() {
+        delegate.setContentType("text/html");
+    }
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/ApplicationScopeExtractorTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/ApplicationScopeExtractorTest.java
new file mode 100644
index 0000000..2378b45
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/ApplicationScopeExtractorTest.java
@@ -0,0 +1,110 @@
+/*
+ * $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.request.portlet.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletContext;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ApplicationScopeExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ApplicationScopeExtractorTest {
+
+    /**
+     * The portlet context.
+     */
+    private PortletContext context;
+
+    /**
+     * The extractot to test.
+     */
+    private ApplicationScopeExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        context = createMock(PortletContext.class);
+        extractor = new ApplicationScopeExtractor(context);
+    }
+
+    /**
+     * Test method for {@link ApplicationScopeExtractor#setValue(java.lang.String, java.lang.Object)}.
+     */
+    @Test
+    public void testSetValue() {
+        context.setAttribute("attribute", "value");
+
+        replay(context);
+        extractor.setValue("attribute", "value");
+        verify(context);
+    }
+
+    /**
+     * Test method for {@link ApplicationScopeExtractor#removeValue(java.lang.String)}.
+     */
+    @Test
+    public void testRemoveValue() {
+        context.removeAttribute("attribute");
+
+        replay(context);
+        extractor.removeValue("attribute");
+        verify(context);
+    }
+
+    /**
+     * Test method for {@link ApplicationScopeExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        expect(context.getAttributeNames()).andReturn(keys);
+
+        replay(context, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(context, keys);
+    }
+
+    /**
+     * Test method for {@link ApplicationScopeExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(context.getAttribute("attribute")).andReturn("value");
+
+        replay(context);
+        assertEquals("value", extractor.getValue("attribute"));
+        verify(context);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/HeaderExtractorTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/HeaderExtractorTest.java
new file mode 100644
index 0000000..3d81ce5
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/HeaderExtractorTest.java
@@ -0,0 +1,121 @@
+/*
+ * $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.request.portlet.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link HeaderExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class HeaderExtractorTest {
+
+    /**
+     * The request.
+     */
+    private PortletRequest request;
+
+    /**
+     * The response.
+     */
+    private PortletResponse response;
+
+    /**
+     * The extractor to test.
+     */
+    private HeaderExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(PortletRequest.class);
+        response = createMock(PortletResponse.class);
+        extractor = new HeaderExtractor(request, response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.extractor.HeaderExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(request.getPropertyNames()).andReturn(keys);
+
+        replay(request, response, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(request, response, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.extractor.HeaderExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(request.getProperty("name")).andReturn("value");
+
+        replay(request, response);
+        assertEquals("value", extractor.getValue("name"));
+        verify(request, response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.extractor.HeaderExtractor#getValues(java.lang.String)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetValues() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(request.getProperties("name")).andReturn(keys);
+
+        replay(request, response, keys);
+        assertEquals(keys, extractor.getValues("name"));
+        verify(request, response, keys);
+    }
+
+    /**
+     * Test method for {@link HeaderExtractor#setValue(String, String)}.
+     */
+    @Test
+    public void testSetValue() {
+        response.setProperty("name", "value");
+
+        replay(request, response);
+        extractor.setValue("name", "value");
+        verify(request, response);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/InitParameterExtractorTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/InitParameterExtractorTest.java
new file mode 100644
index 0000000..4480080
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/InitParameterExtractorTest.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.request.portlet.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletContext;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link InitParameterExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class InitParameterExtractorTest {
+
+    /**
+     * The portlet context.
+     */
+    private PortletContext context;
+
+    /**
+     * The extractor to test.
+     */
+    private InitParameterExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        context = createMock(PortletContext.class);
+        extractor = new InitParameterExtractor(context);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.extractor.InitParameterExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(context.getInitParameterNames()).andReturn(keys);
+
+        replay(context, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(context, keys);
+    }
+
+    /**
+     * Test method for {@link InitParameterExtractor#getValue(String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(context.getInitParameter("name")).andReturn("value");
+
+        replay(context);
+        assertEquals("value", extractor.getValue("name"));
+        verify(context);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/ParameterExtractorTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/ParameterExtractorTest.java
new file mode 100644
index 0000000..dcb9bac
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/ParameterExtractorTest.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.request.portlet.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletRequest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ParameterExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ParameterExtractorTest {
+
+    /**
+     * The request.
+     */
+    private PortletRequest request;
+
+    /**
+     * The extractor to test.
+     */
+    private ParameterExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(PortletRequest.class);
+        extractor = new ParameterExtractor(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.extractor.ParameterExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(request.getParameterNames()).andReturn(keys);
+
+        replay(request, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(request, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.extractor.ParameterExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(request.getParameter("name")).andReturn("value");
+
+        replay(request);
+        assertEquals("value", extractor.getValue("name"));
+        verify(request);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/RequestScopeExtractorTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/RequestScopeExtractorTest.java
new file mode 100644
index 0000000..d922acd
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/RequestScopeExtractorTest.java
@@ -0,0 +1,111 @@
+/*
+ * $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.request.portlet.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletRequest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link RequestScopeExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RequestScopeExtractorTest {
+
+    /**
+     * The request to test.
+     */
+    private PortletRequest request;
+
+    /**
+     * The extractor to test.
+     */
+    private RequestScopeExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(PortletRequest.class);
+        extractor = new RequestScopeExtractor(request);
+    }
+
+    /**
+     * Test method for {@link RequestScopeExtractor#setValue(String, Object)}.
+     */
+    @Test
+    public void testSetValue() {
+        request.setAttribute("name", "value");
+
+        replay(request);
+        extractor.setValue("name", "value");
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link RequestScopeExtractor#removeValue(String)}.
+     */
+    @Test
+    public void testRemoveValue() {
+        request.removeAttribute("name");
+
+        replay(request);
+        extractor.removeValue("name");
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.portlet.extractor.RequestScopeExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(request.getAttributeNames()).andReturn(keys);
+
+        replay(request, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(request, keys);
+    }
+
+    /**
+     * Test method for {@link RequestScopeExtractor#getValue(String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(request.getAttribute("name")).andReturn("value");
+
+        replay(request);
+        assertEquals("value", extractor.getValue("name"));
+        verify(request);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/SessionScopeExtractorTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/SessionScopeExtractorTest.java
new file mode 100644
index 0000000..85c6eb6
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/SessionScopeExtractorTest.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.request.portlet.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.portlet.PortletRequest;
+import javax.portlet.PortletSession;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link SessionScopeExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class SessionScopeExtractorTest {
+
+    /**
+     * The request.
+     */
+    private PortletRequest request;
+
+    /**
+     * The session.
+     */
+    private PortletSession session;
+
+    /**
+     * The scope to test.
+     */
+    private SessionScopeExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(PortletRequest.class);
+        session = createMock(PortletSession.class);
+        extractor = new SessionScopeExtractor(request, PortletSession.PORTLET_SCOPE);
+    }
+
+
+    /**
+     * Tests {@link SessionScopeExtractor#SessionScopeExtractor(PortletRequest, int)}.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testIllegalScope() {
+        replay(request, session);
+        new SessionScopeExtractor(request, 0);
+        verify(request, session);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#setValue(java.lang.String, java.lang.Object)}.
+     */
+    @Test
+    public void testSetValue() {
+        expect(request.getPortletSession()).andReturn(session);
+        session.setAttribute("name", "value", PortletSession.PORTLET_SCOPE);
+
+        replay(request, session);
+        extractor.setValue("name", "value");
+        verify(request, session);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#removeValue(java.lang.String)}.
+     */
+    @Test
+    public void testRemoveValue() {
+        expect(request.getPortletSession(false)).andReturn(session);
+        session.removeAttribute("name", PortletSession.PORTLET_SCOPE);
+
+        replay(request, session);
+        extractor.removeValue("name");
+        verify(request, session);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(request.getPortletSession(false)).andReturn(session);
+        expect(session.getAttributeNames(PortletSession.PORTLET_SCOPE)).andReturn(keys);
+
+        replay(request, session, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(request, session, keys);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#getKeys()}.
+     */
+    @Test
+    public void testGetKeysNoSession() {
+        expect(request.getPortletSession(false)).andReturn(null);
+
+        replay(request, session);
+        assertNull(extractor.getKeys());
+        verify(request, session);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(request.getPortletSession(false)).andReturn(session);
+        expect(session.getAttribute("name", PortletSession.PORTLET_SCOPE)).andReturn("value");
+
+        replay(request, session);
+        assertEquals("value", extractor.getValue("name"));
+        verify(request, session);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValueNoSession() {
+        expect(request.getPortletSession(false)).andReturn(null);
+
+        replay(request, session);
+        assertNull(extractor.getValue("name"));
+        verify(request, session);
+    }
+
+}
diff --git a/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/StateAwareParameterExtractorTest.java b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/StateAwareParameterExtractorTest.java
new file mode 100644
index 0000000..331040e
--- /dev/null
+++ b/tiles-request/tiles-request-portlet/src/test/java/org/apache/tiles/request/portlet/extractor/StateAwareParameterExtractorTest.java
@@ -0,0 +1,53 @@
+/*
+ * $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.request.portlet.extractor;
+
+import static org.easymock.classextension.EasyMock.*;
+
+import javax.portlet.PortletRequest;
+import javax.portlet.StateAwareResponse;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link StateAwareParameterExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class StateAwareParameterExtractorTest {
+
+    /**
+     * Test method for {@link StateAwareParameterExtractor#setValue(String, String)}.
+     */
+    @Test
+    public void testSetValue() {
+        PortletRequest request = createMock(PortletRequest.class);
+        StateAwareResponse response = createMock(StateAwareResponse.class);
+
+        response.setRenderParameter("name", "value");
+
+        replay(request, response);
+        StateAwareParameterExtractor extractor = new StateAwareParameterExtractor(request, response);
+        extractor.setValue("name", "value");
+        verify(request, response);
+    }
+
+}
diff --git a/tiles-request/tiles-request-servlet-wildcard/pom.xml b/tiles-request/tiles-request-servlet-wildcard/pom.xml
new file mode 100644
index 0000000..a7ede2d
--- /dev/null
+++ b/tiles-request/tiles-request-servlet-wildcard/pom.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0"?>
+<!--
+/*
+ * $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.
+ */
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <parent>
+    <groupId>org.apache.tiles</groupId>
+    <artifactId>tiles-request</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>tiles-request-servlet-wildcard</artifactId>
+  <packaging>jar</packaging>
+  <name>Tiles Request - Wildcard file loading in Servlets</name>
+  <description>Wildcard file loading in Servlets: Allows to load resources using wildcards.</description>
+
+  <properties>
+  	<tiles.osgi.symbolicName>org.apache.tiles.servlet.wildcard</tiles.osgi.symbolicName>
+  </properties>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <excludes>
+          <exclude>LICENSE.txt</exclude>
+          <exclude>NOTICE.txt</exclude>
+        </excludes>
+      </resource>
+      <resource>
+        <directory>src/main/resources</directory>
+        <includes>
+          <include>LICENSE.txt</include>
+          <include>NOTICE.txt</include>
+        </includes>
+        <targetPath>META-INF</targetPath>
+      </resource>
+    </resources>
+
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.tiles</groupId>
+      <artifactId>tiles-request-servlet</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-jdk14</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>jcl-over-slf4j</artifactId>
+      <optional>true</optional>
+    </dependency>
+
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-web</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymockclassextension</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/tiles-request/tiles-request-servlet-wildcard/src/main/java/org/apache/tiles/request/servlet/wildcard/WildcardServletApplicationContext.java b/tiles-request/tiles-request-servlet-wildcard/src/main/java/org/apache/tiles/request/servlet/wildcard/WildcardServletApplicationContext.java
new file mode 100644
index 0000000..359d9b2
--- /dev/null
+++ b/tiles-request/tiles-request-servlet-wildcard/src/main/java/org/apache/tiles/request/servlet/wildcard/WildcardServletApplicationContext.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.request.servlet.wildcard;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Locale;
+
+import javax.servlet.ServletContext;
+
+import org.apache.tiles.request.ApplicationResource;
+import org.apache.tiles.request.locale.URLApplicationResource;
+import org.apache.tiles.request.servlet.ServletApplicationContext;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.web.context.support.ServletContextResourcePatternResolver;
+
+/**
+ * Servlet-based implementation of the TilesApplicationContext interface that
+ * can resolve resources even using wildcards.
+ *
+ * @version $Rev$ $Date$
+ */
+public class WildcardServletApplicationContext extends ServletApplicationContext {
+
+    /**
+     * The pattern resolver.
+     */
+    protected ResourcePatternResolver resolver;
+
+    /**
+     * Constructor.
+     *
+     * @param servletContext The servlet context.
+     */
+    public WildcardServletApplicationContext(ServletContext servletContext) {
+        super(servletContext);
+        resolver = new ServletContextResourcePatternResolver(servletContext);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ApplicationResource getResource(String localePath) {
+        ApplicationResource retValue = null;
+        Collection<ApplicationResource> urlSet = getResources(localePath);
+        if (urlSet != null && !urlSet.isEmpty()) {
+            retValue = urlSet.iterator().next();
+        }
+        return retValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ApplicationResource getResource(ApplicationResource base, Locale locale) {
+        ApplicationResource retValue = null;
+        Collection<ApplicationResource> urlSet = getResources(base.getLocalePath(locale));
+        if (urlSet != null && !urlSet.isEmpty()) {
+            retValue = urlSet.iterator().next();
+        }
+        return retValue;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Collection<ApplicationResource> getResources(String path) {
+        Resource[] resources;
+        try {
+            resources = resolver.getResources(path);
+        } catch (IOException e) {
+            return Collections.<ApplicationResource> emptyList();
+        }
+        Collection<ApplicationResource> resourceList = new ArrayList<ApplicationResource>();
+        if (resources != null && resources.length > 0) {
+            for (int i = 0; i < resources.length; i++) {
+                URL url;
+                try {
+                    url = resources[i].getURL();
+                    resourceList.add(new URLApplicationResource(url.toExternalForm(), url));
+                } catch (IOException e) {
+                    // shouldn't happen with the kind of resources we're using
+                    throw new IllegalArgumentException("no URL for " + resources[i].toString(), e);
+                }
+            }
+        }
+        return resourceList;
+    }
+}
diff --git a/tiles-request/tiles-request-servlet-wildcard/src/main/java/org/apache/tiles/request/servlet/wildcard/package-info.java b/tiles-request/tiles-request-servlet-wildcard/src/main/java/org/apache/tiles/request/servlet/wildcard/package-info.java
new file mode 100644
index 0000000..105a6d8
--- /dev/null
+++ b/tiles-request/tiles-request-servlet-wildcard/src/main/java/org/apache/tiles/request/servlet/wildcard/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Classes and interfaces that allow to access the various contexts from a servlet
+ * application. These classes can manage resource getting through the use of
+ * Spring-like wildcards patterns.
+ */
+package org.apache.tiles.request.servlet.wildcard;
diff --git a/tiles-request/tiles-request-servlet-wildcard/src/site/site.xml b/tiles-request/tiles-request-servlet-wildcard/src/site/site.xml
new file mode 100644
index 0000000..8e92dd7
--- /dev/null
+++ b/tiles-request/tiles-request-servlet-wildcard/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Request Microframework">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Request Microframework"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-request/tiles-request-servlet-wildcard/src/test/java/org/apache/tiles/request/servlet/wildcard/WildcardServletApplicationContextTest.java b/tiles-request/tiles-request-servlet-wildcard/src/test/java/org/apache/tiles/request/servlet/wildcard/WildcardServletApplicationContextTest.java
new file mode 100644
index 0000000..1b4cd77
--- /dev/null
+++ b/tiles-request/tiles-request-servlet-wildcard/src/test/java/org/apache/tiles/request/servlet/wildcard/WildcardServletApplicationContextTest.java
@@ -0,0 +1,155 @@
+/*
+ * $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.request.servlet.wildcard;
+
+import junit.framework.TestCase;
+
+import org.apache.tiles.request.locale.URLApplicationResource;
+import org.easymock.EasyMock;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.Vector;
+import java.util.HashSet;
+
+import javax.servlet.ServletContext;
+
+
+/**
+ * Tests {@link WildcardServletApplicationContext}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class WildcardServletApplicationContextTest extends TestCase {
+
+    /**
+     * Number of properties container inside the test.properties file.
+     */
+    private static final int TEST_PROPERTIES_SIZE = 3;
+
+    /**
+     * The root Tiles application context.
+     */
+    private ServletContext servletContext;
+
+    /**
+     * The enhanced Tiles application context.
+     */
+    private WildcardServletApplicationContext context;
+
+    /**
+     * The original class loader.
+     */
+    private ClassLoader original;
+
+    /** {@inheritDoc} */
+    @Override
+    public void setUp() {
+        servletContext = EasyMock.createMock(ServletContext.class);
+        original = Thread.currentThread().getContextClassLoader();
+        try {
+            Thread.currentThread().setContextClassLoader(new MockClassLoader());
+        } catch (MalformedURLException e) {
+            throw new RuntimeException("Error when using the mock classloader");
+        }
+        context = new WildcardServletApplicationContext(servletContext);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void tearDown() {
+        Thread.currentThread().setContextClassLoader(original);
+    }
+
+    /**
+     * Tests resource getting.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    public void testGetResources() throws IOException {
+        String url = "/test.properties";
+        HashSet<URL> set = new HashSet<URL>();
+        URL u = new URL("file://tiles/test.properties");
+        set.add(u);
+        EasyMock.expect(servletContext.getResource(url)).andReturn(u)
+                .anyTimes();
+        File dir = new File(".");
+        EasyMock.expect(servletContext.getResource("/WEB-INF/")).andReturn(
+                dir.toURI().toURL());
+        URL pomUrl = new URL("file://tiles/pom.xml");
+        EasyMock.expect(servletContext.getResource("/WEB-INF/pom.xml"))
+                .andReturn(pomUrl);
+        Set<String> elementSet = new HashSet<String>();
+        elementSet.add("/WEB-INF/pom.xml");
+        EasyMock.expect(servletContext.getResourcePaths("/WEB-INF/")).andReturn(elementSet);
+        EasyMock.replay(servletContext);
+
+        assertEquals(new URLApplicationResource(u.toExternalForm(), u), context.getResource(url));
+        assertEquals(new URLApplicationResource(pomUrl.toExternalForm(), pomUrl), context.getResource("/WEB-INF/*.xml"));
+        assertEquals(TEST_PROPERTIES_SIZE, context.getResources(
+                "classpath*:/test.properties").size());
+
+        assertEquals(1, context.getResources(
+                "classpath*:/org/apache/tiles/request/servlet/wildcard/*Test.class").size());
+        EasyMock.verify(servletContext);
+    }
+
+    /**
+     * An mock class loader.
+     */
+    public class MockClassLoader extends ClassLoader {
+
+        /**
+         * A vector of resources.
+         */
+        private Vector<URL> testPropertiesResources;
+
+        /**
+         * Constructor.
+         *
+         * @throws MalformedURLException If the URL is not valid (that should
+         * not happen).
+         */
+        public MockClassLoader() throws MalformedURLException {
+            testPropertiesResources = new Vector<URL>();
+            testPropertiesResources.add(new URL("file://tiles/test/test.properties"));
+            testPropertiesResources.add(new URL("file://tiles/two/test.properties"));
+            testPropertiesResources.add(new URL("file://tiles/three/test.properties"));
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Enumeration<URL> findResources(String path) throws IOException {
+            Enumeration<URL> retValue = null;
+            if ("test.properties".equals(path)) {
+                retValue = testPropertiesResources.elements();
+            } else {
+                retValue = super.findResources(path);
+            }
+
+            return retValue;
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/pom.xml b/tiles-request/tiles-request-servlet/pom.xml
new file mode 100644
index 0000000..24b457c
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>tiles-request</artifactId>
+    <groupId>org.apache.tiles</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.tiles</groupId>
+  <artifactId>tiles-request-servlet</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Tiles request - Servlet support</name>
+  <description>Tiles request implementation for Servlet technology.</description>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.tiles</groupId>
+      <artifactId>tiles-request-api</artifactId>
+    </dependency>
+    <dependency>
+    	<groupId>javax.servlet</groupId>
+    	<artifactId>servlet-api</artifactId>
+    	<scope>provided</scope>
+    </dependency>
+    <dependency>
+    	<groupId>junit</groupId>
+    	<artifactId>junit</artifactId>
+    	<scope>test</scope>
+    </dependency>
+    <dependency>
+    	<groupId>org.easymock</groupId>
+    	<artifactId>easymock</artifactId>
+    	<scope>test</scope>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.shale</groupId>
+    	<artifactId>shale-test</artifactId>
+    	<scope>test</scope>
+    </dependency>
+    <dependency>
+    	<groupId>org.easymock</groupId>
+    	<artifactId>easymockclassextension</artifactId>
+    	<scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ExternalWriterHttpServletResponse.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ExternalWriterHttpServletResponse.java
new file mode 100644
index 0000000..66c2cdb
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ExternalWriterHttpServletResponse.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.request.servlet;
+
+import java.io.PrintWriter;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * Wraps an HTTP response and overrides its print writer.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ExternalWriterHttpServletResponse extends
+        HttpServletResponseWrapper {
+
+    /**
+     * The print writer to use, instead of the response's one.
+     */
+    private PrintWriter writer;
+
+    /**
+     * Constructor.
+     *
+     * @param response The response to wrap.
+     * @param writer The print writer to use, instead of the response's one.
+     */
+    public ExternalWriterHttpServletResponse(HttpServletResponse response, PrintWriter writer) {
+        super(response);
+        this.writer = writer;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public PrintWriter getWriter() {
+        return writer;
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/NotAServletEnvironmentException.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/NotAServletEnvironmentException.java
new file mode 100644
index 0000000..3b13425
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/NotAServletEnvironmentException.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.request.servlet;
+
+
+/**
+ * Exception that indicates that a resource could not be used because it is not
+ * in a servlet environment.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NotAServletEnvironmentException extends RuntimeException {
+
+    /**
+     * Constructor.
+     */
+    public NotAServletEnvironmentException() {
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     */
+    public NotAServletEnvironmentException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param e The exception to be wrapped.
+     */
+    public NotAServletEnvironmentException(Throwable e) {
+        super(e);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param message The detail message.
+     * @param e The exception to be wrapped.
+     */
+    public NotAServletEnvironmentException(String message, Throwable e) {
+        super(message, e);
+    }
+
+}
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ServletApplicationContext.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ServletApplicationContext.java
new file mode 100644
index 0000000..e841cc3
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ServletApplicationContext.java
@@ -0,0 +1,132 @@
+/*
+ * $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.request.servlet;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.ApplicationResource;
+import org.apache.tiles.request.collection.ReadOnlyEnumerationMap;
+import org.apache.tiles.request.collection.ScopeMap;
+import org.apache.tiles.request.locale.URLApplicationResource;
+import org.apache.tiles.request.servlet.extractor.ApplicationScopeExtractor;
+import org.apache.tiles.request.servlet.extractor.InitParameterExtractor;
+
+/**
+ * Servlet-based implementation of the TilesApplicationContext interface.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ServletApplicationContext implements ApplicationContext {
+
+    /**
+     * The servlet context to use.
+     */
+    private ServletContext servletContext;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of application scope
+     * attributes.</p>
+     */
+    private Map<String, Object> applicationScope = null;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of context initialization
+     * parameters.</p>
+     */
+    private Map<String, String> initParam = null;
+
+    /**
+     * Creates a new instance of ServletTilesApplicationContext.
+     *
+     * @param servletContext The servlet context to use.
+     */
+    public ServletApplicationContext(ServletContext servletContext) {
+        this.servletContext = servletContext;
+    }
+
+    /** {@inheritDoc} */
+    public Object getContext() {
+        return servletContext;
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, Object> getApplicationScope() {
+
+        if ((applicationScope == null) && (servletContext != null)) {
+            applicationScope = new ScopeMap(new ApplicationScopeExtractor(servletContext));
+        }
+        return (applicationScope);
+
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String> getInitParams() {
+
+        if ((initParam == null) && (servletContext != null)) {
+            initParam = new ReadOnlyEnumerationMap<String>(new InitParameterExtractor(servletContext));
+        }
+        return (initParam);
+
+    }
+
+    /** {@inheritDoc} */
+    public ApplicationResource getResource(String localePath) {
+        try {
+            URL url = servletContext.getResource(localePath);
+            if (url != null) {
+                return new URLApplicationResource(localePath, url);
+            } else {
+                return null;
+            }
+        } catch (MalformedURLException e) {
+            return null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public ApplicationResource getResource(ApplicationResource base, Locale locale) {
+        try {
+            URL url = servletContext.getResource(base.getLocalePath(locale));
+            if (url != null) {
+                return new URLApplicationResource(base.getPath(), locale, url);
+            } else {
+                return null;
+            }
+        } catch (MalformedURLException e) {
+            return null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Collection<ApplicationResource> getResources(String path) {
+        ArrayList<ApplicationResource> resources = new ArrayList<ApplicationResource>();
+        resources.add(getResource(path));
+        return resources;
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ServletRequest.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ServletRequest.java
new file mode 100644
index 0000000..d03809a
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ServletRequest.java
@@ -0,0 +1,320 @@
+/*
+ * $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.request.servlet;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tiles.request.AbstractClientRequest;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.attribute.Addable;
+import org.apache.tiles.request.collection.HeaderValuesMap;
+import org.apache.tiles.request.collection.ReadOnlyEnumerationMap;
+import org.apache.tiles.request.collection.ScopeMap;
+import org.apache.tiles.request.servlet.extractor.ParameterExtractor;
+import org.apache.tiles.request.servlet.extractor.RequestScopeExtractor;
+import org.apache.tiles.request.servlet.extractor.HeaderExtractor;
+import org.apache.tiles.request.servlet.extractor.SessionScopeExtractor;
+
+/**
+ * Servlet-based implementation of the TilesApplicationContext interface.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ServletRequest extends AbstractClientRequest {
+
+    /**
+     * The native available scopes: request, session and application.
+     */
+    private static final List<String> SCOPES
+            = Collections.unmodifiableList(Arrays.asList("request", "session", "application"));
+
+    /**
+     * The request object to use.
+     */
+    private HttpServletRequest request;
+
+    /**
+     * The response object to use.
+     */
+    private HttpServletResponse response;
+
+    /**
+     * The response output stream, lazily initialized.
+     */
+    private OutputStream outputStream;
+
+    /**
+     * The response writer, lazily initialized.
+     */
+    private PrintWriter writer;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of header name-value
+     * combinations (immutable).</p>
+     */
+    private Map<String, String> header = null;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of header name-value
+     * combinations (write-only).</p>
+     */
+    private Addable<String> responseHeaders = null;
+
+
+    /**
+     * <p>The lazily instantitated <code>Map</code> of header name-values
+     * combinations (immutable).</p>
+     */
+    private Map<String, String[]> headerValues = null;
+
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of request
+     * parameter name-value.</p>
+     */
+    private Map<String, String> param = null;
+
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of request scope
+     * attributes.</p>
+     */
+    private Map<String, Object> requestScope = null;
+
+    /**
+     * <p>The lazily instantiated <code>Map</code> of session scope
+     * attributes.</p>
+     */
+    private Map<String, Object> sessionScope = null;
+
+
+    /**
+     * Creates a new instance of ServletTilesRequestContext.
+     *
+     * @param applicationContext The application context.
+     * @param request The request object.
+     * @param response The response object.
+     */
+    public ServletRequest(
+            ApplicationContext applicationContext,
+            HttpServletRequest request, HttpServletResponse response) {
+        super(applicationContext);
+        this.request = request;
+        this.response = response;
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String> getHeader() {
+
+        if ((header == null) && (request != null)) {
+            header = new ReadOnlyEnumerationMap<String>(new HeaderExtractor(request, null));
+        }
+        return (header);
+
+    }
+
+    /** {@inheritDoc} */
+    public Addable<String> getResponseHeaders() {
+
+        if ((responseHeaders == null) && (response != null)) {
+            responseHeaders = new HeaderExtractor(null, response);
+        }
+        return (responseHeaders);
+
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, String[]> getHeaderValues() {
+
+        if ((headerValues == null) && (request != null)) {
+            headerValues = new HeaderValuesMap(new HeaderExtractor(request, response));
+        }
+        return (headerValues);
+
+    }
+
+
+    /** {@inheritDoc} */
+    public Map<String, String> getParam() {
+
+        if ((param == null) && (request != null)) {
+            param = new ReadOnlyEnumerationMap<String>(new ParameterExtractor(request));
+        }
+        return (param);
+
+    }
+
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    public Map<String, String[]> getParamValues() {
+        return request.getParameterMap();
+    }
+
+    @Override
+    public Map<String, Object> getContext(String scope) {
+        if("request".equals(scope)){
+            return getRequestScope();
+        }else if("session".equals(scope)){
+            return getSessionScope();
+        }else if("application".equals(scope)){
+            return getApplicationScope();
+        }
+        throw new IllegalArgumentException(scope + " does not exist. Call getAvailableScopes() first to check.");
+    }
+
+    /** {@inheritDoc} */
+    public Map<String, Object> getRequestScope() {
+
+        if ((requestScope == null) && (request != null)) {
+            requestScope = new ScopeMap(new RequestScopeExtractor(request));
+        }
+        return (requestScope);
+
+    }
+
+
+    /** {@inheritDoc} */
+    public Map<String, Object> getSessionScope() {
+
+        if ((sessionScope == null) && (request != null)) {
+            sessionScope = new ScopeMap(new SessionScopeExtractor(request));
+        }
+        return (sessionScope);
+
+    }
+
+    @Override
+    public List<String> getAvailableScopes() {
+        return SCOPES;
+    }
+
+    /** {@inheritDoc} */
+    public void doForward(String path) throws IOException {
+        if (response.isCommitted()) {
+            doInclude(path);
+        } else {
+            forward(path);
+        }
+    }
+
+
+    /** {@inheritDoc} */
+    public void doInclude(String path) throws IOException {
+        RequestDispatcher rd = request.getRequestDispatcher(path);
+
+        if (rd == null) {
+            throw new IOException("No request dispatcher returned for path '"
+                    + path + "'");
+        }
+
+        try {
+            rd.include(request, response);
+        } catch (ServletException ex) {
+            throw ServletUtil.wrapServletException(ex, "ServletException including path '"
+                    + path + "'.");
+        }
+    }
+
+    /**
+     * Forwards to a path.
+     *
+     * @param path The path to forward to.
+     * @throws IOException If something goes wrong during the operation.
+     */
+    private void forward(String path) throws IOException {
+        RequestDispatcher rd = request.getRequestDispatcher(path);
+
+        if (rd == null) {
+            throw new IOException("No request dispatcher returned for path '"
+                    + path + "'");
+        }
+
+        try {
+            rd.forward(request, response);
+        } catch (ServletException ex) {
+            throw ServletUtil.wrapServletException(ex, "ServletException including path '"
+                    + path + "'.");
+        }
+    }
+
+    /** {@inheritDoc} */
+    public OutputStream getOutputStream() throws IOException {
+        if (outputStream == null) {
+            outputStream = response.getOutputStream();
+        }
+        return outputStream;
+    }
+
+    /** {@inheritDoc} */
+    public Writer getWriter() throws IOException {
+        return getPrintWriter();
+    }
+
+    /** {@inheritDoc} */
+    public PrintWriter getPrintWriter() throws IOException {
+        if (writer == null) {
+            writer = response.getWriter();
+        }
+        return writer;
+    }
+
+    /** {@inheritDoc} */
+    public boolean isResponseCommitted() {
+        return response.isCommitted();
+    }
+
+    /** {@inheritDoc} */
+    public void setContentType(String contentType) {
+        response.setContentType(contentType);
+    }
+
+    /** {@inheritDoc} */
+    public Locale getRequestLocale() {
+        return request.getLocale();
+    }
+
+    public HttpServletRequest getRequest() {
+        return request;
+    }
+
+    public HttpServletResponse getResponse() {
+        return response;
+    }
+
+    /** {@inheritDoc} */
+    public boolean isUserInRole(String role) {
+        return request.isUserInRole(role);
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ServletUtil.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ServletUtil.java
new file mode 100644
index 0000000..652e8e9
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/ServletUtil.java
@@ -0,0 +1,122 @@
+/*
+ * $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.request.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.ApplicationAccess;
+import org.apache.tiles.request.DispatchRequestWrapper;
+import org.apache.tiles.request.Request;
+
+/**
+ * Utilities for Tiles request servlet support.
+ *
+ * @version $Rev$ $Date$
+ */
+public final class ServletUtil {
+
+    /**
+     * Constructor.
+     */
+    private ServletUtil() {
+    }
+
+    /**
+     * Wraps a ServletException to create an IOException with the root cause if present.
+     *
+     * @param ex The exception to wrap.
+     * @param message The message of the exception.
+     * @return The wrapped exception.
+     */
+    public static IOException wrapServletException(ServletException ex,
+            String message) {
+        IOException retValue;
+        Throwable rootCause = ex.getRootCause();
+        if (rootCause != null) {
+            // Replace the ServletException with an IOException, with the root
+            // cause of the first as the cause of the latter.
+            retValue = new IOException(message, rootCause);
+        } else {
+            retValue = new IOException(message, ex);
+        }
+
+        return retValue;
+    }
+
+    /**
+     * Returns the application context getting it from the servlet context. It must be
+     * first saved creating a {@link ServletApplicationContext} and using
+     * {@link ApplicationAccess#register(ApplicationContext)}.
+     *
+     * @param servletContext The servlet context.
+     * @return The application context, if found, <code>null</code> otherwise.
+     */
+    public static ApplicationContext getApplicationContext(ServletContext servletContext) {
+        return (ApplicationContext) servletContext
+                .getAttribute(ApplicationAccess.APPLICATION_CONTEXT_ATTRIBUTE);
+    }
+
+    /**
+     * Opens a TilesRequestContext until it finds a ServletTilesRequestContext.
+     *
+     * @param request The request to open.
+     * @return The servlet-based request context.
+     * @throws NotAServletEnvironmentException If a servlet-based request
+     * context could not be found.
+     */
+    public static ServletRequest getServletRequest(Request request) {
+        Request currentRequest = request;
+        while (true) {
+            if (currentRequest == null) {
+                throw new NotAServletEnvironmentException("Last Tiles request context is null");
+            }
+
+            if (currentRequest instanceof ServletRequest) {
+                return (ServletRequest) currentRequest;
+            }
+            if (!(currentRequest instanceof DispatchRequestWrapper)) {
+                throw new NotAServletEnvironmentException("Not a Servlet environment, not supported");
+            }
+            currentRequest = ((DispatchRequestWrapper) currentRequest).getWrappedRequest();
+        }
+    }
+
+    /**
+     * Gets a servlet context from a TilesApplicationContext.
+     *
+     * @param applicationContext The application context to analyze.
+     * @return The servlet context.
+     * @throws NotAServletEnvironmentException If the application context is not
+     * servlet-based.
+     */
+    public static ServletContext getServletContext(ApplicationContext applicationContext) {
+        if (applicationContext instanceof ServletApplicationContext) {
+            return (ServletContext) ((ServletApplicationContext) applicationContext).getContext();
+        }
+
+        throw new NotAServletEnvironmentException("Not a Servlet-based environment");
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/ApplicationScopeExtractor.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/ApplicationScopeExtractor.java
new file mode 100644
index 0000000..b7e3460
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/ApplicationScopeExtractor.java
@@ -0,0 +1,70 @@
+/*
+ * $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.request.servlet.extractor;
+
+import java.util.Enumeration;
+
+import javax.servlet.ServletContext;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+
+/**
+ * Extract attributes from application scope.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ApplicationScopeExtractor implements AttributeExtractor {
+
+    /**
+     * The servlet context.
+     */
+    private ServletContext context;
+
+    /**
+     * Constructor.
+     *
+     * @param context The servlet context.
+     */
+    public ApplicationScopeExtractor(ServletContext context) {
+        this.context = context;
+    }
+
+    @Override
+    public void setValue(String name, Object value) {
+        context.setAttribute(name, value);
+    }
+
+    @Override
+    public void removeValue(String name) {
+        context.removeAttribute(name);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Enumeration<String> getKeys() {
+        return context.getAttributeNames();
+    }
+
+    @Override
+    public Object getValue(String key) {
+        return context.getAttribute(key);
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/HeaderExtractor.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/HeaderExtractor.java
new file mode 100644
index 0000000..2820903
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/HeaderExtractor.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.request.servlet.extractor;
+
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tiles.request.attribute.EnumeratedValuesExtractor;
+
+/**
+ * Extract header values from an HTTP request.
+ *
+ * @version $Rev$ $Date$
+ */
+public class HeaderExtractor implements EnumeratedValuesExtractor {
+
+    /**
+     * The request.
+     */
+    private HttpServletRequest request;
+
+    /**
+     * The response.
+     */
+    private HttpServletResponse response;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request.
+     * @param response The response.
+     */
+    public HeaderExtractor(HttpServletRequest request,
+            HttpServletResponse response) {
+        this.request = request;
+        this.response = response;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Enumeration<String> getKeys() {
+        return request.getHeaderNames();
+   }
+
+    @Override
+    public String getValue(String key) {
+        return request.getHeader(key);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Enumeration<String> getValues(String key) {
+        return request.getHeaders(key);
+    }
+
+    @Override
+    public void setValue(String key, String value) {
+        response.setHeader(key, value);
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/InitParameterExtractor.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/InitParameterExtractor.java
new file mode 100644
index 0000000..6dbf7b7
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/InitParameterExtractor.java
@@ -0,0 +1,61 @@
+/*
+ * $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.request.servlet.extractor;
+
+import java.util.Enumeration;
+
+import javax.servlet.ServletContext;
+
+import org.apache.tiles.request.attribute.HasKeys;
+
+/**
+ * Extract initialization parameters from the servlet context.
+ *
+ * @version $Rev$ $Date$
+ */
+public class InitParameterExtractor implements HasKeys<String> {
+
+    /**
+     * The servlet context.
+     */
+    private ServletContext context;
+
+    /**
+     * Constructor.
+     *
+     * @param context The servlet context.
+     */
+    public InitParameterExtractor(ServletContext context) {
+        this.context = context;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Enumeration<String> getKeys() {
+        return context.getInitParameterNames();
+    }
+
+    @Override
+    public String getValue(String key) {
+        return context.getInitParameter(key);
+    }
+
+}
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/ParameterExtractor.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/ParameterExtractor.java
new file mode 100644
index 0000000..9749270
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/ParameterExtractor.java
@@ -0,0 +1,60 @@
+/*
+ * $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.request.servlet.extractor;
+
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.tiles.request.attribute.HasKeys;
+
+/**
+ * Extract parameters from the request.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ParameterExtractor implements HasKeys<String> {
+
+    /**
+     * The servlet request.
+     */
+    private HttpServletRequest request;
+
+    /**
+     * Constructor.
+     *
+     * @param request The servlet request.
+     */
+    public ParameterExtractor(HttpServletRequest request) {
+        this.request = request;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Enumeration<String> getKeys() {
+        return request.getParameterNames();
+    }
+
+    @Override
+    public String getValue(String key) {
+        return request.getParameter(key);
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/RequestScopeExtractor.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/RequestScopeExtractor.java
new file mode 100644
index 0000000..d558e10
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/RequestScopeExtractor.java
@@ -0,0 +1,70 @@
+/*
+ * $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.request.servlet.extractor;
+
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+
+/**
+ * Extracts attributes from request scope.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RequestScopeExtractor implements AttributeExtractor {
+
+    /**
+     * The servlet request.
+     */
+    private HttpServletRequest request;
+
+    /**
+     * Constructor.
+     *
+     * @param request The servlet request.
+     */
+    public RequestScopeExtractor(HttpServletRequest request) {
+        this.request = request;
+    }
+
+    @Override
+    public void setValue(String name, Object value) {
+        request.setAttribute(name, value);
+    }
+
+    @Override
+    public void removeValue(String name) {
+        request.removeAttribute(name);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Enumeration<String> getKeys() {
+        return request.getAttributeNames();
+    }
+
+    @Override
+    public Object getValue(String key) {
+        return request.getAttribute(key);
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/SessionScopeExtractor.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/SessionScopeExtractor.java
new file mode 100644
index 0000000..6d49743
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/SessionScopeExtractor.java
@@ -0,0 +1,83 @@
+/*
+ * $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.request.servlet.extractor;
+
+import java.util.Enumeration;
+import java.util.Collections;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+
+/**
+ * Extract attributes from session scope.
+ *
+ * @version $Rev$ $Date$
+ */
+public class SessionScopeExtractor implements AttributeExtractor {
+
+    /**
+     * The servlet request.
+     */
+    private HttpServletRequest request;
+
+    /**
+     * Constructor.
+     *
+     * @param request The servlet request.
+     */
+    public SessionScopeExtractor(HttpServletRequest request) {
+        this.request = request;
+    }
+
+    @Override
+    public void setValue(String name, Object value) {
+        request.getSession().setAttribute(name, value);
+    }
+
+    @Override
+    public void removeValue(String name) {
+        HttpSession session = request.getSession(false);
+        if (session != null) {
+            session.removeAttribute(name);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Enumeration<String> getKeys() {
+        HttpSession session = request.getSession(false);
+        if (session != null) {
+            return session.getAttributeNames();
+        }
+        return Collections.enumeration(Collections.<String>emptySet());
+    }
+
+    @Override
+    public Object getValue(String key) {
+        HttpSession session = request.getSession(false);
+        if (session != null) {
+            return session.getAttribute(key);
+        }
+        return null;
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/package-info.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/package-info.java
new file mode 100644
index 0000000..0df9334
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/extractor/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Extractors to get attributes and other info from servlet requests and contexts.
+ */
+package org.apache.tiles.request.servlet.extractor;
diff --git a/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/package-info.java b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/package-info.java
new file mode 100644
index 0000000..331e1f1
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/main/java/org/apache/tiles/request/servlet/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Tiles request support for Servlet technology.
+ */
+package org.apache.tiles.request.servlet;
diff --git a/tiles-request/tiles-request-servlet/src/site/site.xml b/tiles-request/tiles-request-servlet/src/site/site.xml
new file mode 100644
index 0000000..8e92dd7
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Request Microframework">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Request Microframework"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ExternalWriterHttpServletResponseTest.java b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ExternalWriterHttpServletResponseTest.java
new file mode 100644
index 0000000..67d0562
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ExternalWriterHttpServletResponseTest.java
@@ -0,0 +1,55 @@
+/*
+ * $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.request.servlet;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link ExternalWriterHttpServletResponse}.
+ */
+public class ExternalWriterHttpServletResponseTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ExternalWriterHttpServletResponse#getWriter()}.
+     */
+    @Test
+    public void testGetWriter() {
+        HttpServletResponse wrappedResponse = createMock(HttpServletResponse.class);
+        Writer writer = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(writer);
+        replay(wrappedResponse);
+        ExternalWriterHttpServletResponse response = new ExternalWriterHttpServletResponse(
+                wrappedResponse, printWriter);
+        assertEquals(printWriter, response.getWriter());
+        verify(wrappedResponse);
+    }
+
+}
diff --git a/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/NotAServletEnvironmentExceptionTest.java b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/NotAServletEnvironmentExceptionTest.java
new file mode 100644
index 0000000..6a750f8
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/NotAServletEnvironmentExceptionTest.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.request.servlet;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+/**
+ * Tests {@link NotAServletEnvironmentException}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NotAServletEnvironmentExceptionTest {
+
+    /**
+     * Test method for {@link NotAServletEnvironmentException#NotAServletEnvironmentException()}.
+     */
+    @Test
+    public void testNotAServletEnvironmentException() {
+        NotAServletEnvironmentException exception = new NotAServletEnvironmentException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link NotAServletEnvironmentException#NotAServletEnvironmentException(java.lang.String)}.
+     */
+    @Test
+    public void testNotAServletEnvironmentExceptionString() {
+        NotAServletEnvironmentException exception = new NotAServletEnvironmentException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link NotAServletEnvironmentException#NotAServletEnvironmentException(java.lang.Throwable)}.
+     */
+    @Test
+    public void testNotAServletEnvironmentExceptionThrowable() {
+        Throwable cause = new Throwable();
+        NotAServletEnvironmentException exception = new NotAServletEnvironmentException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for {@link NotAServletEnvironmentException#NotAServletEnvironmentException(String, Throwable)}.
+     */
+    @Test
+    public void testNotAServletEnvironmentExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        NotAServletEnvironmentException exception = new NotAServletEnvironmentException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git a/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ServletApplicationContextTest.java b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ServletApplicationContextTest.java
new file mode 100644
index 0000000..1ab3413
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ServletApplicationContextTest.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.request.servlet;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Locale;
+
+import javax.servlet.ServletContext;
+
+import org.apache.tiles.request.ApplicationResource;
+import org.apache.tiles.request.collection.ReadOnlyEnumerationMap;
+import org.apache.tiles.request.collection.ScopeMap;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ServletApplicationContext}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ServletApplicationContextTest {
+
+    /**
+     * The servlet context.
+     */
+    private ServletContext servletContext;
+
+    /**
+     * The application context to test.
+     */
+    private ServletApplicationContext context;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        servletContext = createMock(ServletContext.class);
+        context = new ServletApplicationContext(servletContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletApplicationContext#getContext()}.
+     */
+    @Test
+    public void testGetContext() {
+        replay(servletContext);
+        assertEquals(servletContext, context.getContext());
+        verify(servletContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletApplicationContext#getApplicationScope()}.
+     */
+    @Test
+    public void testGetApplicationScope() {
+        replay(servletContext);
+        assertTrue(context.getApplicationScope() instanceof ScopeMap);
+        verify(servletContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletApplicationContext#getInitParams()}.
+     */
+    @Test
+    public void testGetInitParams() {
+        replay(servletContext);
+        assertTrue(context.getInitParams() instanceof ReadOnlyEnumerationMap);
+        verify(servletContext);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletApplicationContext#getResource(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetResource() throws IOException {
+        URL url = new URL("file:///servletContext/my/path.html");
+        URL urlFr = new URL("file:///servletContext/my/path_fr.html");
+        expect(servletContext.getResource("/my/path.html")).andReturn(url);
+        expect(servletContext.getResource("/my/path_fr.html")).andReturn(urlFr);
+        expect(servletContext.getResource("/null/path.html")).andReturn(null);
+
+        replay(servletContext);
+        ApplicationResource resource = context.getResource("/my/path.html");
+        assertNotNull(resource);
+        assertEquals(resource.getLocalePath(), "/my/path.html");
+        assertEquals(resource.getPath(), "/my/path.html");
+        assertEquals(Locale.ROOT, resource.getLocale());
+        ApplicationResource resourceFr = context.getResource(resource, Locale.FRENCH);
+        assertNotNull(resourceFr);
+        assertEquals("/my/path_fr.html", resourceFr.getLocalePath());
+        assertEquals("/my/path.html", resourceFr.getPath());
+        assertEquals(Locale.FRENCH, resourceFr.getLocale());
+        ApplicationResource nullResource = context.getResource("/null/path.html");
+        assertNull(nullResource);
+        verify(servletContext);
+    }
+
+    /**
+     * Test method for {@link ServletApplicationContext#getResources(String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetResources() throws IOException {
+        URL url = new URL("file:///servletContext/my/path");
+        expect(servletContext.getResource("/my/path")).andReturn(url);
+
+        replay(servletContext);
+        Collection<ApplicationResource> resources = context.getResources("/my/path");
+        assertEquals(1, resources.size());
+        assertEquals(resources.iterator().next().getLocalePath(), "/my/path");
+        verify(servletContext);
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ServletRequestTest.java b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ServletRequestTest.java
new file mode 100644
index 0000000..14b05cf
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ServletRequestTest.java
@@ -0,0 +1,391 @@
+/*
+ * $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.request.servlet;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.collection.HeaderValuesMap;
+import org.apache.tiles.request.collection.ReadOnlyEnumerationMap;
+import org.apache.tiles.request.collection.ScopeMap;
+import org.apache.tiles.request.servlet.extractor.HeaderExtractor;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ServletRequest}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ServletRequestTest {
+
+    /**
+     * The application context.
+     */
+    private ApplicationContext applicationContext;
+
+    /**
+     * The request.
+     */
+    private HttpServletRequest request;
+
+    /**
+     * The response.
+     */
+    private HttpServletResponse response;
+
+    /**
+     * The request to test.
+     */
+    private ServletRequest req;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        applicationContext = createMock(ApplicationContext.class);
+        request = createMock(HttpServletRequest.class);
+        response = createMock(HttpServletResponse.class);
+        req = new ServletRequest(applicationContext, request, response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#doForward(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws ServletException If something goes wrong.
+     */
+    @Test
+    public void testDoForward() throws ServletException, IOException {
+        RequestDispatcher rd = createMock(RequestDispatcher.class);
+
+        expect(response.isCommitted()).andReturn(false);
+        expect(request.getRequestDispatcher("/my/path")).andReturn(rd);
+        rd.forward(request, response);
+
+        replay(applicationContext, request, response, rd);
+        req.doForward("/my/path");
+        verify(applicationContext, request, response, rd);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#doForward(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testDoForwardNoDispatcher() throws IOException {
+        expect(response.isCommitted()).andReturn(false);
+        expect(request.getRequestDispatcher("/my/path")).andReturn(null);
+
+        replay(applicationContext, request, response);
+        try {
+            req.doForward("/my/path");
+        } finally {
+            verify(applicationContext, request, response);
+        }
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#doForward(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws ServletException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testDoForwardServletException() throws ServletException, IOException {
+        RequestDispatcher rd = createMock(RequestDispatcher.class);
+
+        expect(response.isCommitted()).andReturn(false);
+        expect(request.getRequestDispatcher("/my/path")).andReturn(rd);
+        rd.forward(request, response);
+        expectLastCall().andThrow(new ServletException());
+
+        replay(applicationContext, request, response, rd);
+        try {
+            req.doForward("/my/path");
+        } finally {
+            verify(applicationContext, request, response, rd);
+        }
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#doForward(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws ServletException If something goes wrong.
+     */
+    @Test
+    public void testDoForwardInclude() throws ServletException, IOException {
+        RequestDispatcher rd = createMock(RequestDispatcher.class);
+
+        expect(response.isCommitted()).andReturn(true);
+        expect(request.getRequestDispatcher("/my/path")).andReturn(rd);
+        rd.include(request, response);
+
+        replay(applicationContext, request, response, rd);
+        req.doForward("/my/path");
+        verify(applicationContext, request, response, rd);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#doInclude(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws ServletException If something goes wrong.
+     */
+    @Test
+    public void testDoInclude() throws IOException, ServletException {
+        RequestDispatcher rd = createMock(RequestDispatcher.class);
+
+        expect(request.getRequestDispatcher("/my/path")).andReturn(rd);
+        rd.include(request, response);
+
+        replay(applicationContext, request, response, rd);
+        req.doInclude("/my/path");
+        verify(applicationContext, request, response, rd);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#doInclude(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testDoIncludeNoDispatcher() throws IOException {
+        expect(request.getRequestDispatcher("/my/path")).andReturn(null);
+
+        replay(applicationContext, request, response);
+        try {
+            req.doInclude("/my/path");
+        } finally {
+            verify(applicationContext, request, response);
+        }
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#doInclude(java.lang.String)}.
+     * @throws IOException If something goes wrong.
+     * @throws ServletException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testDoIncludeServletException() throws IOException, ServletException {
+        RequestDispatcher rd = createMock(RequestDispatcher.class);
+
+        expect(request.getRequestDispatcher("/my/path")).andReturn(rd);
+        rd.include(request, response);
+        expectLastCall().andThrow(new ServletException());
+
+        replay(applicationContext, request, response, rd);
+        try {
+            req.doInclude("/my/path");
+        } finally {
+            verify(applicationContext, request, response, rd);
+        }
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getHeader()}.
+     */
+    @Test
+    public void testGetHeader() {
+        assertTrue(req.getHeader() instanceof ReadOnlyEnumerationMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getHeader()}.
+     */
+    @Test
+    public void testGetResponseHeaders() {
+        assertTrue(req.getResponseHeaders() instanceof HeaderExtractor);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getHeaderValues()}.
+     */
+    @Test
+    public void testGetHeaderValues() {
+        assertTrue(req.getHeaderValues() instanceof HeaderValuesMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getParam()}.
+     */
+    @Test
+    public void testGetParam() {
+        assertTrue(req.getParam() instanceof ReadOnlyEnumerationMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getParamValues()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetParamValues() {
+        Map<String, String[]> paramMap = createMock(Map.class);
+
+        expect(request.getParameterMap()).andReturn(paramMap);
+
+        replay(applicationContext, request, response, paramMap);
+        assertEquals(paramMap, req.getParamValues());
+        verify(applicationContext, request, response, paramMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getRequestScope()}.
+     */
+    @Test
+    public void testGetRequestScope() {
+        assertTrue(req.getRequestScope() instanceof ScopeMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getSessionScope()}.
+     */
+    @Test
+    public void testGetSessionScope() {
+        assertTrue(req.getSessionScope() instanceof ScopeMap);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getOutputStream()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetOutputStream() throws IOException {
+        ServletOutputStream os = createMock(ServletOutputStream.class);
+
+        expect(response.getOutputStream()).andReturn(os);
+
+        replay(applicationContext, request, response, os);
+        assertEquals(req.getOutputStream(), os);
+        verify(applicationContext, request, response, os);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getWriter()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetWriter() throws IOException {
+        PrintWriter os = createMock(PrintWriter.class);
+
+        expect(response.getWriter()).andReturn(os);
+
+        replay(applicationContext, request, response, os);
+        assertEquals(req.getWriter(), os);
+        verify(applicationContext, request, response, os);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getPrintWriter()}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testGetPrintWriter() throws IOException {
+        PrintWriter os = createMock(PrintWriter.class);
+
+        expect(response.getWriter()).andReturn(os);
+
+        replay(applicationContext, request, response, os);
+        assertEquals(req.getPrintWriter(), os);
+        verify(applicationContext, request, response, os);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#isResponseCommitted()}.
+     */
+    @Test
+    public void testIsResponseCommitted() {
+        expect(response.isCommitted()).andReturn(true);
+
+        replay(applicationContext, request, response);
+        assertTrue(req.isResponseCommitted());
+        verify(applicationContext, request, response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#setContentType(java.lang.String)}.
+     */
+    @Test
+    public void testSetContentType() {
+        response.setContentType("text/html");
+
+        replay(applicationContext, request, response);
+        req.setContentType("text/html");
+        verify(applicationContext, request, response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getRequestLocale()}.
+     */
+    @Test
+    public void testGetRequestLocale() {
+        Locale locale = Locale.ITALY;
+
+        expect(request.getLocale()).andReturn(locale);
+
+        replay(applicationContext, request, response);
+        assertEquals(locale, req.getRequestLocale());
+        verify(applicationContext, request, response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getRequest()}.
+     */
+    @Test
+    public void testGetRequest() {
+        replay(applicationContext, request, response);
+        assertEquals(request, req.getRequest());
+        verify(applicationContext, request, response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#getResponse()}.
+     */
+    @Test
+    public void testGetResponse() {
+        replay(applicationContext, request, response);
+        assertEquals(response, req.getResponse());
+        verify(applicationContext, request, response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.ServletRequest#isUserInRole(java.lang.String)}.
+     */
+    @Test
+    public void testIsUserInRole() {
+        expect(request.isUserInRole("myrole")).andReturn(true);
+
+        replay(applicationContext, request, response);
+        assertTrue(req.isUserInRole("myrole"));
+        verify(applicationContext, request, response);
+    }
+
+}
diff --git a/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ServletUtilTest.java b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ServletUtilTest.java
new file mode 100644
index 0000000..38942b0
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/ServletUtilTest.java
@@ -0,0 +1,82 @@
+/*
+ * $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.request.servlet;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.tiles.request.ApplicationAccess;
+import org.apache.tiles.request.ApplicationContext;
+import org.junit.Test;
+
+/**
+ * Tests {@link ServletUtil}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ServletUtilTest {
+
+    /**
+     * Test method for {@link ServletUtil#wrapServletException(ServletException, String)}.
+     */
+    @Test
+    public void testWrapServletException() {
+        ServletException servletException = new ServletException();
+        IOException exception = ServletUtil.wrapServletException(servletException, "my message");
+        assertEquals(servletException, exception.getCause());
+        assertEquals("my message", exception.getMessage());
+    }
+
+    /**
+     */
+    @Test
+    public void testWrapServletExceptionWithCause() {
+        Throwable cause = createMock(Throwable.class);
+
+        replay(cause);
+        ServletException servletException = new ServletException(cause);
+        IOException exception = ServletUtil.wrapServletException(servletException, "my message");
+        assertEquals(cause, exception.getCause());
+        assertEquals("my message", exception.getMessage());
+        verify(cause);
+    }
+
+    /**
+     * Test method for {@link ServletUtil#getApplicationContext(ServletContext)}.
+     */
+    @Test
+    public void testGetApplicationContext() {
+        ServletContext servletContext = createMock(ServletContext.class);
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+
+        expect(servletContext.getAttribute(ApplicationAccess
+                .APPLICATION_CONTEXT_ATTRIBUTE)).andReturn(applicationContext);
+
+        replay(servletContext, applicationContext);
+        assertEquals(applicationContext, ServletUtil.getApplicationContext(servletContext));
+        verify(servletContext, applicationContext);
+    }
+}
diff --git a/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/ApplicationScopeExtractorTest.java b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/ApplicationScopeExtractorTest.java
new file mode 100644
index 0000000..72f8939
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/ApplicationScopeExtractorTest.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.request.servlet.extractor;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.servlet.ServletContext;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ApplicationScopeExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ApplicationScopeExtractorTest {
+
+    /**
+     * The servlet context.
+     */
+    private ServletContext context;
+
+    /**
+     * The extractor to test.
+     */
+    private ApplicationScopeExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        context = createMock(ServletContext.class);
+        extractor = new ApplicationScopeExtractor(context);
+    }
+
+    /**
+     * Test method for {@link ApplicationScopeExtractor#setValue(String, Object)}.
+     */
+    @Test
+    public void testSetValue() {
+        context.setAttribute("attribute", "value");
+
+        replay(context);
+        extractor.setValue("attribute", "value");
+        verify(context);
+    }
+
+    /**
+     * Test method for {@link ApplicationScopeExtractor#removeValue(String)}.
+     */
+    @Test
+    public void testRemoveValue() {
+        context.removeAttribute("attribute");
+
+        replay(context);
+        extractor.removeValue("attribute");
+        verify(context);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.extractor.ApplicationScopeExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+        expect(context.getAttributeNames()).andReturn(keys);
+
+        replay(context, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(context, keys);
+    }
+
+    /**
+     * Test method for {@link ApplicationScopeExtractor#getValue(String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(context.getAttribute("attribute")).andReturn("value");
+
+        replay(context);
+        assertEquals("value", extractor.getValue("attribute"));
+        verify(context);
+    }
+
+}
diff --git a/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/HeaderExtractorTest.java b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/HeaderExtractorTest.java
new file mode 100644
index 0000000..e05f86f
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/HeaderExtractorTest.java
@@ -0,0 +1,120 @@
+/*
+ * $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.request.servlet.extractor;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link HeaderExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class HeaderExtractorTest {
+
+    /**
+     * The request.
+     */
+    private HttpServletRequest request;
+
+    /**
+     * The response.
+     */
+    private HttpServletResponse response;
+
+    /**
+     * The extractor to test.
+     */
+    private HeaderExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(HttpServletRequest.class);
+        response = createMock(HttpServletResponse.class);
+        extractor = new HeaderExtractor(request, response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.extractor.HeaderExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(request.getHeaderNames()).andReturn(keys);
+
+        replay(request, response, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(request, response, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.extractor.HeaderExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(request.getHeader("name")).andReturn("value");
+
+        replay(request, response);
+        assertEquals("value", extractor.getValue("name"));
+        verify(request, response);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.extractor.HeaderExtractor#getValues(java.lang.String)}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetValues() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(request.getHeaders("name")).andReturn(keys);
+
+        replay(request, response, keys);
+        assertEquals(keys, extractor.getValues("name"));
+        verify(request, response, keys);
+    }
+
+    /**
+     * Test method for {@link HeaderExtractor#setValue(String, String)}.
+     */
+    @Test
+    public void testSetValue() {
+        response.setHeader("name", "value");
+
+        replay(request, response);
+        extractor.setValue("name", "value");
+        verify(request, response);
+    }
+
+}
diff --git a/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/InitParameterExtractorTest.java b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/InitParameterExtractorTest.java
new file mode 100644
index 0000000..f45acaf
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/InitParameterExtractorTest.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.request.servlet.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.servlet.ServletContext;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link InitParameterExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class InitParameterExtractorTest {
+
+    /**
+     * The servlet context.
+     */
+    private ServletContext context;
+
+    /**
+     * The extractor to test.
+     */
+    private InitParameterExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        context = createMock(ServletContext.class);
+        extractor = new InitParameterExtractor(context);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.extractor.InitParameterExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(context.getInitParameterNames()).andReturn(keys);
+
+        replay(context, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(context, keys);
+    }
+
+    /**
+     * Test method for {@link InitParameterExtractor#getValue(String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(context.getInitParameter("name")).andReturn("value");
+
+        replay(context);
+        assertEquals("value", extractor.getValue("name"));
+        verify(context);
+    }
+
+}
diff --git a/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/ParameterExtractorTest.java b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/ParameterExtractorTest.java
new file mode 100644
index 0000000..df0b0bf
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/ParameterExtractorTest.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.request.servlet.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ParameterExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ParameterExtractorTest {
+
+    /**
+     * The request.
+     */
+    private HttpServletRequest request;
+
+    /**
+     * The extractor to test.
+     */
+    private ParameterExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(HttpServletRequest.class);
+        extractor = new ParameterExtractor(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.extractor.ParameterExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(request.getParameterNames()).andReturn(keys);
+
+        replay(request, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(request, keys);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.extractor.ParameterExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(request.getParameter("name")).andReturn("value");
+
+        replay(request);
+        assertEquals("value", extractor.getValue("name"));
+        verify(request);
+    }
+
+}
diff --git a/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/RequestScopeExtractorTest.java b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/RequestScopeExtractorTest.java
new file mode 100644
index 0000000..c5ef1c8
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/RequestScopeExtractorTest.java
@@ -0,0 +1,111 @@
+/*
+ * $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.request.servlet.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link RequestScopeExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class RequestScopeExtractorTest {
+
+    /**
+     * The request.
+     */
+    private HttpServletRequest request;
+
+    /**
+     * The extractor to test.
+     */
+    private RequestScopeExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(HttpServletRequest.class);
+        extractor = new RequestScopeExtractor(request);
+    }
+
+    /**
+     * Test method for {@link RequestScopeExtractor#setValue(String, Object)}.
+     */
+    @Test
+    public void testSetValue() {
+        request.setAttribute("name", "value");
+
+        replay(request);
+        extractor.setValue("name", "value");
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link RequestScopeExtractor#removeValue(String)}.
+     */
+    @Test
+    public void testRemoveValue() {
+        request.removeAttribute("name");
+
+        replay(request);
+        extractor.removeValue("name");
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.servlet.extractor.RequestScopeExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(request.getAttributeNames()).andReturn(keys);
+
+        replay(request, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(request, keys);
+    }
+
+    /**
+     * Test method for {@link RequestScopeExtractor#getValue(String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(request.getAttribute("name")).andReturn("value");
+
+        replay(request);
+        assertEquals("value", extractor.getValue("name"));
+        verify(request);
+    }
+
+}
diff --git a/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/SessionScopeExtractorTest.java b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/SessionScopeExtractorTest.java
new file mode 100644
index 0000000..8be27b4
--- /dev/null
+++ b/tiles-request/tiles-request-servlet/src/test/java/org/apache/tiles/request/servlet/extractor/SessionScopeExtractorTest.java
@@ -0,0 +1,148 @@
+/*
+ * $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.request.servlet.extractor;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link SessionScopeExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class SessionScopeExtractorTest {
+
+    /**
+     * The request.
+     */
+    private HttpServletRequest request;
+
+    /**
+     * The session.
+     */
+    private HttpSession session;
+
+    /**
+     * The extractot to test.
+     */
+    private SessionScopeExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(HttpServletRequest.class);
+        session = createMock(HttpSession.class);
+        extractor = new SessionScopeExtractor(request);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#setValue(java.lang.String, java.lang.Object)}.
+     */
+    @Test
+    public void testSetValue() {
+        expect(request.getSession()).andReturn(session);
+        session.setAttribute("name", "value");
+
+        replay(request, session);
+        extractor.setValue("name", "value");
+        verify(request, session);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#removeValue(java.lang.String)}.
+     */
+    @Test
+    public void testRemoveValue() {
+        expect(request.getSession(false)).andReturn(session);
+        session.removeAttribute("name");
+
+        replay(request, session);
+        extractor.removeValue("name");
+        verify(request, session);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#getKeys()}.
+     */
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testGetKeys() {
+        Enumeration<String> keys = createMock(Enumeration.class);
+
+        expect(request.getSession(false)).andReturn(session);
+        expect(session.getAttributeNames()).andReturn(keys);
+
+        replay(request, session, keys);
+        assertEquals(keys, extractor.getKeys());
+        verify(request, session, keys);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#getKeys()}.
+     */
+    @Test
+    public void testGetKeysNoSession() {
+        expect(request.getSession(false)).andReturn(null);
+
+        replay(request, session);
+        Enumeration<String> keys = extractor.getKeys();
+        assertNotNull(keys);
+        assertFalse(keys.hasMoreElements());
+        verify(request, session);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(request.getSession(false)).andReturn(session);
+        expect(session.getAttribute("name")).andReturn("value");
+
+        replay(request, session);
+        assertEquals("value", extractor.getValue("name"));
+        verify(request, session);
+    }
+
+    /**
+     * Test method for {@link SessionScopeExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValueNoSession() {
+        expect(request.getSession(false)).andReturn(null);
+
+        replay(request, session);
+        assertNull(extractor.getValue("name"));
+        verify(request, session);
+    }
+
+}
diff --git a/tiles-request/tiles-request-velocity/pom.xml b/tiles-request/tiles-request-velocity/pom.xml
new file mode 100644
index 0000000..c8fe373
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>tiles-request</artifactId>
+    <groupId>org.apache.tiles</groupId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.tiles</groupId>
+  <artifactId>tiles-request-velocity</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <name>Tiles Request - Velocity support</name>
+  <description>Velocity implementation of the Tiles request framework</description>
+  <dependencies>
+  	<dependency>
+  		<groupId>org.apache.velocity</groupId>
+  		<artifactId>velocity-tools</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>javax.servlet</groupId>
+  		<artifactId>servlet-api</artifactId>
+  		<scope>provided</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>junit</groupId>
+  		<artifactId>junit</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.easymock</groupId>
+  		<artifactId>easymock</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.easymock</groupId>
+  		<artifactId>easymockclassextension</artifactId>
+  		<scope>test</scope>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.apache.tiles</groupId>
+  		<artifactId>tiles-request-servlet</artifactId>
+  	</dependency>
+  	<dependency>
+  		<groupId>org.apache.tiles</groupId>
+  		<artifactId>tiles-autotag-core-runtime</artifactId>
+  		<optional>true</optional>
+  	</dependency>
+  </dependencies>
+</project>
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/VelocityRequest.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/VelocityRequest.java
new file mode 100644
index 0000000..1f087f6
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/VelocityRequest.java
@@ -0,0 +1,178 @@
+/*
+ * $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.request.velocity;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tiles.request.AbstractViewRequest;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.DispatchRequest;
+import org.apache.tiles.request.servlet.ExternalWriterHttpServletResponse;
+import org.apache.tiles.request.servlet.ServletRequest;
+import org.apache.tiles.request.servlet.ServletUtil;
+import org.apache.velocity.context.Context;
+
+/**
+ * The implementation of the Tiles request context specific for Velocity.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityRequest extends AbstractViewRequest {
+
+    /**
+     * The native available scopes, in fact only "page".
+     */
+    private final List<String> scopes;
+
+    /**
+     * The Velocity current context.
+     */
+    private final Context ctx;
+
+    /**
+     * The writer to use to render the response. It may be null, if not necessary.
+     */
+    private Writer writer;
+
+    /**
+     * The map of the page scope.
+     */
+    private Map<String, Object> pageScope;
+
+    /**
+     * Factory method to create a Velocity request.
+     *
+     * @param applicationContext The application context.
+     * @param request The request.
+     * @param response The response.
+     * @param velocityContext The Velocity context.
+     * @param writer The writer to write into.
+     * @return The request.
+     */
+    public static VelocityRequest createVelocityRequest(
+            ApplicationContext applicationContext, HttpServletRequest request,
+            HttpServletResponse response, Context velocityContext, Writer writer) {
+        DispatchRequest servletRequest = new ServletRequest(
+                applicationContext, request, response);
+        VelocityRequest velocityRequest = new VelocityRequest(
+                servletRequest, velocityContext, writer);
+        return velocityRequest;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param enclosedRequest The request that exposes non-Velocity specific properties
+     * @param ctx The Velocity current context.
+     * @param writer The writer to use to render the response. It may be null, if not necessary.
+     */
+    public VelocityRequest(
+            DispatchRequest enclosedRequest, Context ctx, Writer writer) {
+        super(enclosedRequest);
+        List<String> scopes = new ArrayList<String>();
+        scopes.addAll(enclosedRequest.getAvailableScopes());
+        scopes.add("page");
+        this.scopes = Collections.unmodifiableList(scopes);
+        this.ctx = ctx;
+        this.writer = writer;
+    }
+
+    @Override
+    public List<String> getAvailableScopes() {
+        return scopes;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void doInclude(String path) throws IOException {
+        ServletRequest servletRequest = org.apache.tiles.request.servlet.ServletUtil.getServletRequest(this);
+        HttpServletRequest request = servletRequest.getRequest();
+        HttpServletResponse response = servletRequest.getResponse();
+        RequestDispatcher rd = request.getRequestDispatcher(path);
+
+        if (rd == null) {
+            throw new IOException("No request dispatcher returned for path '"
+                    + path + "'");
+        }
+
+        PrintWriter printWriter = getPrintWriter();
+        try {
+            rd.include(request, new ExternalWriterHttpServletResponse(response,
+                    printWriter));
+        } catch (ServletException ex) {
+            throw ServletUtil.wrapServletException(ex, "ServletException including path '"
+                    + path + "'.");
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public PrintWriter getPrintWriter() {
+        if (writer == null) {
+            throw new IllegalStateException(
+                    "A writer-less Tiles request has been created, cannot return a PrintWriter");
+        }
+        if (writer instanceof PrintWriter) {
+            return (PrintWriter) writer;
+        }
+        return new PrintWriter(writer);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Writer getWriter() {
+        if (writer == null) {
+            throw new IllegalStateException(
+                    "A writer-less Tiles request has been created, cannot return a PrintWriter");
+        }
+        return writer;
+    }
+
+    /**
+     * Returns the page scope.
+     *
+     * @return The page scope.
+     */
+    public Map<String, Object> getPageScope() {
+        if (pageScope == null) {
+            pageScope = new VelocityScopeMap(ctx);
+        }
+        return pageScope;
+    }
+
+    @Override
+    public Map<String, Object> getContext(String scope) {
+        return "page".equals(scope) ? getPageScope() : super.getContext(scope);
+    }
+
+}
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/VelocityScopeMap.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/VelocityScopeMap.java
new file mode 100644
index 0000000..b268fac
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/VelocityScopeMap.java
@@ -0,0 +1,89 @@
+/*
+ * $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.request.velocity;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.tiles.request.collection.ScopeMap;
+import org.apache.tiles.request.velocity.extractor.VelocityScopeExtractor;
+import org.apache.velocity.context.Context;
+
+/**
+ * <p>Private implementation of <code>Map</code> for servlet request
+ * attributes.</p>
+ *
+ * @version $Rev$ $Date$
+ */
+
+final class VelocityScopeMap extends ScopeMap {
+
+    /**
+     * The request object to use.
+     */
+    private Context request = null;
+
+    /**
+     * Constructor.
+     *
+     * @param request The request object to use.
+     */
+    public VelocityScopeMap(Context request) {
+        super(new VelocityScopeExtractor(request));
+        this.request = request;
+    }
+
+    @Override
+    public Object remove(Object key) {
+        return request.remove(key);
+    }
+
+    @Override
+    public Object put(String key, Object value) {
+        return request.put(key, value);
+    }
+
+    /** {@inheritDoc} */
+    public boolean containsKey(Object key) {
+        return request.containsKey(key);
+    }
+
+
+    /** {@inheritDoc} */
+    public boolean isEmpty() {
+        return size() < 1;
+    }
+
+
+    /** {@inheritDoc} */
+    public Set<String> keySet() {
+        Set<String> set = new HashSet<String>();
+        for (Object key : request.getKeys()) {
+            set.add((String) key);
+        }
+        return (set);
+    }
+
+    /** {@inheritDoc} */
+    public int size() {
+        return request.getKeys().length;
+    }
+}
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/VelocityAutotagRuntime.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/VelocityAutotagRuntime.java
new file mode 100644
index 0000000..9d7b419
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/VelocityAutotagRuntime.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.request.velocity.autotag;
+
+import java.io.Writer;
+import java.util.Map;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.autotag.core.runtime.AutotagRuntime;
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.servlet.ServletUtil;
+import org.apache.tiles.request.velocity.VelocityRequest;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.runtime.directive.Directive;
+import org.apache.velocity.runtime.parser.node.ASTBlock;
+import org.apache.velocity.runtime.parser.node.ASTMap;
+import org.apache.velocity.runtime.parser.node.Node;
+import org.apache.velocity.tools.view.ViewContext;
+
+/**
+ * A Runtime for implementing Velocity Directives.
+ */
+public class VelocityAutotagRuntime extends Directive implements AutotagRuntime {
+    private InternalContextAdapter context;
+    private Writer                 writer;
+    private Node                   node;
+    private Map<String, Object>    params;
+
+    /** {@inheritDoc} */
+    @Override
+    public Request createRequest() {
+        ViewContext viewContext = (ViewContext) context.getInternalUserContext();
+        HttpServletRequest request = viewContext.getRequest();
+        HttpServletResponse response = viewContext.getResponse();
+        ServletContext servletContext = viewContext.getServletContext();
+        return VelocityRequest.createVelocityRequest(ServletUtil.getApplicationContext(servletContext),
+                                                     request,
+                                                     response,
+                                                     context,
+                                                     writer);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public ModelBody createModelBody() {
+        ASTBlock block = (ASTBlock) node.jjtGetChild(1);
+        return new VelocityModelBody(context, block, writer);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @SuppressWarnings("unchecked")
+    public Object getParameter(String name, Object defaultValue) {
+        if (params == null) {
+            ASTMap astMap = (ASTMap) node.jjtGetChild(0);
+            params = (Map<String, Object>) astMap.value(context);
+        }
+        Object result = params.get(name);
+        if (result == null) {
+            result = defaultValue;
+        }
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int getType() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean render(InternalContextAdapter context, Writer writer, Node node) {
+        this.context = context;
+        this.writer = writer;
+        this.node = node;
+        return false;
+    }
+
+}
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/VelocityModelBody.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/VelocityModelBody.java
new file mode 100644
index 0000000..6fed188
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/VelocityModelBody.java
@@ -0,0 +1,65 @@
+/*
+ * $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.request.velocity.autotag;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.tiles.autotag.core.runtime.AbstractModelBody;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.runtime.parser.node.ASTBlock;
+
+/**
+ * Body abstraction for a Velocity directive body.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityModelBody extends AbstractModelBody {
+
+    /**
+     * The real body.
+     */
+    private ASTBlock body;
+
+    /**
+     * The Velocity context.
+     */
+    private InternalContextAdapter context;
+
+    /**
+     * Constructor.
+     *
+     * @param context The Velocity context.
+     * @param body The real body.
+     * @param defaultWriter The default writer.
+     */
+    public VelocityModelBody(InternalContextAdapter context, ASTBlock body, Writer defaultWriter) {
+        super(defaultWriter);
+        this.context = context;
+        this.body = body;
+    }
+
+    @Override
+    public void evaluate(Writer writer) throws IOException {
+        body.render(context, writer);
+    }
+
+}
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/VelocityUtil.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/VelocityUtil.java
new file mode 100644
index 0000000..ffdab20
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/VelocityUtil.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.request.velocity.autotag;
+
+import java.util.Map;
+
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.runtime.parser.node.ASTMap;
+import org.apache.velocity.runtime.parser.node.Node;
+
+/**
+ * Utilities for Velocity usage in Tiles.
+ *
+ * @version $Rev$ $Date$
+ */
+public final class VelocityUtil {
+
+    /**
+     * Private constructor to avoid instantiation.
+     */
+    private VelocityUtil() {
+    }
+
+    /**
+     * Extracts the parameters from the directives, by getting the child at
+     * position 0 supposing it is a map.
+     *
+     * @param context The Velocity context.
+     * @param node The node to use.
+     * @return The extracted parameters.
+     */
+    @SuppressWarnings("unchecked")
+    public static Map<String, Object> getParameters(InternalContextAdapter context,
+            Node node) {
+        ASTMap astMap = (ASTMap) node.jjtGetChild(0);
+        Map<String, Object> params = (Map<String, Object>) astMap
+                .value(context);
+        return params;
+    }
+
+    /**
+     * Returns the "value" parameter if it is not null, otherwise returns
+     * "defaultValue".
+     *
+     * @param value The value to return, if it is not null.
+     * @param defaultValue The value to return, if <code>value</code> is null.
+     * @return The value, defaulted if necessary.
+     */
+    public static Object getObject(Object value, Object defaultValue) {
+        if (value == null) {
+            value = defaultValue;
+        }
+        return value;
+    }
+}
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/package-info.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/package-info.java
new file mode 100644
index 0000000..812198c
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/autotag/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $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.
+ */
+/**
+ * Runtime part of Autotag support for Velocity.
+ */
+package org.apache.tiles.request.velocity.autotag;
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/extractor/VelocityScopeExtractor.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/extractor/VelocityScopeExtractor.java
new file mode 100644
index 0000000..e81821d
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/extractor/VelocityScopeExtractor.java
@@ -0,0 +1,103 @@
+/*
+ * $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.request.velocity.extractor;
+
+import java.util.Enumeration;
+
+import org.apache.tiles.request.attribute.AttributeExtractor;
+import org.apache.velocity.context.Context;
+
+/**
+ * Extracts attributes from Velocity context..
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityScopeExtractor implements AttributeExtractor {
+
+    /**
+     * The Velocity context.
+     */
+    private Context context;
+
+    /**
+     * Constructor.
+     *
+     * @param context The Velocity context.
+     */
+    public VelocityScopeExtractor(Context context) {
+        this.context = context;
+    }
+
+    @Override
+    public void removeValue(String name) {
+        context.remove(name);
+    }
+
+    @Override
+    public Enumeration<String> getKeys() {
+        return new KeyEnumeration(context.getKeys());
+    }
+
+    @Override
+    public Object getValue(String key) {
+        return context.get(key);
+    }
+
+    @Override
+    public void setValue(String key, Object value) {
+        context.put(key, value);
+    }
+
+    /**
+     * Enumerates an array.
+     */
+    private static class KeyEnumeration implements Enumeration<String> {
+
+        /**
+         * The current index.
+         */
+        private int index = 0;
+
+        /**
+         * The array to enumerate.
+         */
+        private Object[] keys;
+
+        /**
+         * Constructor.
+         *
+         * @param keys The array to enumerate.
+         */
+        public KeyEnumeration(Object[] keys) {
+            this.keys = keys;
+        }
+
+        @Override
+        public boolean hasMoreElements() {
+            return index < keys.length;
+        }
+
+        @Override
+        public String nextElement() {
+            return (String) keys[index++];
+        }
+    }
+}
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/extractor/package-info.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/extractor/package-info.java
new file mode 100644
index 0000000..bed6df2
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/extractor/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Extractors to get info about Velocity-specific objects.
+ */
+package org.apache.tiles.request.velocity.extractor;
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/package-info.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/package-info.java
new file mode 100644
index 0000000..54b7c71
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Tiles request support for Velocity.
+ */
+package org.apache.tiles.request.velocity;
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/ApplicationContextJeeConfig.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/ApplicationContextJeeConfig.java
new file mode 100644
index 0000000..b967948
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/ApplicationContextJeeConfig.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.request.velocity.render;
+
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.servlet.ServletUtil;
+import org.apache.velocity.tools.view.JeeConfig;
+
+/**
+ * Implements JeeConfig to use parameters set through
+ * {@link VelocityRenderer#setParameter(String, String)}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ApplicationContextJeeConfig implements JeeConfig {
+
+    /**
+     * The application context.
+     */
+    private ApplicationContext applicationContext;
+
+    /**
+     * The initialization parameters for VelocityView.
+     */
+    private Map<String, String> params;
+
+    /**
+     * Constructor.
+     *
+     * @param applicationContext The application context.
+     * @param params Configuration parameters.
+     */
+    public ApplicationContextJeeConfig(ApplicationContext applicationContext, Map<String, String> params) {
+        this.applicationContext = applicationContext;
+        this.params = new HashMap<String, String>(params);
+    }
+
+    /** {@inheritDoc} */
+    public String getInitParameter(String name) {
+        return params.get(name);
+    }
+
+    /** {@inheritDoc} */
+    public String findInitParameter(String key) {
+        return params.get(key);
+    }
+
+    /** {@inheritDoc} */
+    public Enumeration<String> getInitParameterNames() {
+        return Collections.enumeration(params.keySet());
+    }
+
+    /** {@inheritDoc} */
+    public String getName() {
+        return "Application Context JEE Config";
+    }
+
+    /** {@inheritDoc} */
+    public ServletContext getServletContext() {
+        return ServletUtil.getServletContext(applicationContext);
+    }
+}
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/VelocityRenderer.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/VelocityRenderer.java
new file mode 100644
index 0000000..990b5a5
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/VelocityRenderer.java
@@ -0,0 +1,83 @@
+/*
+ * $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.request.velocity.render;
+
+import java.io.IOException;
+
+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.servlet.ServletRequest;
+import org.apache.tiles.request.servlet.ServletUtil;
+import org.apache.velocity.Template;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.tools.view.VelocityView;
+
+/**
+ * Attribute renderer for rendering Velocity templates as attributes. <br>
+ * It is available only to Servlet-based environment.<br>
+ * It uses {@link VelocityView} to render the response.<br>
+ * To initialize it correctly, call {@link #setParameter(String, String)} for
+ * all the parameters that you want to set, and then call {@link #commit()}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityRenderer implements Renderer {
+
+    /**
+     * The VelocityView object to use.
+     */
+    private VelocityView velocityView;
+
+    /**
+     * Constructor.
+     *
+     * @param velocityView The Velocity view manager.
+     */
+    public VelocityRenderer(VelocityView velocityView) {
+        this.velocityView = velocityView;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void render(String path, Request request) throws IOException {
+        if (path == null) {
+            throw new CannotRenderException("Cannot dispatch a null path");
+        }
+
+        ServletRequest servletRequest = ServletUtil.getServletRequest(request);
+        // then get a context
+        Context context = velocityView.createContext(servletRequest
+                .getRequest(), servletRequest.getResponse());
+
+        // get the template
+        Template template = velocityView.getTemplate((String) path);
+
+        // merge the template and context into the writer
+        velocityView.merge(template, context, request.getWriter());
+    }
+
+    /** {@inheritDoc} */
+    public boolean isRenderable(String path, Request request) {
+        return path != null && path.startsWith("/") && path.endsWith(".vm");
+    }
+}
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/VelocityRendererBuilder.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/VelocityRendererBuilder.java
new file mode 100644
index 0000000..b041af2
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/VelocityRendererBuilder.java
@@ -0,0 +1,95 @@
+/*
+ * $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.request.velocity.render;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.velocity.tools.view.VelocityView;
+
+/**
+ * Builds a {@link VelocityRenderer}.
+ *
+ * @version $Rev$ $Date$
+ */
+public final class VelocityRendererBuilder {
+
+    /**
+     * The initialization parameters for VelocityView.
+     */
+    private Map<String, String> params = new HashMap<String, String>();
+
+    /**
+     * The application context.
+     */
+    private ApplicationContext applicationContext;
+
+    /**
+     * Constructor.
+     */
+    private VelocityRendererBuilder() {
+    }
+
+    /**
+     * Returns a new instance of the builder.
+     *
+     * @return A new builder.
+     */
+    public static VelocityRendererBuilder createInstance() {
+        return new VelocityRendererBuilder();
+    }
+
+    /**
+     * Sets a parameter for the internal servlet.
+     *
+     * @param key The name of the parameter.
+     * @param value The value of the parameter.
+     * @return This builder.
+     */
+    public VelocityRendererBuilder setParameter(String key, String value) {
+        params.put(key, value);
+        return this;
+    }
+
+    /**
+     * Sets the application context.
+     *
+     * @param applicationContext The application context.
+     * @return This builder.
+     */
+    public VelocityRendererBuilder setApplicationContext(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
+        return this;
+    }
+
+    /**
+     * Creates the Velocity renderer.
+     *
+     * @return The Velocity renderer.
+     */
+    public VelocityRenderer build() {
+        VelocityView velocityView = new VelocityView(
+                new ApplicationContextJeeConfig(applicationContext, params));
+        return new VelocityRenderer(velocityView);
+    }
+}
diff --git a/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/package-info.java b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/package-info.java
new file mode 100644
index 0000000..1b2877f
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/main/java/org/apache/tiles/request/velocity/render/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * $Id: package-info.java 1049711 2010-12-15 21:12:00Z apetrelli $
+ *
+ * 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.
+ */
+/**
+ * Renderering support for Velocity.
+ */
+package org.apache.tiles.request.velocity.render;
diff --git a/tiles-request/tiles-request-velocity/src/site/site.xml b/tiles-request/tiles-request-velocity/src/site/site.xml
new file mode 100644
index 0000000..8e92dd7
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/site/site.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+/*
+ * $Id: site.xml 1081442 2011-03-14 16:21:08Z apetrelli $
+ *
+ * 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.
+ */
+-->
+<project name="Apache - Request Microframework">
+    <bannerLeft>
+        <name>Apache Software Foundation</name>
+        <src>http://www.apache.org/images/asf-logo.gif</src>
+        <href>http://www.apache.org</href>
+    </bannerLeft>
+    <bannerRight>
+        <name>Apache Tiles&#8482;</name>
+        <src>http://tiles.apache.org/images/logo.png</src>
+        <href>http://tiles.apache.org</href>
+    </bannerRight>
+    <body>
+
+        <links>
+            <item name="Apache" href="http://www.apache.org" />
+            <item name="Tiles" href="http://tiles.apache.org" />
+        </links>
+
+
+        <menu name="Apache Tiles&#8482;">
+            <item
+                   name="Tiles Home"
+                   href="../../index.html"/>
+            <item
+                   name="Request Microframework"
+                   href="../index.html"/>
+        </menu>
+
+        <menu ref="modules" />
+        <menu ref="reports" />
+
+    </body>
+</project>
diff --git a/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/VelocityRequestTest.java b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/VelocityRequestTest.java
new file mode 100644
index 0000000..1bc3f91
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/VelocityRequestTest.java
@@ -0,0 +1,273 @@
+/*
+ * $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.request.velocity;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collections;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.DispatchRequest;
+import org.apache.tiles.request.servlet.ExternalWriterHttpServletResponse;
+import org.apache.tiles.request.servlet.ServletRequest;
+import org.apache.velocity.context.Context;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link VelocityRequest}.
+ */
+public class VelocityRequestTest {
+
+    /**
+     * The request context to test.
+     */
+    private VelocityRequest context;
+
+    /**
+     * The Velocity context.
+     */
+    private Context velocityContext;
+
+    /**
+     * A string writer.
+     */
+    private StringWriter writer;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        velocityContext = createMock(Context.class);
+        writer = new StringWriter();
+    }
+
+    /**
+     * Tests {@link VelocityRequest
+     * #createVelocityRequest(ApplicationContext, HttpServletRequest, HttpServletResponse, Context, Writer)}.
+     */
+    @Test
+    public void testCreateVelocityRequest() {
+        HttpServletRequest httpRequest = createMock(HttpServletRequest.class);
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+
+        replay(velocityContext, httpRequest, response, applicationContext);
+        context = VelocityRequest.createVelocityRequest(applicationContext,
+                httpRequest, response, velocityContext, writer);
+        ServletRequest servletRequest = (ServletRequest) context.getWrappedRequest();
+        assertEquals(httpRequest, servletRequest.getRequest());
+        assertEquals(response, servletRequest.getResponse());
+        verify(velocityContext, httpRequest, response, applicationContext);
+    }
+
+    /**
+      * Tests {@link FreemarkerRequest#getNativeScopes()}.
+      */
+    @Test
+    public void testGetAvailableScopes() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+        replay(enclosedRequest);
+        context = new VelocityRequest(enclosedRequest, velocityContext, writer);
+        assertArrayEquals(new String[] {"parent", "page"}, context.getAvailableScopes().toArray());
+        verify(enclosedRequest);
+    }
+
+    /**
+     * Tests {@link VelocityRequest#doInclude(String)}.
+     *
+     * @throws IOException If something goes wrong.
+     * @throws ServletException If something goes wrong.
+     */
+    @Test
+    public void testDoInclude() throws IOException, ServletException {
+        String path = "this way";
+        ServletRequest enclosedRequest = createMock(ServletRequest.class);
+        HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        RequestDispatcher dispatcher = createMock(RequestDispatcher.class);
+
+        expect(servletRequest.getRequestDispatcher("this way")).andReturn(dispatcher);
+        dispatcher.include(eq(servletRequest), isA(ExternalWriterHttpServletResponse.class));
+        replay(servletRequest, response, dispatcher);
+
+        expect(enclosedRequest.getRequest()).andReturn(servletRequest);
+        expect(enclosedRequest.getResponse()).andReturn(response);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+
+        replay(velocityContext, enclosedRequest);
+        context = new VelocityRequest(enclosedRequest, velocityContext, writer);
+        context.doInclude(path);
+        verify(velocityContext, enclosedRequest, servletRequest, response, dispatcher);
+    }
+
+    /**
+     * Tests {@link VelocityRequest#doInclude(String)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testDoIncludeNoRequestDispatcher() throws IOException {
+        String path = "this way";
+        ServletRequest enclosedRequest = createMock(ServletRequest.class);
+        HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+
+        expect(servletRequest.getRequestDispatcher("this way")).andReturn(null);
+        replay(servletRequest, response);
+
+        expect(enclosedRequest.getRequest()).andReturn(servletRequest);
+        expect(enclosedRequest.getResponse()).andReturn(response);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+
+        replay(velocityContext, enclosedRequest);
+        context = new VelocityRequest(enclosedRequest, velocityContext, writer);
+        context.doInclude(path);
+        verify(velocityContext, enclosedRequest, servletRequest, response);
+    }
+
+    /**
+     * Tests {@link VelocityRequest#doInclude(String)}.
+     *
+     * @throws IOException If something goes wrong.
+     * @throws ServletException If something goes wrong.
+     */
+    @Test(expected = IOException.class)
+    public void testDoIncludeServletException() throws IOException, ServletException {
+        String path = "this way";
+        ServletRequest enclosedRequest = createMock(ServletRequest.class);
+        HttpServletRequest servletRequest = createMock(HttpServletRequest.class);
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        RequestDispatcher dispatcher = createMock(RequestDispatcher.class);
+
+        expect(servletRequest.getRequestDispatcher("this way")).andReturn(dispatcher);
+        dispatcher.include(eq(servletRequest), isA(ExternalWriterHttpServletResponse.class));
+        expectLastCall().andThrow(new ServletException());
+        replay(servletRequest, response, dispatcher);
+
+        expect(enclosedRequest.getRequest()).andReturn(servletRequest);
+        expect(enclosedRequest.getResponse()).andReturn(response);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+
+        replay(velocityContext, enclosedRequest);
+        context = new VelocityRequest(enclosedRequest, velocityContext, writer);
+        context.doInclude(path);
+        verify(velocityContext, enclosedRequest, servletRequest, response, dispatcher);
+    }
+
+    /**
+     * Tests {@link VelocityRequest#getPrintWriter()}.
+     */
+    @Test
+    public void testGetPrintWriter() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+
+        replay(velocityContext, enclosedRequest);
+        context = new VelocityRequest(enclosedRequest, velocityContext, writer);
+        assertNotNull(context.getPrintWriter());
+        verify(velocityContext, enclosedRequest);
+    }
+
+    /**
+     * Tests {@link VelocityRequest#getPrintWriter()}.
+     */
+    @Test
+    public void testGetPrintWriterPrintWriter() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+
+        PrintWriter printWriter = new PrintWriter(writer);
+        replay(velocityContext, enclosedRequest);
+        context = new VelocityRequest(enclosedRequest, velocityContext, printWriter);
+        assertEquals(printWriter, context.getPrintWriter());
+        verify(velocityContext, enclosedRequest);
+    }
+
+    /**
+     * Tests {@link VelocityRequest#getPrintWriter()}.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testGetPrintWriterNoWriter() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+
+        replay(velocityContext, enclosedRequest);
+        context = new VelocityRequest(enclosedRequest, velocityContext, null);
+        context.getPrintWriter();
+        verify(velocityContext, enclosedRequest);
+    }
+
+    /**
+     * Tests {@link VelocityRequest#getWriter()}.
+     */
+    @Test
+    public void testGetWriter() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+
+        replay(velocityContext, enclosedRequest);
+        context = new VelocityRequest(enclosedRequest, velocityContext, writer);
+        assertEquals(writer, context.getWriter());
+        verify(velocityContext, enclosedRequest);
+    }
+
+    /**
+     * Tests {@link VelocityRequest#getWriter()}.
+     */
+    @Test(expected = IllegalStateException.class)
+    public void testGetWriterNoWriter() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+
+        replay(velocityContext, enclosedRequest);
+        context = new VelocityRequest(enclosedRequest, velocityContext, null);
+        context.getWriter();
+        verify(velocityContext, enclosedRequest);
+    }
+
+    /**
+     * Tests {@link VelocityRequest#getPageScope()}.
+     */
+    @Test
+    public void testGetPageScope() {
+        DispatchRequest enclosedRequest = createMock(DispatchRequest.class);
+        expect(enclosedRequest.getAvailableScopes()).andReturn(Collections.singletonList("parent"));
+
+        replay(velocityContext, enclosedRequest);
+        context = new VelocityRequest(enclosedRequest, velocityContext, writer);
+        assertTrue(context.getPageScope() instanceof VelocityScopeMap);
+        verify(velocityContext, enclosedRequest);
+    }
+}
diff --git a/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/VelocityScopeMapTest.java b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/VelocityScopeMapTest.java
new file mode 100644
index 0000000..4f64306
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/VelocityScopeMapTest.java
@@ -0,0 +1,144 @@
+/*
+ * $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.request.velocity;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Set;
+
+import org.apache.velocity.context.Context;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link VelocityScopeMap}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityScopeMapTest {
+
+    /**
+     * The Velocity context.
+     */
+    private Context request;
+
+    /**
+     * The map to test.
+     */
+    private VelocityScopeMap map;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(Context.class);
+        map = new VelocityScopeMap(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.velocity.VelocityScopeMap#containsKey(java.lang.Object)}.
+     */
+    @Test
+    public void testContainsKey() {
+        expect(request.containsKey("key")).andReturn(true);
+
+        replay(request);
+        assertTrue(map.containsKey("key"));
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.velocity.VelocityScopeMap#isEmpty()}.
+     */
+    @Test
+    public void testIsEmpty() {
+        expect(request.getKeys()).andReturn(new Object[0]);
+
+        replay(request);
+        assertTrue(map.isEmpty());
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.velocity.VelocityScopeMap#isEmpty()}.
+     */
+    @Test
+    public void testIsEmptyFalse() {
+        expect(request.getKeys()).andReturn(new Object[] {"one", "two"});
+
+        replay(request);
+        assertFalse(map.isEmpty());
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.velocity.VelocityScopeMap#keySet()}.
+     */
+    @Test
+    public void testKeySet() {
+        expect(request.getKeys()).andReturn(new Object[] {"one", "two"});
+
+        replay(request);
+        Set<String> set = map.keySet();
+        assertEquals(2, set.size());
+        assertTrue(set.contains("one"));
+        assertTrue(set.contains("two"));
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.velocity.VelocityScopeMap#size()}.
+     */
+    @Test
+    public void testSize() {
+        expect(request.getKeys()).andReturn(new Object[] {"one", "two"});
+
+        replay(request);
+        assertEquals(2, map.size());
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link VelocityScopeMap#put(String, Object)}.
+     */
+    @Test
+    public void testPutStringObject() {
+        expect(request.put("key", "value")).andReturn("oldValue");
+
+        replay(request);
+        assertEquals("oldValue", map.put("key", "value"));
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link org.apache.tiles.request.velocity.VelocityScopeMap#remove(java.lang.Object)}.
+     */
+    @Test
+    public void testRemoveObject() {
+        expect(request.remove("key")).andReturn("value");
+
+        replay(request);
+        assertEquals("value", map.remove("key"));
+        verify(request);
+    }
+}
diff --git a/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/autotag/VelocityAutotagRuntimeTest.java b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/autotag/VelocityAutotagRuntimeTest.java
new file mode 100644
index 0000000..526c55d
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/autotag/VelocityAutotagRuntimeTest.java
@@ -0,0 +1,110 @@
+/*
+ * $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.request.velocity.autotag;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+import java.io.Writer;
+import java.util.Map;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.tiles.autotag.core.runtime.ModelBody;
+import org.apache.tiles.request.ApplicationAccess;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.Request;
+import org.apache.tiles.request.velocity.VelocityRequest;
+import org.apache.tiles.request.velocity.autotag.VelocityAutotagRuntime;
+import org.apache.tiles.request.velocity.autotag.VelocityModelBody;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.runtime.parser.node.ASTBlock;
+import org.apache.velocity.runtime.parser.node.ASTMap;
+import org.apache.velocity.runtime.parser.node.Node;
+import org.apache.velocity.tools.view.ViewToolContext;
+import org.junit.Test;
+
+public class VelocityAutotagRuntimeTest {
+    @Test
+    public void testCreateRequest() {
+        InternalContextAdapter context = createMock(InternalContextAdapter.class);
+        Writer writer = createMock(Writer.class);
+        Node node = createMock(Node.class);
+        ViewToolContext viewContext = createMock(ViewToolContext.class);
+        HttpServletRequest request = createMock(HttpServletRequest.class);
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        ServletContext servletContext = createMock(ServletContext.class);
+        ApplicationContext applicationContext = createMock(ApplicationContext.class);
+
+        expect(context.getInternalUserContext()).andReturn(viewContext);
+        expect(viewContext.getRequest()).andReturn(request);
+        expect(viewContext.getResponse()).andReturn(response);
+        expect(viewContext.getServletContext()).andReturn(servletContext);
+        expect(servletContext.getAttribute(ApplicationAccess.APPLICATION_CONTEXT_ATTRIBUTE)).andReturn(applicationContext);
+
+        replay(context, writer, node, viewContext, request, response, servletContext, applicationContext);
+        VelocityAutotagRuntime runtime = new VelocityAutotagRuntime();
+        runtime.render(context, writer, node);
+        Request velocityRequest = runtime.createRequest();
+        assertTrue(velocityRequest instanceof VelocityRequest);
+        verify(context, writer, node, viewContext, request, response, servletContext, applicationContext);
+    }
+
+    @Test
+    public void testCreateModelBody() {
+        InternalContextAdapter context = createMock(InternalContextAdapter.class);
+        Writer writer = createMock(Writer.class);
+        Node node = createMock(Node.class);
+        ASTBlock block = createMock(ASTBlock.class);
+        expect(node.jjtGetChild(1)).andReturn(block);
+        replay(context, writer, node, block);
+        VelocityAutotagRuntime runtime = new VelocityAutotagRuntime();
+        runtime.render(context, writer, node);
+        ModelBody modelBody = runtime.createModelBody();
+        assertTrue(modelBody instanceof VelocityModelBody);
+        verify(context, writer, node, block);
+    }
+
+    @Test
+    public void testGetParameter() {
+        InternalContextAdapter context = createMock(InternalContextAdapter.class);
+        Writer writer = createMock(Writer.class);
+        Node node = createMock(Node.class);
+        ASTMap astMap = createMock(ASTMap.class);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> params = createMock(Map.class);
+        expect(node.jjtGetChild(0)).andReturn(astMap);
+        expect(astMap.value(context)).andReturn(params);
+        expect(params.get(eq("notnullParam"))).andReturn(new Integer(42)).anyTimes();
+        expect(params.get(eq("nullParam"))).andReturn(null).anyTimes();
+        replay(context, writer, node, astMap, params);
+        VelocityAutotagRuntime runtime = new VelocityAutotagRuntime();
+        runtime.render(context, writer, node);
+        Object notnullParam = runtime.getParameter("notnullParam", null);
+        Object nullParam = runtime.getParameter("nullParam", null);
+        Object notnullParamDefault = runtime.getParameter("notnullParam", new Integer(24));
+        Object nullParamDefault = runtime.getParameter("nullParam", new Integer(24));
+        assertEquals(42, notnullParam);
+        assertEquals(null, nullParam);
+        assertEquals(42, notnullParamDefault);
+        assertEquals(24, nullParamDefault);
+        verify(context, writer, node, astMap, params);
+    }
+}
diff --git a/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/autotag/VelocityModelBodyTest.java b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/autotag/VelocityModelBodyTest.java
new file mode 100644
index 0000000..bca5efd
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/autotag/VelocityModelBodyTest.java
@@ -0,0 +1,60 @@
+/*
+ * $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.request.velocity.autotag;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.tiles.request.velocity.autotag.VelocityModelBody;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.runtime.parser.node.ASTBlock;
+import org.junit.Test;
+
+/**
+ * Tests {@link VelocityModelBody}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityModelBodyTest {
+
+    /**
+     * Test method for {@link org.apache.tiles.request.velocity.autotag.VelocityModelBody#evaluate(java.io.Writer)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testEvaluateWriter() throws IOException {
+        InternalContextAdapter internalContextAdapter = createMock(InternalContextAdapter.class);
+        ASTBlock body = createMock(ASTBlock.class);
+        Writer writer = createMock(Writer.class);
+        expect(body.render(internalContextAdapter, writer)).andReturn(true);
+
+        replay(internalContextAdapter, body, writer);
+        VelocityModelBody modelBody = createMockBuilder(VelocityModelBody.class)
+                .withConstructor(internalContextAdapter, body, writer)
+                .createMock();
+        replay(modelBody);
+        modelBody.evaluate(writer);
+        verify(internalContextAdapter, body, writer, modelBody);
+    }
+
+}
diff --git a/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/autotag/VelocityUtilTest.java b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/autotag/VelocityUtilTest.java
new file mode 100644
index 0000000..633ed9c
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/autotag/VelocityUtilTest.java
@@ -0,0 +1,71 @@
+/*
+ * $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.request.velocity.autotag;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Map;
+
+import org.apache.tiles.request.velocity.autotag.VelocityUtil;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.runtime.parser.node.ASTMap;
+import org.apache.velocity.runtime.parser.node.Node;
+import org.junit.Test;
+
+/**
+ * Tests {@link VelocityUtil}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityUtilTest {
+
+    /**
+     * Test method for {@link VelocityUtil#getParameters(InternalContextAdapter, Node)}.
+     */
+    @Test
+    public void testGetParameters() {
+        InternalContextAdapter context = createMock(InternalContextAdapter.class);
+        Node node = createMock(Node.class);
+        ASTMap astMap = createMock(ASTMap.class);
+        @SuppressWarnings("unchecked")
+        Map<String, Object> params = createMock(Map.class);
+
+        expect(node.jjtGetChild(0)).andReturn(astMap);
+        expect(astMap.value(context)).andReturn(params);
+
+        replay(context, node, astMap, params);
+        assertSame(params, VelocityUtil.getParameters(context, node));
+        verify(context, node, astMap, params);
+    }
+
+    /**
+     * Test method for {@link VelocityUtil#getObject(Object, Object)}.
+     */
+    @Test
+    public void testGetObject() {
+        assertEquals(new Integer(1), VelocityUtil.getObject(new Integer(1), new Integer(2)));
+        assertEquals(new Integer(1), VelocityUtil.getObject(new Integer(1), null));
+        assertEquals(new Integer(2), VelocityUtil.getObject(null, new Integer(2)));
+        assertNull(VelocityUtil.getObject(null, null));
+    }
+
+}
diff --git a/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/extractor/VelocityScopeExtractorTest.java b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/extractor/VelocityScopeExtractorTest.java
new file mode 100644
index 0000000..4a86fe8
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/extractor/VelocityScopeExtractorTest.java
@@ -0,0 +1,110 @@
+/*
+ * $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.request.velocity.extractor;
+
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+
+import org.apache.velocity.context.Context;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link VelocityScopeExtractor}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityScopeExtractorTest {
+
+    /**
+     * The Velocity context.
+     */
+    private Context request;
+
+    /**
+     * The extractor to test.
+     */
+    private VelocityScopeExtractor extractor;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        request = createMock(Context.class);
+        extractor = new VelocityScopeExtractor(request);
+    }
+
+    /**
+     * Test method for {@link VelocityScopeExtractor#removeValue(java.lang.String)}.
+     */
+    @Test
+    public void testRemoveValue() {
+        expect(request.remove("key")).andReturn("value");
+
+        replay(request);
+        extractor.removeValue("key");
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link VelocityScopeExtractor#getKeys()}.
+     */
+    @Test
+    public void testGetKeys() {
+        expect(request.getKeys()).andReturn(new Object[] {"one", "two"});
+
+        replay(request);
+        Enumeration<String> keys = extractor.getKeys();
+        assertTrue(keys.hasMoreElements());
+        assertEquals("one", keys.nextElement());
+        assertTrue(keys.hasMoreElements());
+        assertEquals("two", keys.nextElement());
+        assertFalse(keys.hasMoreElements());
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link VelocityScopeExtractor#getValue(java.lang.String)}.
+     */
+    @Test
+    public void testGetValue() {
+        expect(request.get("key")).andReturn("value");
+
+        replay(request);
+        assertEquals("value", extractor.getValue("key"));
+        verify(request);
+    }
+
+    /**
+     * Test method for {@link VelocityScopeExtractor#setValue(java.lang.String, java.lang.Object)}.
+     */
+    @Test
+    public void testSetValue() {
+        expect(request.put("key", "value")).andReturn(null);
+
+        replay(request);
+        extractor.setValue("key", "value");
+        verify(request);
+    }
+}
diff --git a/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/render/ApplicationContextJeeConfigTest.java b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/render/ApplicationContextJeeConfigTest.java
new file mode 100644
index 0000000..036e6e5
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/render/ApplicationContextJeeConfigTest.java
@@ -0,0 +1,145 @@
+/*
+ * $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.request.velocity.render;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.apache.tiles.request.servlet.ServletApplicationContext;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests {@link ApplicationContextJeeConfig}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class ApplicationContextJeeConfigTest {
+
+    /**
+     * The configuration to test.
+     */
+    private ApplicationContextJeeConfig config;
+
+    /**
+     * The application context.
+     */
+    private ServletApplicationContext applicationContext;
+
+    /**
+     * The servlet context.
+     */
+    private ServletContext servletContext;
+
+    /**
+     * Custom parameters.
+     */
+    private Map<String, String> params;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        servletContext = createMock(ServletContext.class);
+        applicationContext = new ServletApplicationContext(servletContext);
+    }
+
+    /**
+     * Tears down the test.
+     */
+    @After
+    public void tearDown() {
+        verify(servletContext);
+    }
+
+    /**
+     * Tests {@link ApplicationContextJeeConfig#getInitParameter(String)}.
+     */
+    @Test
+    public void testGetInitParameter() {
+        params = new HashMap<String, String>();
+        params.put("one", "value1");
+        config = new ApplicationContextJeeConfig(applicationContext, params);
+        replay(servletContext);
+        assertEquals("value1", config.getInitParameter("one"));
+    }
+
+    /**
+     * Tests {@link ApplicationContextJeeConfig#findInitParameter(String)}.
+     */
+    @Test
+    public void testFindInitParameter() {
+        params = new HashMap<String, String>();
+        params.put("one", "value1");
+        config = new ApplicationContextJeeConfig(applicationContext, params);
+        replay(servletContext);
+        assertEquals("value1", config.findInitParameter("one"));
+    }
+
+    /**
+     * Tests {@link ApplicationContextJeeConfig#getInitParameterNames()}.
+     */
+    @Test
+    public void testGetInitParameterNames() {
+        params = new HashMap<String, String>();
+        params.put("one", "value1");
+        config = new ApplicationContextJeeConfig(applicationContext, params);
+        replay(servletContext);
+        @SuppressWarnings("unchecked")
+        Enumeration<String> names = config.getInitParameterNames();
+        assertTrue(names.hasMoreElements());
+        assertEquals("one", names.nextElement());
+        assertFalse(names.hasMoreElements());
+    }
+
+    /**
+     * Tests {@link ApplicationContextJeeConfig#getName()}.
+     */
+    @Test
+    public void testGetName() {
+        params = new HashMap<String, String>();
+        params.put("one", "value1");
+        config = new ApplicationContextJeeConfig(applicationContext, params);
+        replay(servletContext);
+        assertEquals("Application Context JEE Config", config.getName());
+    }
+
+    /**
+     * Tests {@link ApplicationContextJeeConfig#getServletContext()}.
+     */
+    @Test
+    public void testGetServletContext() {
+        params = new HashMap<String, String>();
+        params.put("one", "value1");
+        config = new ApplicationContextJeeConfig(applicationContext, params);
+        replay(servletContext);
+        assertEquals(servletContext, config.getServletContext());
+    }
+
+}
diff --git a/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/render/VelocityRendererTest.java b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/render/VelocityRendererTest.java
new file mode 100644
index 0000000..9a07157
--- /dev/null
+++ b/tiles-request/tiles-request-velocity/src/test/java/org/apache/tiles/request/velocity/render/VelocityRendererTest.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.request.velocity.render;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.classextension.EasyMock.*;
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tiles.request.render.CannotRenderException;
+import org.apache.tiles.request.render.Renderer;
+import org.apache.tiles.request.servlet.ServletRequest;
+import org.apache.velocity.Template;
+import org.apache.velocity.tools.view.VelocityView;
+import org.apache.velocity.tools.view.ViewToolContext;
+import org.junit.Test;
+
+/**
+ * Tests {@link VelocityRenderer}.
+ *
+ * @version $Rev$ $Date$
+ */
+public class VelocityRendererTest {
+
+    /**
+     * Tests {@link VelocityRenderer#render(String, org.apache.tiles.request.Request)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testRender() throws IOException {
+        VelocityView view = createMock(VelocityView.class);
+        ServletRequest request = createMock(ServletRequest.class);
+        HttpServletRequest httpRequest = createMock(HttpServletRequest.class);
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        ViewToolContext context = createMock(ViewToolContext.class);
+        Template template = createMock(Template.class);
+        Writer writer = createMock(Writer.class);
+
+        expect(request.getRequest()).andReturn(httpRequest);
+        expect(request.getResponse()).andReturn(response);
+        expect(view.createContext(httpRequest, response)).andReturn(context);
+        expect(view.getTemplate("/test.vm")).andReturn(template);
+        expect(request.getWriter()).andReturn(writer);
+        view.merge(template, context, writer);
+
+        replay(view, request, httpRequest, response, context, template, writer);
+        Renderer renderer = new VelocityRenderer(view);
+        renderer.render("/test.vm", request);
+        verify(view, request, httpRequest, response, context, template, writer);
+    }
+
+    /**
+     * Tests {@link VelocityRenderer#render(String, org.apache.tiles.request.Request)}.
+     * @throws IOException If something goes wrong.
+     */
+    @Test(expected = CannotRenderException.class)
+    public void testRenderException() throws IOException {
+        VelocityView view = createMock(VelocityView.class);
+        ServletRequest request = createMock(ServletRequest.class);
+
+        replay(view, request);
+        Renderer renderer = new VelocityRenderer(view);
+        try {
+            renderer.render(null, request);
+        } finally {
+            verify(view, request);
+        }
+    }
+
+    /**
+     * Test method for
+     * {@link VelocityRenderer#isRenderable(String, org.apache.tiles.request.Request)}
+     * .
+     */
+    @Test
+    public void testIsRenderable() {
+        VelocityView view = createMock(VelocityView.class);
+        replay(view);
+        Renderer renderer = new VelocityRenderer(view);
+        assertTrue(renderer.isRenderable("/my/template.vm", null));
+        assertFalse(renderer.isRenderable("my/template.vm", null));
+        assertFalse(renderer.isRenderable("/my/template.jsp", null));
+        verify(view);
+    }
+
+}