net.officefloor.plugin.web.http.application.WebApplicationAutoWireOfficeFloorSourceTest.java Source code

Java tutorial

Introduction

Here is the source code for net.officefloor.plugin.web.http.application.WebApplicationAutoWireOfficeFloorSourceTest.java

Source

/*
 * OfficeFloor - http://www.officefloor.net
 * Copyright (C) 2005-2013 Daniel Sagenschneider
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.officefloor.plugin.web.http.application;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.io.Writer;
import java.net.URI;
import java.nio.charset.Charset;
import java.sql.SQLException;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;

import net.officefloor.autowire.AutoWire;
import net.officefloor.autowire.AutoWireManagement;
import net.officefloor.autowire.AutoWireSection;
import net.officefloor.autowire.ManagedObjectSourceWirer;
import net.officefloor.autowire.ManagedObjectSourceWirerContext;
import net.officefloor.compile.OfficeFloorCompiler;
import net.officefloor.compile.impl.structure.OfficeFloorNodeImpl;
import net.officefloor.compile.spi.office.OfficeSectionInput;
import net.officefloor.compile.spi.office.OfficeSectionOutput;
import net.officefloor.compile.test.issues.MockCompilerIssues;
import net.officefloor.frame.api.escalate.Escalation;
import net.officefloor.frame.api.manage.OfficeFloor;
import net.officefloor.frame.internal.structure.ManagedObjectScope;
import net.officefloor.frame.internal.structure.ProcessState;
import net.officefloor.frame.test.OfficeFrameTestCase;
import net.officefloor.plugin.section.clazz.ClassSectionSource;
import net.officefloor.plugin.section.clazz.ManagedObject;
import net.officefloor.plugin.section.clazz.NextTask;
import net.officefloor.plugin.socket.server.http.HttpTestUtil;
import net.officefloor.plugin.socket.server.http.ServerHttpConnection;
import net.officefloor.plugin.socket.server.http.source.HttpServerSocketManagedObjectSource;
import net.officefloor.plugin.socket.server.http.source.HttpsServerSocketManagedObjectSource;
import net.officefloor.plugin.socket.server.impl.AbstractServerSocketManagedObjectSource;
import net.officefloor.plugin.web.http.location.HttpApplicationLocationManagedObjectSource;
import net.officefloor.plugin.web.http.resource.source.SourceHttpResourceFactory;
import net.officefloor.plugin.web.http.route.HttpRouteTask;
import net.officefloor.plugin.web.http.session.HttpSession;
import net.officefloor.plugin.web.http.session.HttpSessionManagedObjectSource;
import net.officefloor.plugin.web.http.template.parse.HttpTemplate;
import net.officefloor.plugin.web.http.template.section.HttpTemplateSectionExtension;
import net.officefloor.plugin.web.http.template.section.HttpTemplateSectionExtensionContext;
import net.officefloor.plugin.web.http.template.section.HttpTemplateSectionSource;
import net.officefloor.plugin.work.clazz.FlowInterface;

/**
 * Tests the {@link WebApplicationAutoWireOfficeFloorSource}.
 * 
 * @author Daniel Sagenschneider
 */
public class WebApplicationAutoWireOfficeFloorSourceTest extends OfficeFrameTestCase {

    /**
     * Binds the {@link ManagedObject} to the {@link ProcessState}.
     */
    private static final ManagedObjectSourceWirer processScopeWirer = new ManagedObjectSourceWirer() {
        @Override
        public void wire(ManagedObjectSourceWirerContext context) {
            context.setManagedObjectScope(ManagedObjectScope.PROCESS);
        }
    };

    /**
     * Host name for testing.
     */
    private static final String HOST_NAME = HttpApplicationLocationManagedObjectSource.getDefaultHostName();

    /**
     * {@link WebApplicationAutoWireOfficeFloorSource} to be tested.
     */
    private final WebApplicationAutoWireOfficeFloorSource source = new WebApplicationAutoWireOfficeFloorSource();

    /**
     * {@link CloseableHttpClient}.
     */
    private CloseableHttpClient client;

    /**
     * Port on which the web application is to run.
     */
    private int port;

    /**
     * Secure port on which the web application is to be run.
     */
    private int securePort;

    @Override
    protected void setUp() throws Exception {

        // Configure the port
        this.port = HttpTestUtil.getAvailablePort();
        this.source.getOfficeFloorCompiler().addProperty(
                HttpApplicationLocationManagedObjectSource.PROPERTY_HTTP_PORT, String.valueOf(this.port));
        HttpServerSocketManagedObjectSource.autoWire(this.source, this.port,
                WebApplicationAutoWireOfficeFloorSource.HANDLER_SECTION_NAME,
                WebApplicationAutoWireOfficeFloorSource.HANDLER_INPUT_NAME);

        // Configure the secure port
        this.securePort = HttpTestUtil.getAvailablePort();
        this.source.getOfficeFloorCompiler().addProperty(
                HttpApplicationLocationManagedObjectSource.PROPERTY_HTTPS_PORT, String.valueOf(this.securePort));
        HttpsServerSocketManagedObjectSource.autoWire(this.source, this.securePort,
                HttpTestUtil.getSslEngineSourceClass(),
                WebApplicationAutoWireOfficeFloorSource.HANDLER_SECTION_NAME,
                WebApplicationAutoWireOfficeFloorSource.HANDLER_INPUT_NAME);

        // Configure the HTTP Request State and HTTP Session
        this.source.addManagedObject(HttpRequestStateManagedObjectSource.class.getName(), processScopeWirer,
                new AutoWire(HttpRequestState.class));
        this.source.addManagedObject(HttpSessionManagedObjectSource.class.getName(), processScopeWirer,
                new AutoWire(HttpSession.class)).setTimeout(60 * 1000);

        // Configure the client (to not redirect)
        HttpClientBuilder builder = HttpClientBuilder.create();
        HttpTestUtil.configureHttps(builder);
        HttpTestUtil.configureNoRedirects(builder);
        this.client = builder.build();
    }

    @Override
    protected void tearDown() throws Exception {
        try {
            // Stop the client
            this.client.close();
        } finally {
            // Ensure close
            AutoWireManagement.closeAllOfficeFloors();
        }
    }

    /**
     * Ensure able to auto-wire template with no logic class.
     */
    public void testTemplateWithNoLogicClass() throws Exception {
        this.doTemplateWithNoLogicClassTest(false);
    }

    /**
     * Ensure able to auto-wire secure template with no logic class.
     */
    public void testSecureTemplateWithNoLogicClass() throws Exception {
        this.doTemplateWithNoLogicClassTest(true);
    }

    /**
     * Undertakes test to enable to auto-wire template with no logic class.
     */
    public void doTemplateWithNoLogicClassTest(boolean isSecure) throws Exception {

        final String templatePath = this.getClassPath("NoLogicTemplate.ofp");

        // Add HTTP template with no logic class
        HttpTemplateAutoWireSection template = this.source.addHttpTemplate("template", templatePath, null);
        template.setTemplateSecure(isSecure);
        this.source.linkToResource(template, "link", "resource.html");
        this.source.openOfficeFloor();

        // Ensure template available
        this.assertHttpRequest("/template", isSecure, 200, "/template-link");

        // Ensure link connected to resource
        this.assertHttpRequest("/template-link", isSecure, 200, "RESOURCE");
    }

    /**
     * Ensure able to add HTTP template.
     */
    public void testTemplate() throws Exception {
        this.doTemplateTest(false);
    }

    /**
     * Ensure able to add secure HTTP template.
     */
    public void testSecureTemplate() throws Exception {
        this.doTemplateTest(true);
    }

