net.officefloor.launch.woof.WoofServletContainerLauncherTest.java Source code

Java tutorial

Introduction

Here is the source code for net.officefloor.launch.woof.WoofServletContainerLauncherTest.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.launch.woof;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import net.officefloor.compile.OfficeFloorCompiler;
import net.officefloor.frame.api.manage.OfficeFloor;
import net.officefloor.frame.test.OfficeFrameTestCase;
import net.officefloor.plugin.socket.server.http.HttpTestUtil;
import net.officefloor.plugin.woof.WoofOfficeFloorSource;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;

import com.google.gwt.core.ext.ServletContainer;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;

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

    /**
     * {@link CloseableHttpClient}.
     */
    private final CloseableHttpClient client = HttpTestUtil.createHttpClient();

    /**
     * Port listening for HTTP requests.
     */
    private int port;

    /**
     * Raw source files. Typically will be
     * <code>[project]/src/main/webapp</code> directory containing web
     * resources.
     */
    private File webappDirectory;

    /**
     * WAR files. Typically will be <code>[project]/target/gwt-war</code>
     * containing GWT generated content. This allows a separate directory to
     * keep raw source and generated content separate.
     */
    private File warDirectory;

    /**
     * {@link MockTreeLogger}.
     */
    private MockTreeLogger logger;

    /**
     * {@link ServletContainer}.
     */
    private ServletContainer servletContainer;

    @Override
    protected void setUp() throws Exception {

        // Specify the port
        this.port = HttpTestUtil.getAvailablePort();

        // Obtain the temp location to create necessary directories
        File tempDir = new File(System.getProperty("java.io.tmpdir"),
                this.getClass().getSimpleName() + "-" + this.getName());
        if (tempDir.exists()) {
            this.deleteDirectory(tempDir);
        }
        assertTrue("Failed to setup test", tempDir.mkdir());

        // Create the webapp directory
        this.webappDirectory = new File(tempDir, "webapp");
        assertTrue("Failed to setup src directory", this.webappDirectory.mkdir());

        // Ensure webapp directory contains web.xml
        File webInfDir = new File(this.webappDirectory, "WEB-INF");
        assertTrue("Failed to setup web app directory", webInfDir.mkdir());
        this.writeFile(webInfDir, "web.xml", "marker file");

        // Create file within webapp directory
        this.writeFile(this.webappDirectory, "source.html", "SOURCE");

        // Create the war target directory
        this.warDirectory = new File(tempDir, "war");
        assertTrue("Failed to setup target directory", this.warDirectory.mkdir());

        // Create file within target directory
        this.writeFile(this.warDirectory, "target.html", "TARGET");

        // Ensure class path resource available
        assertNotNull("Invalid testing as class path resource not available",
                Thread.currentThread().getContextClassLoader().getResourceAsStream("PUBLIC/classpath.html"));

        // Provide WoOF configuration for launcher
        WoofDevelopmentConfiguration configuration = new WoofDevelopmentConfiguration();
        configuration.setWarDirectory(this.warDirectory);
        configuration.setWebAppDirectory(this.webappDirectory);
        configuration.addResourceDirectory(this.webappDirectory);
        configuration.storeConfiguration(
                new File(this.warDirectory, WoofServletContainerLauncher.CONFIGURATION_FILE_NAME));

        // Start WoOF
        this.logger = new MockTreeLogger();
        WoofServletContainerLauncher launcher = new WoofServletContainerLauncher();
        this.servletContainer = launcher.start(this.logger, this.port, this.warDirectory);
    }

    @Override
    protected void tearDown() throws Exception {
        try {
            // Stop the client
            this.client.close();

        } finally {
            // Ensure also stop WoOF
            if (this.servletContainer != null) {
                this.servletContainer.stop();
            }
        }
    }

    /**
     * Ensure {@link OfficeFloor} able to provide content.
     */
    public void testOfficeFloorProvidingContent() throws Exception {

        // Ensure able to obtain the various files
        this.doHttpRequest("source.html", "SOURCE");
        this.doHttpRequest("target.html", "TARGET");
        this.doHttpRequest("classpath.html", "CLASSPATH");
    }

    /**
     * Ensure {@link OfficeFloor} provides changed content.
     */
    public void testOfficeFloorServingChangingContent() throws Exception {

        // Ensure correct content within target dirctory
        this.doHttpRequest("target.html", "TARGET");

        // Change the content of the resource
        this.writeFile(this.warDirectory, "target.html", "CHANGED");

        // Ensure received changed content
        this.doHttpRequest("target.html", "CHANGED");
    }

    /**
     * Ensure {@link OfficeFloor} provides new content.
     */
    public void testOfficeFloorServingNewContent() throws Exception {

        // Ensure can get existing content (ensure up and running)
        this.doHttpRequest("target.html", "TARGET");

        // Ensure resource not available
        HttpResponse response = this.client.execute(new HttpGet("http://localhost:" + this.port + "/new.html"));
        assertEquals("Should not find resource", 404, response.getStatusLine().getStatusCode());
        EntityUtils.consume(response.getEntity());

        // Provide new resource
        this.writeFile(this.warDirectory, "new.html", "NEW");

        // Ensure able to obtain new resource
        this.doHttpRequest("new.html", "NEW");
    }

    /**
     * Ensure gracefully handles failure in starting.
     */
    public void testFailureStarting() throws Exception {

        // Stop setup instance
        this.servletContainer.stop();

        // Use fresh logger to just receive failure to start
        MockTreeLogger logger = new MockTreeLogger();

        try {
            // Attempt to start new instance that has invalid configuration
            this.servletContainer = new WoofServletContainer(logger, "NAME", HttpTestUtil.getAvailablePort(),
                    this.webappDirectory, new File[0], OfficeFloorCompiler.newPropertyList()) {
                @Override
                protected WoofOfficeFloorSource createWoofOfficeFloorSource() {

                    // Create WoOF configured with invalid configuration
                    WoofOfficeFloorSource source = new WoofOfficeFloorSource();
                    source.getOfficeFloorCompiler()
                            .addProperty(WoofOfficeFloorSource.PROPERTY_WOOF_CONFIGURATION_LOCATION, "fail.woof");

                    // Return WoOF
                    return source;
                }
            };

            // Should not be successful in starting
            fail("Should not be successful in starting");

        } catch (UnableToCompleteException ex) {
            assertEquals("Incorrect cause", "(see previous log entries)", ex.getMessage());
        }

        // Validate the logging of failure to start
        String expectedMessage = "Failed to source OfficeFloor from OfficeFloorSource (source="
                + WoofOfficeFloorSource.class.getName() + ", location=auto-wire)";
        logger.validateLogMessages(expectedMessage);
    }

    /**
     * Ensure able to refresh.
     */
    public void testRefresh() throws Exception {

        // Refresh
        this.servletContainer.refresh();

        // Ensure able to obtain the various files
        this.doHttpRequest("source.html", "SOURCE");
        this.doHttpRequest("target.html", "TARGET");
        this.doHttpRequest("classpath.html", "CLASSPATH");

        // Validate logging (starting/stopping)
        this.logger.validateLogMessages("OfficeFloor (WoOF) started on port " + this.port,
                "OfficeFloor (WoOF) stopped", "OfficeFloor (WoOF) started on port " + this.port);
    }

    /**
     * Undertakes the HTTP request.
     * 
     * @param resourceUri
     *            URI of the resource.
     * @param expectedResourceContent
     *            Expected resource content.
     */
    private void doHttpRequest(String resourceUri, String expectedResourceContent) throws IOException {

        // Do the request
        HttpResponse response = this.client
                .execute(new HttpGet("http://localhost:" + this.port + "/" + resourceUri));

        // Validate the successful
        assertEquals("Should be successful", 200, response.getStatusLine().getStatusCode());

        // Validate correct content
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        response.getEntity().writeTo(buffer);
        String responseText = new String(buffer.toByteArray());
        assertEquals("Incorrect resource content", expectedResourceContent, responseText);
    }

    /**
     * Writes the {@link File} (either creating new or overwriting).
     * 
     * @param directory
     *            Directory to contain the file.
     * @param fileName
     *            {@link File} name.
     * @param fileContent
     *            Content for {@link File}.
     */
    private void writeFile(File directory, String fileName, String fileContent) throws IOException {
        Writer writer = new FileWriter(new File(directory, fileName));
        writer.append(fileContent);
        writer.close();
    }

    /**
     * Mock {@link TreeLogger} for testing.
     */
    private static class MockTreeLogger extends TreeLogger {

        /**
         * Log messages.
         */
        public List<String> messages = new ArrayList<String>();

        /**
         * Validates the log messages.
         * 
         * @param expectedMessages
         *            Expected messages.
         */
        private void validateLogMessages(String... expectedMessages) {
            assertEquals("Incorrect number of log messages", expectedMessages.length, this.messages.size());
            for (int i = 0; i < expectedMessages.length; i++) {
                assertEquals("Incorrect log message " + i, expectedMessages[i], this.messages.get(i));
            }
        }

        /*
         * ===================== TreeLogger ==============================
         */

        @Override
        public TreeLogger branch(Type type, String msg, Throwable caught, HelpInfo helpInfo) {
            fail("Should not require to branch");
            return this;
        }

        @Override
        public boolean isLoggable(Type type) {
            return true; // always log for testing
        }

        @Override
        public void log(Type type, String msg, Throwable caught, HelpInfo helpInfo) {
            this.messages.add(msg);
        }
    }

}