net.officefloor.plugin.servlet.webxml.AbstractWebXmlTestCase.java Source code

Java tutorial

Introduction

Here is the source code for net.officefloor.plugin.servlet.webxml.AbstractWebXmlTestCase.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.servlet.webxml;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import net.officefloor.compile.OfficeFloorCompiler;
import net.officefloor.compile.properties.Property;
import net.officefloor.compile.properties.PropertyList;
import net.officefloor.compile.spi.section.ManagedObjectDependency;
import net.officefloor.compile.spi.section.SectionDesigner;
import net.officefloor.compile.spi.section.SectionInput;
import net.officefloor.compile.spi.section.SectionManagedObject;
import net.officefloor.compile.spi.section.SectionManagedObjectSource;
import net.officefloor.compile.spi.section.SectionObject;
import net.officefloor.compile.spi.section.SectionOutput;
import net.officefloor.compile.spi.section.SectionTask;
import net.officefloor.compile.spi.section.SectionWork;
import net.officefloor.compile.spi.section.TaskFlow;
import net.officefloor.compile.spi.section.TaskObject;
import net.officefloor.compile.spi.section.source.SectionSourceContext;
import net.officefloor.compile.test.issues.FailTestCompilerIssues;
import net.officefloor.frame.api.manage.OfficeFloor;
import net.officefloor.frame.internal.structure.FlowInstigationStrategyEnum;
import net.officefloor.frame.internal.structure.ManagedObjectScope;
import net.officefloor.frame.spi.source.ResourceSource;
import net.officefloor.frame.test.OfficeFrameTestCase;
import net.officefloor.model.impl.repository.classloader.ClassLoaderConfigurationContext;
import net.officefloor.model.impl.repository.xml.XmlConfigurationContext;
import net.officefloor.model.repository.ConfigurationContext;
import net.officefloor.model.repository.ConfigurationItem;
import net.officefloor.plugin.servlet.container.source.HttpServletWorkSource;
import net.officefloor.plugin.servlet.context.OfficeServletContext;
import net.officefloor.plugin.servlet.context.source.OfficeServletContextManagedObjectSource;
import net.officefloor.plugin.servlet.filter.configuration.FilterInstance;
import net.officefloor.plugin.servlet.filter.configuration.FilterMappings;
import net.officefloor.plugin.servlet.host.ServletServer;
import net.officefloor.plugin.servlet.route.source.ServletRouteWorkSource;
import net.officefloor.plugin.servlet.security.HttpServletSecurity;
import net.officefloor.plugin.servlet.webxml.model.WebAppModel;
import net.officefloor.plugin.socket.server.http.HttpTestUtil;
import net.officefloor.plugin.socket.server.http.ServerHttpConnection;
import net.officefloor.plugin.web.http.application.HttpRequestState;
import net.officefloor.plugin.web.http.security.HttpSecurity;
import net.officefloor.plugin.web.http.session.HttpSession;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.CloseableHttpClient;

/**
 * Abstract functionality for <code>web.xml</code> testing.
 * 
 * @author Daniel Sagenschneider
 */
public abstract class AbstractWebXmlTestCase extends OfficeFrameTestCase {

    /**
     * Created {@link CloseableHttpClient} instances.
     */
    private final List<CloseableHttpClient> clients = new LinkedList<>();

    /**
     * {@link OfficeFloor} for the Servlet application.
     */
    private OfficeFloor officeFloor = null;

    /**
     * Port that {@link Servlet} application will be listening on for requests.
     */
    private int port;

    @Override
    protected void tearDown() throws Exception {

        // Stop the HTTP clients
        for (CloseableHttpClient client : this.clients) {
            client.close();
        }

        // Stop servlet application (if started)
        if (this.officeFloor != null) {
            this.stopServletApplication();
        }
    }