    /**
     * Undertakes test to ensure able to add HTTP template.
     */
    private void doTemplateTest(boolean isSecure) throws Exception {

        final String SUBMIT_URI = "/uri-submit";

        final String templatePath = this.getClassPath("template.ofp");

        // Add HTTP template (with URL)
        HttpTemplateAutoWireSection section = this.source.addHttpTemplate("uri", templatePath,
                MockTemplateLogic.class);
        section.setTemplateSecure(isSecure);
        this.source.openOfficeFloor();

        // Ensure correct section details
        assertEquals("Incorrect section name", "uri", section.getSectionName());
        assertEquals("Incorrect section source", HttpTemplateSectionSource.class.getName(),
                section.getSectionSourceClassName());
        assertEquals("Incorrect section location", this.getClassPath("template.ofp"), templatePath);
        assertEquals("Incorrect template path", templatePath, section.getTemplatePath());
        assertEquals("Incorrect template URI", "/uri", section.getTemplateUri());

        // Ensure template available
        this.assertHttpRequest("/uri", isSecure, 200, SUBMIT_URI);
    }

    /**
     * Ensure able to provide the Content-Type with the {@link Charset}.
     */
    public void testTemplateForContentTypeWithCharset() throws Exception {

        // Obtain non-default charset
        Charset charset = Charset.defaultCharset();
        if (AbstractServerSocketManagedObjectSource.DEFAULT_CHARSET.equalsIgnoreCase(charset.name())) {
            charset = Charset.forName("UTF-16");
        }

        // Create the content type
        String contentType = "text/plain; one=1; charset=" + charset.name() + "; another";

        // Add HTTP template with Content-Type
        HttpTemplateAutoWireSection section = this.source.addHttpTemplate("uri", "PUBLIC/resource.html", null);
        section.setTemplateContentType(contentType);
        this.source.openOfficeFloor();

        // Ensure correct Content-Type
        assertEquals("Incorrect Content-Type", contentType, section.getTemplateContentType());

        // Ensure template correct (charset appended as handled specifically)
        HttpResponse response = this.assertHttpRequest("/uri", 200, "RESOURCE");
        assertEquals("Incorrect Content-Type on response", "text/plain; one=1; another; charset=" + charset.name(),
                response.getFirstHeader("Content-Type").getValue());
    }

    /**
     * Ensure able to provide the Content-Type.
     */
    public void testTemplateContentTypeWithDefaultCharset() throws Exception {

        // Add HTTP template with Content-Type
        HttpTemplateAutoWireSection section = this.source.addHttpTemplate("uri", "PUBLIC/resource.html", null);
        section.setTemplateContentType("text/plain");
        this.source.openOfficeFloor();

        // Ensure correct Content-Type
        assertEquals("Incorrect Content-Type", "text/plain", section.getTemplateContentType());

        // Ensure template correct
        HttpResponse response = this.assertHttpRequest("/uri", 200, "RESOURCE");
        assertEquals("Incorrect Content-Type on response",
                "text/plain; charset=" + AbstractServerSocketManagedObjectSource.getCharset(null).name(),
                response.getFirstHeader("Content-Type").getValue());
    }

    /**
     * Ensure <code>charset</code> parameter providing on unknown Content-Type.
     */
    public void testTemplateNonTextContentType() throws Exception {

        // Add HTTP template with Content-Type
        HttpTemplateAutoWireSection section = this.source.addHttpTemplate("uri", "PUBLIC/resource.html", null);
        section.setTemplateContentType("x-test/non-text");
        this.source.openOfficeFloor();

        // Ensure correct Content-Type
        assertEquals("Incorrect Content-Type", "x-test/non-text", section.getTemplateContentType());

        // Ensure template correct
        HttpResponse response = this.assertHttpRequest("/uri", 200, "RESOURCE");
        assertEquals("Incorrect Content-Type on response",
                "x-test/non-text; charset=" + AbstractServerSocketManagedObjectSource.getCharset(null).name(),
                response.getFirstHeader("Content-Type").getValue());

    }

    /**
     * Tests redirect on POST for default configuration.
     */
    public void testPostDefaultRenderRedirect() throws Exception {
        this.doRenderRedirectTest("POST");
    }

    /**
     * Tests redirect on configured HTTP method.
     */
    public void testConfiguredRenderRedirect() throws Exception {
        this.doRenderRedirectTest("OTHER", "POST", "PUT", "OTHER");
    }

    /**
     * Ensure redirect before rendering for particular HTTP methods.
     * 
     * @param method
     *            HTTP method to use.
     * @param renderRedirectHttpMethods
     *            Render redirect HTTP methods.
     */
    public void doRenderRedirectTest(String method, String... renderRedirectHttpMethods) throws Exception {

        // Add the template
        final String templatePath = this.getClassPath("template.ofp");
        HttpTemplateAutoWireSection template = this.source.addHttpTemplate("uri", templatePath,
                MockTemplateLogic.class);

        // Ensure able to provide appropriate render redirect HTTP methods
        for (String renderRedirectHttpMethod : renderRedirectHttpMethods) {
            template.addRenderRedirectHttpMethod(renderRedirectHttpMethod);
        }

        // Ensure correctly provided
        String[] configuredMethods = template.getRenderRedirectHttpMethods();
        assertEquals("Incorrect number of render redirect HTTP methods", renderRedirectHttpMethods.length,
                configuredMethods.length);
        for (int i = 0; i < renderRedirectHttpMethods.length; i++) {
            assertEquals("Incorrect render redirect HTTP method " + i, renderRedirectHttpMethods[i],
                    configuredMethods[i]);
        }

        // Open
        this.source.openOfficeFloor();

        // Ensure appropriately redirects
        String url = "http://" + HOST_NAME + ":" + this.port + "/uri";
        HttpUriRequest request = new HttpConfiguredRequest(method, url);
        String redirectUrl = "/uri" + HttpRouteTask.REDIRECT_URI_SUFFIX;
        this.assertHttpRequest(request, redirectUrl, 200, "/uri-submit");
    }

    /**
     * {@link HttpUriRequest} for HTTP method <code>OTHER</code>.
     */
    private static class HttpConfiguredRequest extends HttpEntityEnclosingRequestBase {

        /**
         * HTTP method.
         */
        private final String method;

        /**
         * Initiate.
         * 
         * @param method
         *            HTTP method.
         * @param uri
         *            URI.
         */
        public HttpConfiguredRequest(String method, String uri) {
            this.setURI(URI.create(uri));
            this.method = method;
        }

        @Override
        public String getMethod() {
            return this.method;
        }
    }

    /**
     * Ensure able to add HTTP template that is available for default root.
     */
    public void testRootTemplate() throws Exception {
        this.doRootTemplateTest(false);
    }

    /**
     * Ensure able to add secure HTTP template that is available for default
     * root.
     */
    public void testSecureRootTemplate() throws Exception {
        this.doRootTemplateTest(true);
    }

    /**
     * Undertakes test to ensure able to add HTTP template that is available for
     * default root.
     */
    public void doRootTemplateTest(boolean isSecure) throws Exception {

        final String SUBMIT_URI = "/-submit";

        final String templatePath = this.getClassPath("template.ofp");

        // Add HTTP template (with URL)
        HttpTemplateAutoWireSection section = this.source.addHttpTemplate("/", templatePath,
                MockTemplateLogic.class);
        section.setTemplateSecure(isSecure);
        this.source.openOfficeFloor();

        // Ensure correct section details
        assertEquals("Incorrect section name", "_root_", section.getSectionName());
        assertEquals("Incorrect section source", HttpTemplateSectionSource.class.getName(),
                section.getSectionSourceClassName());
        assertEquals("Incorrect section location", this.getClassPath("template.ofp"), templatePath);
        assertEquals("Incorrect template path", templatePath, section.getTemplatePath());
        assertEquals("Incorrect template URI", "/", section.getTemplateUri());

        // Ensure template available at default root
        this.assertHttpRequest("/", isSecure, 200, SUBMIT_URI);

        // Ensure root link works
        this.assertHttpRequest(SUBMIT_URI, isSecure, 200, "submitted" + SUBMIT_URI);
    }

    /**
     * Mock logic for the template.
     */
    public static class MockTemplateLogic {

        /**
         * Submit handler.
         * 
         * @param connection
         *            {@link ServerHttpConnection}.
         */
        @NextTask("doNothing")
        public void submit(ServerHttpConnection connection) throws IOException {
            WebApplicationAutoWireOfficeFloorSourceTest.writeResponse("submitted", connection);
        }

        /**
         * Do nothing after submit.
         */
        public void doNothing() {
        }
    }

    /**
     * Ensure issue if attempt to add more than one HTTP template for a URI.
     */
    public void testMultipleTemplatesWithSameUri() throws Exception {

        final String TEMPLATE_URI = "template";

        // Add HTTP template
        this.source.addHttpTemplate(TEMPLATE_URI, this.getClassPath("template.ofp"), MockTemplateLogic.class);

        // Ensure indicates template already registered for URI
        try {
            this.source.addHttpTemplate(TEMPLATE_URI, this.getClassPath("template.ofp"), MockTemplateLogic.class);
            fail("Should not successfully add template for duplicate URI");
        } catch (IllegalStateException ex) {
            assertEquals("Incorrect cause", "HTTP Template already added for URI '/" + TEMPLATE_URI + "'",
                    ex.getMessage());
        }

        // Ensure indicates template already registered for canonical URI
        try {
            this.source.addHttpTemplate("/" + TEMPLATE_URI, this.getClassPath("template.ofp"),
                    MockTemplateLogic.class);
            fail("Should not successfully add template for duplicate URI");
        } catch (IllegalStateException ex) {
            assertEquals("Incorrect cause", "HTTP Template already added for URI '/" + TEMPLATE_URI + "'",
                    ex.getMessage());
        }
    }

    /**
     * Ensure able to request the template link.
     */
    public void testTemplateLink() throws Exception {
        this.doTemplateLinkTest(false);
    }

    /**
     * Ensure able to request the secure template link.
     */
    public void testSecureTemplateLink() throws Exception {
        this.doTemplateLinkTest(true);
    }

    /**
     * Undertakes test to ensure able to request the template link.
     */
    private void doTemplateLinkTest(boolean isSecure) throws Exception {

        final String SUBMIT_URI = "/uri-submit";

        // Add HTTP template
        this.source.addHttpTemplate("uri", this.getClassPath("template.ofp"), MockTemplateLogic.class)
                .setTemplateSecure(isSecure);
        this.source.openOfficeFloor();

        // Ensure submit on task for template is correct
        this.assertHttpRequest(SUBMIT_URI, isSecure, 200, "submitted" + SUBMIT_URI);
    }

    /**
     * Ensure can secure a link.
     */
    public void testSecureLink() throws Exception {

        final String SUBMIT_URI = "/uri-submit";

        // Add HTTP template
        this.source.addHttpTemplate("uri", this.getClassPath("template.ofp"), MockTemplateLogic.class)
                .setLinkSecure("submit", true);
        this.source.openOfficeFloor();

        // Ensure submit on task for template is correct
        this.assertHttpRequest(SUBMIT_URI, true, 200, "submitted" + SUBMIT_URI);
    }

    /**
     * Ensure can set link as non-secure.
     */
    public void testNonSecureLink() throws Exception {

        final String SUBMIT_URI = "/uri-submit";

        // Add HTTP template
        HttpTemplateAutoWireSection template = this.source.addHttpTemplate("uri", this.getClassPath("template.ofp"),
                MockTemplateLogic.class);
        template.setTemplateSecure(true);
        template.setLinkSecure("submit", false);
        this.source.openOfficeFloor();

        // Ensure submit on task for template is correct
        String requestUrl = "http://" + HOST_NAME + ":" + this.port + SUBMIT_URI;
        String redirectUrl = "https://" + HOST_NAME + ":" + this.securePort + "/uri"
                + HttpRouteTask.REDIRECT_URI_SUFFIX;
        String linkUrl = "http://" + HOST_NAME + ":" + this.port + SUBMIT_URI;
        this.assertHttpRequest(new HttpGet(requestUrl), redirectUrl, 200, "submitted" + linkUrl);
    }

    /**
     * Ensure can inherit template.
     */
    public void testInheritTemplate() throws Exception {

        // Add link target template
        HttpTemplateAutoWireSection target = this.source.addHttpTemplate("/target",
                this.getClassPath("/template.ofp"), MockTemplateLogic.class);

        // Add parent template
        HttpTemplateAutoWireSection parent = this.source.addHttpTemplate("/parent", this.getClassPath("Parent.ofp"),
                null);
        this.source.linkToHttpTemplate(parent, "submit", target);

        // Add child template (inheriting content and links)
        HttpTemplateAutoWireSection child = this.source.addHttpTemplate("/child", this.getClassPath("Child.ofp"),
                null);
        child.setSuperSection(parent);

        // Open OfficeFloor
        this.source.openOfficeFloor();

        // Ensure child inherits content
        this.assertHttpRequest("/child", 200, "Parent CHILD introduced /child-submit");

        // Ensure child inherits link configuration
        this.assertHttpRequest("/child-submit", 200, "/target-submit");
    }

    /**
     * Ensure template inheritance hierarchy is in correct order for ancestors.
     */
    public void testInheritTemplateHierarchy() throws Exception {

        // Add link target template
        HttpTemplateAutoWireSection target = this.source.addHttpTemplate("/target",
                this.getClassPath("/template.ofp"), MockTemplateLogic.class);

        // Add parent template
        HttpTemplateAutoWireSection parent = this.source.addHttpTemplate("/parent", this.getClassPath("Parent.ofp"),
                null);
        this.source.linkToHttpTemplate(parent, "submit", target);

        // Add child template (inheriting content and links)
        HttpTemplateAutoWireSection child = this.source.addHttpTemplate("/child", this.getClassPath("Child.ofp"),
                null);
        child.setSuperSection(parent);

        // Add grand child template (override the link)
        HttpTemplateAutoWireSection grandChild = this.source.addHttpTemplate("/grandchild",
                this.getClassPath("GrandChild.ofp"), null);
        grandChild.setSuperSection(child);

        // Open OfficeFloor
        this.source.openOfficeFloor();

        // Ensure grand child overrides section with link (no need to inherit)
        this.assertHttpRequest("/grandchild", 200, "Grandchild CHILD introduced Overridden");
    }

    /**
     * Ensure template can inherit section link configuration.
     */
    public void testTemplateInheritSectionLinkConfiguration() throws Exception {

        // Add link target template
        HttpTemplateAutoWireSection target = this.source.addHttpTemplate("/target",
                this.getClassPath("/template.ofp"), MockTemplateLogic.class);

        // Add grand parent section
        AutoWireSection grandParent = this.source.addSection("GRAND_PARENT", ClassSectionSource.class.getName(),
                GrandParentSection.class.getName());
        this.source.linkToHttpTemplate(grandParent, "submit", target);

        // Add parent template (inheriting link configuration)
        HttpTemplateAutoWireSection parent = this.source.addHttpTemplate("/parent", this.getClassPath("Parent.ofp"),
                null);
        parent.setSuperSection(grandParent);

        // Add child template (inheriting content and links)
        HttpTemplateAutoWireSection child = this.source.addHttpTemplate("/child", this.getClassPath("Child.ofp"),
                null);
        child.setSuperSection(parent);

        // Open OfficeFloor
        this.source.openOfficeFloor();

        // Ensure child inherits content
        this.assertHttpRequest("/child", 200, "Parent CHILD introduced /child-submit");

        // Ensure child inherits link configuration
        this.assertHttpRequest("/child-submit", 200, "/target-submit");
    }