    /**
     * Starts the {@link Servlet} application for the input <code>web.xml</code>
     * file.
     * 
     * @param webXmlFileName
     *            Name of the <code>web.xml</code> file to configure the
     *            {@link Servlet} application.
     */
    protected void startServletApplication(String webXmlFileName) {
        try {
            // Obtain the port for the application
            this.port = HttpTestUtil.getAvailablePort();

            // Create the configuration context
            XmlConfigurationContext xmlContext = new XmlConfigurationContext(this, "OfficeFloor.xml");
            xmlContext.addTag("port", String.valueOf(this.port));
            xmlContext.addTag("web.xml.file.name", webXmlFileName);
            ResourceSource resourceSource = new MockResourceSource(xmlContext,
                    new ClassLoaderConfigurationContext(this.getClass().getClassLoader()));

            // Create and configure the compiler
            OfficeFloorCompiler compiler = OfficeFloorCompiler.newOfficeFloorCompiler(null);
            compiler.addResources(resourceSource);
            compiler.setCompilerIssues(new FailTestCompilerIssues());

            // Compiler the Office Floor
            this.officeFloor = compiler.compile("office-floor");

            // Open (start) the Office Floor for the Servlet application
            this.officeFloor.openOfficeFloor();

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

    /**
     * Creates a {@link HttpClient}.
     * 
     * @return {@link HttpClient}.
     */
    protected HttpClient createHttpClient() {
        CloseableHttpClient client = HttpTestUtil.createHttpClient();
        this.clients.add(client);
        return client;
    }

    /**
     * Obtains the URL of the server ({@link Servlet} application).
     * 
     * @return URL of the server.
     */
    protected String getServerUrl() {
        return "http://localhost:" + this.port;
    }

    /**
     * Stops the {@link Servlet} application.
     */
    protected void stopServletApplication() {
        this.officeFloor.closeOfficeFloor();
        this.officeFloor = null;
    }

    /**
     * Ensure correct design.
     */
    protected void doDesignTest(String webXmlFileName, DesignRecorder recorder) {
        try {

            // Mocks
            final SectionSourceContext context = this.createMock(SectionSourceContext.class);
            final SectionDesigner designer = this.createMock(SectionDesigner.class);

            // Obtain the web.xml configuration
            File webXmlConfiguration = this.findFile(this.getClass(), webXmlFileName);

            // Record obtaining the web.xml configuration
            this.recordReturn(context, context.getSectionLocation(), webXmlFileName);
            this.recordReturn(context, context.getResource(webXmlFileName),
                    new FileInputStream(webXmlConfiguration));

            // Record obtaining the meta-data for unmarshalling the web.xml
            String unmarshallerLocation = WebAppModel.class.getPackage().getName().replace('.', '/')
                    + "/UnmarshalWebXml.xml";
            this.recordReturn(context, context.getResource(unmarshallerLocation),
                    this.getClass().getClassLoader().getResourceAsStream(unmarshallerLocation));

            // Do further recording
            if (recorder != null) {
                recorder.designer = designer;
                recorder.record(designer);
            }

            // Test
            this.replayMockObjects();
            new WebXmlSectionSource().sourceSection(designer, context);
            this.verifyMockObjects();

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

    /**
     * Records design of section.
     */
    protected abstract class DesignRecorder {

        /**
         * Service {@link SectionInput}.
         */
        protected final SectionInput SERVICE_INPUT = createMock(SectionInput.class);

        /**
         * Unhandled {@link SectionOutput}.
         */
        protected final SectionOutput UNHANDLED_OUTPUT = createMock(SectionOutput.class);

        /**
         * {@link ServletException} {@link SectionOutput}.
         */
        protected final SectionOutput SERVLET_EXCEPTION_OUTPUT = createMock(SectionOutput.class);

        /**
         * {@link IOException} {@link SectionOutput}.
         */
        protected final SectionOutput IO_EXCEPTION_OUTPUT = createMock(SectionOutput.class);

        /**
         * {@link ServletServer} {@link SectionObject}.
         */
        protected final SectionObject SERVLET_SERVER_OBJECT = createMock(SectionObject.class);

        /**
         * {@link ServerHttpConnection} {@link SectionObject}.
         */
        protected final SectionObject HTTP_CONNECTION_OBJECT = createMock(SectionObject.class);

        /**
         * Request attributes {@link SectionObject}.
         */
        protected final SectionObject REQUEST_ATTRIBUTES_OBJECT = createMock(SectionObject.class);

        /**
         * {@link HttpSession} {@link SectionObject}.
         */
        protected final SectionObject HTTP_SESSION_OBJECT = createMock(SectionObject.class);

        /**
         * {@link HttpSecurity} {@link SectionObject}.
         */
        protected final SectionObject HTTP_SECURITY_OBJECT = createMock(SectionObject.class);

        /**
         * {@link OfficeServletContext} {@link SectionManagedObject}.
         */
        protected final SectionManagedObject OFFICE_SERVLET_CONTEXT_MO = createMock(SectionManagedObject.class);

        /**
         * {@link FilterInstance} instances.
         */
        protected final List<FilterInstance> filters = new LinkedList<FilterInstance>();

        /**
         * {@link FilterMappings}.
         */
        protected final FilterMappings filterMappings = new FilterMappings();

        /**
         * {@link MimeMapping} instances.
         */
        protected final List<MimeMapping> mimeMappings = new LinkedList<MimeMapping>();

        /**
         * {@link SectionDesigner}.
         */
        private SectionDesigner designer;

        /**
         * Records design of the {@link Servlet} application section.
         * 
         * @param designer
         *            Mock {@link SectionDesigner} to record design.
         */
        abstract void record(SectionDesigner designer);

        /**
         * Records initialising.
         */
        protected void recordInit() {

            // Record the input
            recordReturn(this.designer, this.designer.addSectionInput("service", null), SERVICE_INPUT);

            // Record the outputs
            recordReturn(this.designer, this.designer.addSectionOutput("unhandled", null, false), UNHANDLED_OUTPUT);
            recordReturn(this.designer, this.designer.addSectionOutput(ServletException.class.getSimpleName(),
                    ServletException.class.getName(), true), SERVLET_EXCEPTION_OUTPUT);
            recordReturn(this.designer, this.designer.addSectionOutput(IOException.class.getSimpleName(),
                    IOException.class.getName(), true), IO_EXCEPTION_OUTPUT);

            // Record the objects
            recordReturn(this.designer,
                    this.designer.addSectionObject("SERVLET_SERVER", ServletServer.class.getName()),
                    SERVLET_SERVER_OBJECT);
            recordReturn(this.designer,
                    this.designer.addSectionObject("HTTP_CONNECTION", ServerHttpConnection.class.getName()),
                    HTTP_CONNECTION_OBJECT);
            recordReturn(this.designer,
                    this.designer.addSectionObject("REQUEST_ATTRIBUTES", HttpRequestState.class.getName()),
                    REQUEST_ATTRIBUTES_OBJECT);
            recordReturn(this.designer, this.designer.addSectionObject("HTTP_SESSION", HttpSession.class.getName()),
                    HTTP_SESSION_OBJECT);
            recordReturn(this.designer,
                    this.designer.addSectionObject("HTTP_SECURITY", HttpServletSecurity.class.getName()),
                    HTTP_SECURITY_OBJECT);
        }

        /**
         * Adds a {@link FilterInstance} for recording.
         * 
         * @param filter
         *            {@link FilterInstance}.
         */
        protected void recordFilter(FilterInstance filter) {
            this.filters.add(filter);
        }

        /**
         * Obtains the {@link FilterMappings}.
         * 
         * @return {@link FilterMappings}.
         */
        protected FilterMappings getFilterMappings() {
            return this.filterMappings;
        }

        /**
         * Records a MIME mapping.
         * 
         * @param extension
         *            Extension.
         * @param mimeType
         *            MIME type.
         */
        protected void recordMimeMapping(String extension, String mimeType) {
            this.mimeMappings.add(new MimeMapping(extension, mimeType));
        }

        /**
         * Records creating the {@link OfficeServletContext}.
         * 
         * @param contextParamNameValuePairs
         *            Expected context parameters.
         */
        protected void recordOfficeServletContext(String... contextParamNameValuePairs) {

            // Record loading the office servlet context managed object source
            final SectionManagedObjectSource source = createMock(SectionManagedObjectSource.class);
            recordReturn(this.designer, this.designer.addSectionManagedObjectSource("OfficeServletContext",
                    OfficeServletContextManagedObjectSource.class.getName()), source);
            source.addProperty(OfficeServletContextManagedObjectSource.PROPERTY_SERVLET_CONTEXT_NAME,
                    "OfficeFloor");

            // Record the context parameters
            for (int i = 0; i < contextParamNameValuePairs.length; i += 2) {
                String name = contextParamNameValuePairs[i];
                String value = contextParamNameValuePairs[i + 1];
                source.addProperty(OfficeServletContextManagedObjectSource.PROPERTY_PREFIX_INIT_PARAMETER + name,
                        value);
            }

            // Record loading the office servlet context managed object
            recordReturn(source, source.addSectionManagedObject("OfficeServletContext", ManagedObjectScope.PROCESS),
                    OFFICE_SERVLET_CONTEXT_MO);

            // Record the MIME mappings
            for (MimeMapping mapping : this.mimeMappings) {
                source.addProperty(
                        OfficeServletContextManagedObjectSource.PROPERTY_PREFIX_FILE_EXTENSION_TO_MIME_TYPE
                                + mapping.extension,
                        mapping.mimeType);
            }

            // Record linking servlet server dependency
            final ManagedObjectDependency servletServerDependency = createMock(ManagedObjectDependency.class);
            recordReturn(OFFICE_SERVLET_CONTEXT_MO,
                    OFFICE_SERVLET_CONTEXT_MO.getManagedObjectDependency("SERVLET_SERVER"),
                    servletServerDependency);
            this.designer.link(servletServerDependency, SERVLET_SERVER_OBJECT);

            // Create properties for the filters
            PropertyList filterProperties = OfficeFloorCompiler.newPropertyList();
            for (FilterInstance filter : this.filters) {
                filter.outputProperties(filterProperties);
            }
            this.filterMappings.outputProperties(filterProperties);

            // Record the filter properties
            for (Property filterProperty : filterProperties) {
                source.addProperty(filterProperty.getName(), filterProperty.getValue());
            }
        }

        /**
         * Records the {@link ServletRouteWorkSource}.
         */
        protected void recordRouteService() {

            // Record the route work and its task
            final SectionWork work = createMock(SectionWork.class);
            recordReturn(this.designer,
                    this.designer.addSectionWork("Route", ServletRouteWorkSource.class.getName()), work);
            final SectionTask task = createMock(SectionTask.class);
            recordReturn(work, work.addSectionTask("route", ServletRouteWorkSource.TASK_ROUTE), task);
            this.designer.link(SERVICE_INPUT, task);

            // Record linking unhandled flow
            final TaskFlow unhandledFlow = createMock(TaskFlow.class);
            recordReturn(task, task.getTaskFlow("UNHANDLED"), unhandledFlow);
            this.designer.link(unhandledFlow, UNHANDLED_OUTPUT, FlowInstigationStrategyEnum.SEQUENTIAL);

            // Record linking HTTP connection
            final TaskObject httpConnection = createMock(TaskObject.class);
            recordReturn(task, task.getTaskObject("HTTP_CONNECTION"), httpConnection);
            this.designer.link(httpConnection, HTTP_CONNECTION_OBJECT);

            // Record linking Office Servlet Context
            final TaskObject officeServletContext = createMock(TaskObject.class);
            recordReturn(task, task.getTaskObject("OFFICE_SERVLET_CONTEXT"), officeServletContext);
            this.designer.link(officeServletContext, OFFICE_SERVLET_CONTEXT_MO);
        }

        /**
         * Records creating a {@link HttpServlet}.
         * 
         * @param servletName
         *            {@link Servlet} name.
         * @param servletMapping
         *            {@link Servlet} mapping.
         * @param initParamNameValues
         *            Init param name value pairs.
         * @return Constructed {@link SectionTask} for the {@link HttpServlet}.
         */
        protected SectionTask recordHttpServlet(String servletName, String servletMapping,
                String... initParamNameValues) {

            // Record creating the work with parameters
            final SectionWork work = createMock(SectionWork.class);
            recordReturn(this.designer,
                    this.designer.addSectionWork(servletName, HttpServletWorkSource.class.getName()), work);
            work.addProperty(HttpServletWorkSource.PROPERTY_SERVLET_NAME, servletName);
            work.addProperty(HttpServletWorkSource.PROPERTY_HTTP_SERVLET_CLASS_NAME,
                    MockHttpServlet.class.getName());
            work.addProperty(HttpServletWorkSource.PROPERTY_SERVLET_MAPPINGS, servletMapping);
            for (int i = 0; i < initParamNameValues.length; i += 2) {
                String name = initParamNameValues[i];
                String value = initParamNameValues[i + 1];
                work.addProperty(HttpServletWorkSource.PROPERTY_PREFIX_INIT_PARAMETER + name, value);
            }

            // Record creating the task
            final SectionTask task = createMock(SectionTask.class);
            recordReturn(work, work.addSectionTask("service-by-" + servletName, "service"), task);

            // Record linking the servicer mapping
            final TaskObject servicerMapping = createMock(TaskObject.class);
            recordReturn(task, task.getTaskObject("SERVICER_MAPPING"), servicerMapping);
            servicerMapping.flagAsParameter();

            // Record linking the office servlet context
            final TaskObject officeServletContext = createMock(TaskObject.class);
            recordReturn(task, task.getTaskObject("OFFICE_SERVLET_CONTEXT"), officeServletContext);
            this.designer.link(officeServletContext, OFFICE_SERVLET_CONTEXT_MO);

            // Record linking the HTTP connection
            final TaskObject httpConnection = createMock(TaskObject.class);
            recordReturn(task, task.getTaskObject("HTTP_CONNECTION"), httpConnection);
            this.designer.link(httpConnection, HTTP_CONNECTION_OBJECT);

            // Record linking the request attributes
            final TaskObject requestAttributes = createMock(TaskObject.class);
            recordReturn(task, task.getTaskObject("REQUEST_ATTRIBUTES"), requestAttributes);
            this.designer.link(requestAttributes, REQUEST_ATTRIBUTES_OBJECT);

            // Record linking the HTTP session
            final TaskObject httpSession = createMock(TaskObject.class);
            recordReturn(task, task.getTaskObject("HTTP_SESSION"), httpSession);
            this.designer.link(httpSession, HTTP_SESSION_OBJECT);

            // Record linking the HTTP security
            final TaskObject httpSecurity = createMock(TaskObject.class);
            recordReturn(task, task.getTaskObject("HTTP_SECURITY"), httpSecurity);
            this.designer.link(httpSecurity, HTTP_SECURITY_OBJECT);

            // Record linking the servlet exception escalation
            final TaskFlow servletException = createMock(TaskFlow.class);
            recordReturn(task, task.getTaskEscalation(ServletException.class.getName()), servletException);
            this.designer.link(servletException, SERVLET_EXCEPTION_OUTPUT, FlowInstigationStrategyEnum.SEQUENTIAL);

            // Record linking the io exception escalation
            final TaskFlow ioException = createMock(TaskFlow.class);
            recordReturn(task, task.getTaskEscalation(IOException.class.getName()), ioException);
            this.designer.link(ioException, IO_EXCEPTION_OUTPUT, FlowInstigationStrategyEnum.SEQUENTIAL);

            // Return the task
            return task;
        }
    }

    /**
     * Mock {@link ConfigurationContext} for testing.
     */
    private class MockResourceSource implements ResourceSource {

        /**
         * Delegate {@link ConfigurationContext} instances.
         */
        private final ConfigurationContext[] delegates;

        /**
         * Initiate.
         * 
         * @param delegates
         *            Delegate {@link ConfigurationContext} instances.
         */
        public MockResourceSource(ConfigurationContext... delegates) {
            this.delegates = delegates;
        }

        /*
         * ======================= ResourceSource =======================
         */

        @Override
        public InputStream sourceResource(String location) {
            try {
                // Return first available item from a delegate
                for (ConfigurationContext delegate : this.delegates) {
                    ConfigurationItem item = delegate.getConfigurationItem(location);
                    if (item != null) {
                        return item.getConfiguration();
                    }
                }

                // As here no item available from any delegate
                return null;

            } catch (Exception ex) {
                return null;
            }
        }
    }

    /**
     * MIME mapping.
     */
    private static class MimeMapping {

        /**
         * Extension.
         */
        public final String extension;

        /**
         * MIME type.
         */
        public final String mimeType;

        /**
         * Initiate.
         * 
         * @param extension
         *            Extension.
         * @param mimeType
         *            MIME type.
         */
        public MimeMapping(String extension, String mimeType) {
            this.extension = extension;
            this.mimeType = mimeType;
        }
    }

}