    /**
     * Parent section.
     */
    public static class GrandParentSection {
        @NextTask("submit")
        public void input() {
        }
    }

    /**
     * Ensure can inherit link being secure.
     */
    public void testInheritTemplateLinkSecure() throws Exception {

        // Add link target template
        HttpTemplateAutoWireSection target = this.source.addHttpTemplate("/target",
                this.getClassPath("/template.ofp"), MockTemplateLogic.class);

        // Add parent template
        HttpTemplateAutoWireSection parent = this.source.addHttpTemplate("/parent", this.getClassPath("Parent.ofp"),
                null);
        parent.setLinkSecure("submit", false);
        this.source.linkToHttpTemplate(parent, "submit", target);

        // Add child template (inheriting content and links)
        HttpTemplateAutoWireSection child = this.source.addHttpTemplate("/child", this.getClassPath("Child.ofp"),
                null);
        parent.setLinkSecure("submit", true); // overrides parent
        child.setSuperSection(parent);

        // Add child template (inheriting content and links)
        HttpTemplateAutoWireSection grandChild = this.source.addHttpTemplate("/grandchild",
                this.getClassPath("LinkChild.ofp"), null);
        grandChild.setSuperSection(child);

        // Open OfficeFloor
        this.source.openOfficeFloor();

        // Ensure child inherits link secure
        this.assertHttpRequest("/grandchild", 200,
                "Parent LINK_CHILD override https://" + HOST_NAME + ":" + this.securePort + "/grandchild-submit");

        // Ensure child inherits link secure
        this.assertHttpRequest("/grandchild-submit", true, 200,
                "http://" + HOST_NAME + ":" + this.port + "/target-submit");
    }

    /**
     * Ensure can not inherit template link secure if no longer exists (as
     * containing section overridden and no longer contains the link).
     */
    public void testNotInheritMissingTemplateLinkSecure() throws Exception {

        // Add link target template
        HttpTemplateAutoWireSection target = this.source.addHttpTemplate("/target",
                this.getClassPath("/template.ofp"), MockTemplateLogic.class);

        // Add parent template
        HttpTemplateAutoWireSection parent = this.source.addHttpTemplate("/parent", this.getClassPath("Parent.ofp"),
                null);
        parent.setLinkSecure("submit", true);
        this.source.linkToHttpTemplate(parent, "submit", target);

        // Add child template (inheriting content and links)
        HttpTemplateAutoWireSection child = this.source.addHttpTemplate("/child", this.getClassPath("Child.ofp"),
                null);
        child.setSuperSection(parent);

        // Add grand child template (override the link)
        HttpTemplateAutoWireSection grandChild = this.source.addHttpTemplate("/grandchild",
                this.getClassPath("GrandChild.ofp"), null);
        grandChild.setSuperSection(child);

        // Open OfficeFloor
        this.source.openOfficeFloor();

        // Ensure grand child overrides section with link (no need to inherit)
        this.assertHttpRequest("/grandchild", 200, "Grandchild CHILD introduced Overridden");
    }

    /**
     * Ensure issue if the template inheritance hierarchy is cyclic.
     */
    public void testCyclicTemplateInheritanceHierarchy() throws Exception {

        final CyclicInheritanceException exception = new CyclicInheritanceException(
                "Template /parent has a cyclic inheritance hierarchy ( child : parent : child : ... )");

        // Record issue of cyclic inheritance hierarchy
        final MockCompilerIssues issues = new MockCompilerIssues(this);
        this.source.getOfficeFloorCompiler().setCompilerIssues(issues);
        issues.recordIssue("OfficeFloor", OfficeFloorNodeImpl.class,
                "Template /parent has a cyclic inheritance hierarchy ( child : parent : child : ... )");
        issues.recordIssue("OfficeFloor", OfficeFloorNodeImpl.class,
                "Failed to source OfficeFloor from OfficeFloorSource (source="
                        + WebApplicationAutoWireOfficeFloorSource.class.getName() + ", location=auto-wire)",
                exception);

        // Test
        this.replayMockObjects();

        // Add link target template
        HttpTemplateAutoWireSection target = this.source.addHttpTemplate("/target",
                this.getClassPath("/template.ofp"), MockTemplateLogic.class);

        // Add parent template
        HttpTemplateAutoWireSection parent = this.source.addHttpTemplate("/parent", this.getClassPath("Parent.ofp"),
                null);
        this.source.linkToHttpTemplate(parent, "submit", target);

        // Add child template (inheriting content and links)
        HttpTemplateAutoWireSection child = this.source.addHttpTemplate("/child", this.getClassPath("Child.ofp"),
                null);
        child.setSuperSection(parent);

        // Cyclic inheritance hierarchy
        parent.setSuperSection(child);

        // Open OfficeFloor (manually to use mock compiler issues)
        OfficeFloor officeFloor = this.source.getOfficeFloorCompiler().compile("auto-wire");
        assertNull("Should not have loaded the OfficeFloor", officeFloor);

        // Ensure report cyclic inheritance hierarchy
        this.verifyMockObjects();
    }

    /**
     * Ensure default template URI suffix is applied.
     */
    public void testDefaultTemplateUriSuffix() throws Exception {

        final String SUFFIX = ".suffix";
        final String TEMPLATE_URI = "/uri" + SUFFIX;
        final String LINK_URI = "/uri-submit" + SUFFIX;

        // Add HTTP template with default template URI suffix
        this.source.addHttpTemplate("uri", this.getClassPath("template.ofp"), MockTemplateLogic.class);
        this.source.setDefaultHttpTemplateUriSuffix(SUFFIX);
        this.source.openOfficeFloor();

        // Ensure service template URI with suffix
        this.assertHttpRequest(TEMPLATE_URI, 200, LINK_URI);

        // Ensure service template link URI with suffix
        this.assertHttpRequest(LINK_URI, 200, "submitted" + LINK_URI);
    }

    /**
     * Ensure root template does not apply suffix for template, only links.
     */
    public void testRootTemplateUriSuffix() throws Exception {

        final String SUFFIX = ".suffix";
        final String TEMPLATE_URI = "/";
        final String LINK_URI = "/-submit" + SUFFIX;

        // Add root HTTP template with default template URI suffix
        this.source.addHttpTemplate("/", this.getClassPath("template.ofp"), MockTemplateLogic.class);
        this.source.setDefaultHttpTemplateUriSuffix(SUFFIX);
        this.source.openOfficeFloor();

        // Ensure service template URI with suffix
        this.assertHttpRequest(TEMPLATE_URI, 200, LINK_URI);

        // Ensure service template link URI with suffix
        this.assertHttpRequest(LINK_URI, 200, "submitted" + LINK_URI);
    }

    /**
     * Ensure can specify no default template URI suffix is applied.
     */
    public void testNoDefaultTemplateUriSuffix() throws Exception {

        final String TEMPLATE_URI = "/uri";
        final String LINK_URI = "/uri-submit";

        // Add HTTP template with default template URI suffix
        this.source.addHttpTemplate("uri", this.getClassPath("template.ofp"), MockTemplateLogic.class);
        this.source.setDefaultHttpTemplateUriSuffix(null);
        this.source.openOfficeFloor();

        // Ensure service template URI with suffix
        this.assertHttpRequest(TEMPLATE_URI, 200, LINK_URI);

        // Ensure service template link URI with suffix
        this.assertHttpRequest(LINK_URI, 200, "submitted" + LINK_URI);
    }

    /**
     * Ensure template URI suffix appended to template URI and link URIs.
     */
    public void testTemplateUriSuffix() throws Exception {

        final String SUFFIX = ".suffix";
        final String TEMPLATE_URI = "/uri" + SUFFIX;
        final String LINK_URI = "/uri-submit" + SUFFIX;

        // Add HTTP template
        HttpTemplateAutoWireSection template = this.source.addHttpTemplate("uri", this.getClassPath("template.ofp"),
                MockTemplateLogic.class);
        template.setTemplateUriSuffix(SUFFIX);
        this.source.openOfficeFloor();

        // Ensure service template URI with suffix
        this.assertHttpRequest(TEMPLATE_URI, 200, LINK_URI);

        // Ensure service template link URI with suffix
        this.assertHttpRequest(LINK_URI, 200, "submitted" + LINK_URI);
    }

    /**
     * Ensure able to override default template URI suffix.
     */
    public void testOverrideTemplateUriSuffix() throws Exception {

        final String TEMPLATE_URI = "/uri.override";
        final String LINK_URI = "/uri-submit.override";

        // Provide default template URI suffix
        this.source.setDefaultHttpTemplateUriSuffix(".suffix");

        // Add HTTP template
        HttpTemplateAutoWireSection template = this.source.addHttpTemplate("uri", this.getClassPath("template.ofp"),
                MockTemplateLogic.class);
        template.setTemplateUriSuffix(".override");
        this.source.openOfficeFloor();

        // Ensure service template URI with suffix
        this.assertHttpRequest(TEMPLATE_URI, 200, LINK_URI);

        // Ensure service template link URI with suffix
        this.assertHttpRequest(LINK_URI, 200, "submitted" + LINK_URI);
    }

    /**
     * Ensure appropriate linked URIs.
     */
    public void testLinkedUris() {

        // Add HTTP template (not root so should not be included)
        HttpTemplateAutoWireSection template = this.source.addHttpTemplate("template",
                this.getClassPath("template.ofp"), MockTemplateLogic.class);

        // Provide URI link
        this.source.linkUri("uri", template, HttpTemplateSectionSource.RENDER_TEMPLATE_INPUT_NAME);

        // Validate URIs
        assertUris(this.source.getURIs(), "/uri");

        // Validate with root HTTP template
        this.source.addHttpTemplate("/", this.getClassPath("template.ofp"), MockTemplateLogic.class);
        assertUris(this.source.getURIs(), "/", "/uri");
    }

    /**
     * Asserts the URIs are correct.
     * 
     * @param actualUris
     *            Actual URIs.
     * @param expectedUris
     *            Expected URIs.
     */
    private static void assertUris(String[] actualUris, String... expectedUris) {
        assertEquals("Incorrect number of URIs", expectedUris.length, actualUris.length);
        for (int i = 0; i < expectedUris.length; i++) {
            assertEquals("Incorrect URI " + i, expectedUris[i], actualUris[i]);
        }
    }

    /**
     * Ensure able to provide {@link HttpTemplateSectionExtension}.
     */
    public void testTemplateExtension() throws Exception {

        // Add HTTP template
        HttpTemplateAutoWireSection template = this.source.addHttpTemplate("template",
                this.getClassPath("Extension.ofp"), MockExtensionTemplateLogic.class);

        // Add template extension
        HttpTemplateAutoWireSectionExtension extension = template
                .addTemplateExtension(MockHttpTemplateSectionExtension.class);
        extension.addProperty("name", "value");

        // Open
        this.source.openOfficeFloor();

        // Ensure extend the template
        this.assertHttpRequest("/template", 200, "extended");
    }

    /**
     * Mock {@link HttpTemplateSectionExtension} for testing.
     */
    public static class MockHttpTemplateSectionExtension implements HttpTemplateSectionExtension {
        @Override
        public void extendTemplate(HttpTemplateSectionExtensionContext context) throws Exception {
            context.setTemplateContent("${extend}");
        }
    }

    /**
     * Template logic for the extension test.
     */
    public static class MockExtensionTemplateLogic {

        public MockExtensionTemplateLogic getTemplate() {
            return this;
        }

        public String getExtend() {
            return "extended";
        }
    }

    /**
     * Ensure able to link URI to {@link OfficeSectionInput} for processing.
     */
    public void testLinkUriToSectionInput() throws Exception {
        this.doLinkUriToSectionInputTest(false);
    }

    /**
     * Ensure able to link secure URI to {@link OfficeSectionInput} for
     * processing.
     */
    public void testLinkSecureUriToSectionInput() throws Exception {
        this.doLinkUriToSectionInputTest(true);
    }

    /**
     * Ensure able to link URI to {@link OfficeSectionInput} for processing.
     */
    public void doLinkUriToSectionInputTest(boolean isSecure) throws Exception {

        // Add section for handling request
        AutoWireSection section = this.source.addSection("SECTION", ClassSectionSource.class.getName(),
                MockTemplateLogic.class.getName());
        this.source.linkUri("test", section, "submit").setUriSecure(isSecure);
        this.source.openOfficeFloor();

        // Ensure can send to URI
        this.assertHttpRequest("/test", isSecure, 200, "submitted");
    }

    /**
     * Ensure able to link {@link OfficeSectionOutput} to {@link HttpTemplate}.
     */
    public void testLinkToHttpTemplate() throws Exception {

        // Add linking to HTTP template
        AutoWireSection section = this.source.addSection("SECTION", ClassSectionSource.class.getName(),
                MockLinkHttpTemplate.class.getName());
        this.source.linkUri("test", section, "service");
        HttpTemplateAutoWireSection template = this.source.addHttpTemplate("template",
                this.getClassPath("template.ofp"), MockTemplateLogic.class);
        this.source.linkToHttpTemplate(section, "http-template", template);
        this.source.openOfficeFloor();

        // Ensure link to the HTTP template
        this.assertHttpRequest("/test", 200, "LINK to /template-submit");
    }

    /**
     * Ensure can inherit link to {@link HttpTemplate}.
     */
    public void testInheritLinkToHttpTemplate() throws Exception {

        // Add the template
        HttpTemplateAutoWireSection template = this.source.addHttpTemplate("template",
                this.getClassPath("template.ofp"), MockTemplateLogic.class);

        // Add parent linking to resource
        AutoWireSection parent = this.source.addSection("PARENT", ClassSectionSource.class.getName(),
                MockLinkHttpTemplate.class.getName());
        this.source.linkToHttpTemplate(parent, "http-template", template);

        // Add child inheriting link configuration
        AutoWireSection child = this.source.addSection("CHILD", ClassSectionSource.class.getName(),
                MockLinkHttpTemplate.class.getName());
        this.source.linkUri("test", child, "service");
        child.setSuperSection(parent);

        // Open OfficeFloor
        this.source.openOfficeFloor();

        // Ensure link to the HTTP template
        this.assertHttpRequest("/test", 200, "LINK to /template-submit");
    }

    /**
     * Provides mock functionality to link to a HTTP template.
     */
    public static class MockLinkHttpTemplate {
        @NextTask("http-template")
        public void service(ServerHttpConnection connection) throws IOException {
            WebApplicationAutoWireOfficeFloorSourceTest.writeResponse("LINK to ", connection);
        }
    }

    /**
     * Ensure able to link to resource.
     */
    public void testLinkToResource() throws Exception {

        // Add linking to resource
        AutoWireSection section = this.source.addSection("SECTION", ClassSectionSource.class.getName(),
                MockLinkResource.class.getName());
        this.source.linkUri("test", section, "service");
        this.source.linkToResource(section, "resource", "resource.html");
        this.source.openOfficeFloor();

        // Ensure provide the resource
        this.assertHttpRequest("/test", 200, "RESOURCE");
    }

    /**
     * Ensure can inherit link to resource.
     */
    public void testInheritLinkToResource() throws Exception {

        // Add parent linking to resource
        AutoWireSection parent = this.source.addSection("PARENT", ClassSectionSource.class.getName(),
                MockLinkResource.class.getName());
        this.source.linkToResource(parent, "resource", "resource.html");

        // Add child inheriting link configuration
        AutoWireSection child = this.source.addSection("CHILD", ClassSectionSource.class.getName(),
                MockLinkResource.class.getName());
        this.source.linkUri("test", child, "service");
        child.setSuperSection(parent);

        // Open OfficeFloor
        this.source.openOfficeFloor();

        // Ensure provide the resource
        this.assertHttpRequest("/test", 200, "RESOURCE");
    }

    /**
     * Provides mock functionality to link to a resource.
     */
    public static class MockLinkResource {
        @NextTask("resource")
        public void service(ServerHttpConnection connection) throws IOException {
            WebApplicationAutoWireOfficeFloorSourceTest.writeResponse("LINK to ", connection);
        }
    }

    /**
     * Ensure able to link {@link Escalation} to
     * {@link HttpTemplateAutoWireSection}.
     */
    public void testLinkEscalationToTemplate() throws Exception {

        // Add escalation to template
        AutoWireSection failingSection = this.source.addSection("FAILING", ClassSectionSource.class.getName(),
                FailingSection.class.getName());
        this.source.linkUri("test", failingSection, "task");
        HttpTemplateAutoWireSection template = this.source.addHttpTemplate("handler",
                this.getClassPath("template.ofp"), MockTemplateLogic.class);
        this.source.linkEscalation(SQLException.class, template);
        this.source.openOfficeFloor();

        // Ensure link escalation to template
        this.assertHttpRequest("/test", 200, "Escalated to /handler-submit");
    }

    /**
     * Section class that fails and provides an {@link Escalation}.
     */
    public static class FailingSection {
        public void task(ServerHttpConnection connection) throws Exception {
            WebApplicationAutoWireOfficeFloorSourceTest.writeResponse("Escalated to ", connection);
            throw new SQLException("Test failure");
        }
    }

    /**
     * Ensure able to link {@link Escalation} to resource.
     */
    public void testLinkEscalationToResource() throws Exception {

        // Add escalation to resource
        AutoWireSection failingSection = this.source.addSection("FAILING", ClassSectionSource.class.getName(),
                FailingSection.class.getName());
        this.source.linkUri("test", failingSection, "task");
        this.source.linkEscalation(SQLException.class, "resource.html");
        this.source.openOfficeFloor();

        // Ensure link escalation to resource
        this.assertHttpRequest("/test", 200, "RESOURCE");
    }

    /**
     * Ensure able to utilise the Http Session object.
     */
    public void testHttpSessionObject() throws Exception {

        // Provide HTTP Session
        this.source.addManagedObject(HttpSessionManagedObjectSource.class.getName(), null,
                new AutoWire(HttpSession.class)).setTimeout(10 * 1000);

        // Add two templates to ensure object available to both
        this.source.addHttpTemplate("one", this.getClassPath("StatefulObject.ofp"),
                MockHttpSessionObjectTemplate.class);
        this.source.addHttpTemplate("two", this.getClassPath("StatefulObject.ofp"),
                MockHttpSessionObjectTemplate.class);

        // Add the HTTP Session object
        this.source.addHttpSessionObject(MockHttpSessionObject.class);

        // Start the HTTP Server
        this.source.openOfficeFloor();

        // Ensure state maintained across requests
        this.assertHttpRequest("/one", 200, "1");
        this.assertHttpRequest("/one", 200, "2");

        // Ensure state maintained on another template
        this.assertHttpRequest("/two", 200, "3");

        // Ensure reflected on original template
        this.assertHttpRequest("/one", 200, "4");
    }

    /**
     * Provides mock template logic for Http Session Object.
     */
    public static class MockHttpSessionObjectTemplate {
        public MockHttpSessionObject getTemplate(MockHttpSessionObject object) {
            object.count++; // increment count to indicate maintaining state
            return object;
        }
    }

    /**
     * Mock Http Session Object.
     */
    public static class MockHttpSessionObject implements Serializable {
        public int count = 0;

        public int getCount() {
            return count;
        }
    }

    /**
     * Ensure {@link HttpSessionStateful} annotation is honoured for templates.
     */
    public void testHttpSessionStatefulAnnotation() throws Exception {

        // Provide HTTP Session
        this.source.addManagedObject(HttpSessionManagedObjectSource.class.getName(), null,
                new AutoWire(HttpSession.class)).setTimeout(10 * 1000);

        // Add two templates with annotations for HttpSessionStateful
        this.source.addHttpTemplate("one", this.getClassPath("StatefulObject.ofp"),
                MockAnnotatedHttpSessionStatefulTemplate.class);
        this.source.addHttpTemplate("two", this.getClassPath("StatefulObject.ofp"),
                MockAnnotatedHttpSessionStatefulTemplate.class);

        // HTTP Session Object should be detected and added

        // Start the HTTP Server
        this.source.openOfficeFloor();

        // Ensure state maintained across requests
        this.assertHttpRequest("/one", 200, "1");
        this.assertHttpRequest("/one", 200, "2");

        // Ensure state maintained on another template
        this.assertHttpRequest("/two", 200, "3");

        // Ensure reflected on original template
        this.assertHttpRequest("/one", 200, "4");
    }

    /**
     * Provides mock template logic for using the {@link HttpSessionStateful}
     * annotation.
     */
    public static class MockAnnotatedHttpSessionStatefulTemplate {
        public MockAnnotatedHttpSessionStatefulObject getTemplate(MockAnnotatedHttpSessionStatefulObject object) {
            object.count++; // increment count to indicate maintaining state
            return object;
        }
    }

    /**
     * Mock Http Session Object as annotated.
     */
    @HttpSessionStateful
    public static class MockAnnotatedHttpSessionStatefulObject implements Serializable {
        public int count = 0;

        public int getCount() {
            return count;
        }
    }

    /**
     * Ensure can override the binding name to the {@link HttpSession}.
     */
    public void testHttpSessionStatefulAnnotationOverridingBoundName() throws Exception {

        // Provide HTTP Session
        this.source.addManagedObject(HttpSessionManagedObjectSource.class.getName(), null,
                new AutoWire(HttpSession.class)).setTimeout(10 * 1000);

        // Add the template
        this.source.addHttpTemplate("template", this.getClassPath("StatefulObject.ofp"),
                MockAnnotatedOverriddenBindNameHttpSessionStatefulTemplate.class);

        // HTTP Session object should be detected and added

        // Start the HTTP Server
        this.source.openOfficeFloor();

        // Ensure same object (test within template logic)
        this.assertHttpRequest("/template", 200, "1");
    }

    /**
     * Provides mock template logic for validating the overriding binding name
     * for the {@link HttpSession} object.
     */
    public static class MockAnnotatedOverriddenBindNameHttpSessionStatefulTemplate implements Serializable {
        public MockAnnotatedOverriddenBindNameHttpSessionStatefulObject getTemplate(
                MockAnnotatedOverriddenBindNameHttpSessionStatefulObject object, HttpSession session) {

            // Ensure object bound under annotated name within the session
            Object sessionObject = session.getAttribute("BIND");
            assertEquals("Should be same object", object, sessionObject);

            // Return for rendering
            return object;
        }
    }

    /**
     * Mock Http Session Object with overridden binding name.
     */
    @HttpSessionStateful("BIND")
    public static class MockAnnotatedOverriddenBindNameHttpSessionStatefulObject implements Serializable {
        public int getCount() {
            return 1;
        }
    }

    /**
     * Ensure able to utilise the {@link HttpRequestState} object.
     */
    public void testHttpRequestObject() throws Exception {

        final String URI = "/template-submit";

        // Provide HTTP Request State
        this.source.addManagedObject(HttpRequestStateManagedObjectSource.class.getName(), null,
                new AutoWire(HttpRequestState.class));

        // Add the template
        this.source.addHttpTemplate("template", this.getClassPath("HttpStateObject.ofp"),
                MockHttpRequestStateTemplate.class);

        // HTTP request object should be detected and added

        // Start the HTTP Server
        this.source.openOfficeFloor();

        // Ensure same object (test within template logic)
        this.assertHttpRequest(URI, 200, "maintained state-" + URI);
    }

    /**
     * Provides mock template logic for validating the {@link HttpRequestState}.
     */
    public static class MockHttpRequestStateTemplate {

        public void submit(MockHttpRequestStateObject object, HttpRequestState state) {

            // Ensure object bound under annotated name within the request state
            Object requestObject = state.getAttribute("BIND");
            assertEquals("Should be same object", object, requestObject);

            // Specify value as should maintain state through request
            object.text = "maintained state";
        }

        public MockHttpRequestStateObject getTemplate(MockHttpRequestStateObject object) {
            // Value should be specified in submit
            return object;
        }
    }

    /**
     * Mock Http Request State Object with overridden binding name.
     */
    @HttpRequestStateful("BIND")
    public static class MockHttpRequestStateObject implements Serializable {

        public String text = "not specified";

        public String getText() {
            return this.text;
        }
    }

    /**
     * Ensure able to utilise the HTTP Request Object to load parameters.
     */
    public void testRequestObjectLoadingParameters() throws Exception {

        // Add the template to use parameters object
        this.source.addHttpTemplate("template", this.getClassPath("ParametersObject.ofp"),
                MockHttpParametersObjectTemplate.class);

        // Add the HTTP Request Object to load parameters
        this.source.addHttpRequestObject(MockHttpParametersObject.class, true);

        // Start the HTTP Server
        this.source.openOfficeFloor();

        // Ensure provide HTTP parameters
        this.assertHttpRequest("/template?text=VALUE", 200, "VALUE");
    }

    /**
     * Provides mock template logic for the HTTP Parameters Object.
     */
    public static class MockHttpParametersObjectTemplate {
        public MockHttpParametersObject getTemplate(MockHttpParametersObject object) {
            return object;
        }
    }

    /**
     * Mock HTTP Parameters Object.
     */
    public static class MockHttpParametersObject implements Serializable {

        private String text;

        public void setText(String text) {
            this.text = text;
        }

        public String getText() {
            return this.text;
        }
    }

    /**
     * Ensure {@link HttpParameters} is honoured for templates.
     */
    public void testAnnotatedHttpParameters() throws Exception {

        // Add the template to use parameters object
        this.source.addHttpTemplate("template", this.getClassPath("ParametersObject.ofp"),
                MockAnnotatedHttpParametersTemplate.class);

        // Start the HTTP Server
        this.source.openOfficeFloor();

        // Ensure provide HTTP parameters
        this.assertHttpRequest("/template?text=VALUE", 200, "VALUE");
    }

    /**
     * Provides mock template logic for the HTTP Parameters Object.
     */
    public static class MockAnnotatedHttpParametersTemplate {
        public MockAnnotatedHttpParameters getTemplate(MockAnnotatedHttpParameters object) {
            return object;
        }
    }

    /**
     * Mock HTTP Parameters Object.
     */
    @HttpParameters("BIND")
    public static class MockAnnotatedHttpParameters implements Serializable {

        private String text;

        public void setText(String text) {
            this.text = text;
        }

        public String getText() {
            return this.text;
        }
    }

    /**
     * Ensure able to utilise the HTTP Application object.
     */
    public void testHttpApplicationObject() throws Exception {

        final String URI = "/template-submit";

        // Provide HTTP Application State
        this.source.addManagedObject(HttpApplicationStateManagedObjectSource.class.getName(), processScopeWirer,
                new AutoWire(HttpApplicationState.class));

        // Add the template
        this.source.addHttpTemplate("template", this.getClassPath("HttpStateObject.ofp"),
                MockHttpApplicationStateTemplate.class);

        // HTTP application state object should be detected and added

        // Start the HTTP Server
        this.source.openOfficeFloor();

        // Ensure same object (test within template logic)
        this.assertHttpRequest(URI, 200, "maintained state-" + URI);
    }

    /**
     * Provides mock template logic for validating the
     * {@link HttpApplicationState}.
     */
    public static class MockHttpApplicationStateTemplate {

        public void submit(MockHttpApplicationStateObject object, HttpApplicationState state) {

            // Ensure object bound under annotated name within application state
            Object applicationObject = state.getAttribute("BIND");
            assertEquals("Should be same object", object, applicationObject);

            // Specify value as should maintain state through application
            object.text = "maintained state";
        }

        public MockHttpApplicationStateObject getTemplate(MockHttpApplicationStateObject object) {
            // Value should be specified in submit
            return object;
        }
    }

    /**
     * Mock Http Application State Object with overridden binding name.
     */
    @HttpApplicationStateful("BIND")
    public static class MockHttpApplicationStateObject {

        public String text = "not specified";

        public String getText() {
            return this.text;
        }
    }

    /**
     * Ensure able to obtain resources.
     */
    public void testObtainResources() throws Exception {

        // Obtain war directory
        File warDirectory = this.findFile(this.getClass(), "template.ofp").getParentFile();

        // Add configuration
        OfficeFloorCompiler compiler = this.source.getOfficeFloorCompiler();
        compiler.addProperty(SourceHttpResourceFactory.PROPERTY_RESOURCE_DIRECTORIES,
                warDirectory.getAbsolutePath());
        compiler.addProperty(SourceHttpResourceFactory.PROPERTY_DEFAULT_DIRECTORY_FILE_NAMES, "resource.html");

        // Start the HTTP Server
        this.source.openOfficeFloor();

        // Ensure class path resources are available
        this.assertHttpRequest("/resource.html", 200, "RESOURCE");
        this.assertHttpRequest("/", 200, "RESOURCE");

        // Ensure WAR directory resource are available
        this.assertHttpRequest("/template.ofp", 200, "#{submit}");
    }

    /**
     * Ensure able to obtain changing resources.
     */
    public void testObtainChangingResources() throws Exception {

        // Create WAR directory in temp for testing
        File warDirectory = new File(System.getProperty("java.io.tmpdir"),
                this.getClass().getSimpleName() + "-" + this.getName());
        if (warDirectory.exists()) {
            this.deleteDirectory(warDirectory);
        }
        assertTrue("Failed to create WAR directory", warDirectory.mkdir());

        // Create file within war directory
        FileWriter writer = new FileWriter(new File(warDirectory, "test.html"));
        writer.write(this.getName());
        writer.close();

        // Add configuration
        OfficeFloorCompiler compiler = this.source.getOfficeFloorCompiler();
        compiler.addProperty(SourceHttpResourceFactory.PROPERTY_RESOURCE_DIRECTORIES,
                warDirectory.getAbsolutePath());
        compiler.addProperty(SourceHttpResourceFactory.PROPERTY_DEFAULT_DIRECTORY_FILE_NAMES, "test.html");
        compiler.addProperty(SourceHttpResourceFactory.PROPERTY_DIRECT_STATIC_CONTENT, String.valueOf(false));

        // Start the HTTP Server
        this.source.openOfficeFloor();

        // Ensure obtain resource
        this.assertHttpRequest("/test.html", 200, this.getName());
        this.assertHttpRequest("/", 200, this.getName());

        // Change the resource
        writer = new FileWriter(new File(warDirectory, "test.html"));
        writer.write("CHANGED");
        writer.close();

        // Ensure as not keeping in direct memory that pick up changes
        this.assertHttpRequest("/test.html", 200, "CHANGED");
        this.assertHttpRequest("/", 200, "CHANGED");
    }

    /**
     * Ensure able to chain a servicer.
     */
    public void testChainServicer() throws Exception {

        // Add section to override servicing
        AutoWireSection section = this.source.addSection("SECTION", ClassSectionSource.class.getName(),
                MockChainedServicer.class.getName());
        this.source.chainServicer(section, "service", "notHandled");

        // Start the HTTP Server
        this.source.openOfficeFloor();

        // Ensure chained servicer services request
        MockChainedServicer.isServiced = false;
        this.assertHttpRequest("/chain", 200, "CHAIN SERVICED - /chain");
        assertTrue("Should be chained", MockChainedServicer.isServiced);

        // Ensure default end of chain servicing
        MockChainedServicer.isServiced = true;
        this.assertHttpRequest("/resource.html", 200, "RESOURCE");
        assertTrue("Should be chained", MockChainedServicer.isServiced);
    }

    /**
     * Provides mock functionality of chained servicer.
     */
    public static class MockChainedServicer {

        public volatile static boolean isServiced = false;

        @FlowInterface
        public static interface Flows {
            void notHandled();
        }

        public void service(ServerHttpConnection connection, Flows flows) throws IOException {

            // Flag that serviced
            isServiced = true;

            // Service
            String uri = connection.getHttpRequest().getRequestURI();
            if ("/chain".equals(uri)) {
                // Service the request
                WebApplicationAutoWireOfficeFloorSourceTest.writeResponse("CHAIN SERVICED - " + uri, connection);
            } else {
                // Write some content to indicate chained
                WebApplicationAutoWireOfficeFloorSourceTest.writeResponse("CHAIN - ", connection);

                // Hand off to next in chain
                flows.notHandled();
            }
        }
    }

    /**
     * Ensure able to specify as the last chain servicer.
     */
    public void testChainLastServicer() throws Exception {

        // Add section to override servicing
        AutoWireSection section = this.source.addSection("SECTION", ClassSectionSource.class.getName(),
                MockLastChainServicer.class.getName());
        this.source.chainServicer(section, "service", null);

        // Add another in chain (which should be ignored with log warning)
        AutoWireSection ignore = this.source.addSection("ANOTHER", ClassSectionSource.class.getName(),
                MockChainedServicer.class.getName());
        this.source.chainServicer(ignore, "service", "notHandled");

        // Start the HTTP Server
        this.source.openOfficeFloor();

        // Ensure override non-routed servicing
        MockChainedServicer.isServiced = false;
        this.assertHttpRequest("/unhandled", 200, "CHAIN END - /unhandled");
        assertFalse("Should not be part of chain", MockChainedServicer.isServiced);
    }

    /**
     * Provides mock functionality of last chain servicing.
     */
    public static class MockLastChainServicer {
        public void service(ServerHttpConnection connection) throws IOException {
            String uri = connection.getHttpRequest().getRequestURI();
            WebApplicationAutoWireOfficeFloorSourceTest.writeResponse("CHAIN END - " + uri, connection);
        }
    }

    /**
     * Asserts the HTTP request returns expected result.
     * 
     * @param uri
     *            URI to send the HTTP request.
     * @param expectedResponseStatus
     *            Expected response status.
     * @param expectedResponseEntity
     *            Expected response entity.
     * @return {@link HttpResponse}.
     */
    private HttpResponse assertHttpRequest(String uri, int expectedResponseStatus, String expectedResponseEntity) {
        return this.assertHttpRequest(uri, false, expectedResponseStatus, expectedResponseEntity);
    }

    /**
     * Asserts the HTTP Request returns expected result after a redirect.
     * 
     * @param uri
     *            URI to send the HTTP request.
     * @param isRedirect
     *            Indicates if a redirect for secure
     *            {@link ServerHttpConnection} should occur.
     * @param expectedResponseStatus
     *            Expected response status.
     * @param expectedResponseEntity
     *            Expected response entity.
     * @return {@link HttpResponse}.
     */
    private HttpResponse assertHttpRequest(String uri, boolean isRedirect, int expectedResponseStatus,
            String expectedResponseEntity) {

        // Create the request
        String url = "http://" + HOST_NAME + ":" + this.port + uri;
        HttpGet request = new HttpGet(url);

        // Provide redirect URL if expecting to redirect
        String redirectUrl = null;
        if (isRedirect) {
            redirectUrl = "https://" + HOST_NAME + ":" + this.securePort + uri + HttpRouteTask.REDIRECT_URI_SUFFIX;
        }

        // Assert HTTP request
        return this.assertHttpRequest(request, redirectUrl, expectedResponseStatus, expectedResponseEntity);
    }

    /**
     * Asserts the HTTP Request returns expected result after a redirect.
     * 
     * @param request
     *            {@link HttpUriRequest}.
     * @param redirectUrl
     *            Indicates if a redirect should occur and what is the expected
     *            redirect URL.
     * @param expectedResponseStatus
     *            Expected response status.
     * @param expectedResponseEntity
     *            Expected response entity.
     * @return {@link HttpResponse}.
     */
    private HttpResponse assertHttpRequest(HttpUriRequest request, String redirectUrl, int expectedResponseStatus,
            String expectedResponseEntity) {
        try {

            // Send the request
            HttpResponse response = this.client.execute(request);

            // Determine if redirect
            if (redirectUrl != null) {

                // Ensure appropriate redirect
                assertEquals("Should be redirect", 303, response.getStatusLine().getStatusCode());
                assertEquals("Incorrect redirect URL", redirectUrl, response.getFirstHeader("Location").getValue());

                // Consume response to allow sending next request
                response.getEntity().getContent().close();

                // Handle server relative redirect
                if (redirectUrl.startsWith("/")) {
                    redirectUrl = "http://" + HOST_NAME + ":" + this.port + redirectUrl;
                }

                // Send the redirect for response
                response = this.client.execute(new HttpGet(redirectUrl));
            }

            // Ensure obtained as expected
            String actualResponseBody = HttpTestUtil.getEntityBody(response);
            assertEquals("Incorrect response", expectedResponseEntity, actualResponseBody);

            // Ensure correct response status
            assertEquals("Should be successful", expectedResponseStatus, response.getStatusLine().getStatusCode());

            // Return the response
            return response;

        } catch (Exception ex) {
            throw fail(ex);
        }
    }

    /**
     * Obtains the class path to the file.
     * 
     * @param fileName
     *            Name of the file.
     * @return Class path to the file.
     */
    private String getClassPath(String fileName) {
        return this.getFileLocation(this.getClass(), fileName);
    }

    /**
     * Writes the response.
     * 
     * @param response
     *            Response.
     * @param connection
     *            {@link ServerHttpConnection}.
     */
    private static void writeResponse(String response, ServerHttpConnection connection) throws IOException {
        Writer writer = connection.getHttpResponse().getEntityWriter();
        writer.append(response);
    }

}