org.epics.archiverappliance.TomcatSetup.java Source code

Java tutorial

Introduction

Here is the source code for org.epics.archiverappliance.TomcatSetup.java

Source

/*******************************************************************************
 * Copyright (c) 2011 The Board of Trustees of the Leland Stanford Junior University
 * as Operator of the SLAC National Accelerator Laboratory.
 * Copyright (c) 2011 Brookhaven National Laboratory.
 * EPICS archiver appliance is distributed subject to a Software License Agreement found
 * in file LICENSE that is included with this distribution.
 *******************************************************************************/
package org.epics.archiverappliance;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.LogOutputStream;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.epics.archiverappliance.config.ConfigService;
import org.epics.archiverappliance.config.ConfigServiceForTests;
import org.epics.archiverappliance.config.DefaultConfigService;
import org.epics.archiverappliance.config.persistence.InMemoryPersistence;
import org.epics.archiverappliance.config.persistence.JDBM2Persistence;

/**
 * Setup Tomcat without having to import all the tomcat jars into your project.
 * @author mshankar
 *
 */
public class TomcatSetup {
    private static Logger logger = Logger.getLogger(TomcatSetup.class.getName());
    LinkedList<ExecuteWatchdog> executorWatchDogs = new LinkedList<ExecuteWatchdog>();
    LinkedList<File> cleanupFolders = new LinkedList<File>();
    private static int DEFAULT_SERVER_STARTUP_PORT = 16000;

    private void initialSetup(String testName) throws IOException {
        File testFolder = new File("tomcat_" + testName);
        if (testFolder.exists()) {
            FileUtils.deleteDirectory(testFolder);
        }
        assert (testFolder.mkdir());
        cleanupFolders.add(testFolder);

    }

    /**
     * Set up an individual tomcat with the webapps loaded.
     * We create a work folder appropriate for the test; create the webapps/logs/conf folders and then call catalina.sh run.
     * @throws Exception
     */
    public void setUpWebApps(String testName) throws Exception {
        initialSetup(testName);
        createAndStartTomcatInstance(testName, ConfigServiceForTests.TESTAPPLIANCE0,
                ConfigServiceForTests.RETRIEVAL_TEST_PORT, DEFAULT_SERVER_STARTUP_PORT);
    }

    /**
     * Set up a cluster of tomcat instances.
     * Note that these are NOT clustered using tomcat's clustering technology (which is geared towards session replication and the such).
     * @param clusterCount
     * @throws Exception
     */
    public void setUpClusterWithWebApps(String testName, int clusterCount) throws Exception {
        initialSetup(testName);
        for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) {
            logger.info("Starting up " + "appliance" + clusterIndex);
            createAndStartTomcatInstance(testName, "appliance" + clusterIndex,
                    ConfigServiceForTests.RETRIEVAL_TEST_PORT + clusterIndex,
                    DEFAULT_SERVER_STARTUP_PORT + clusterIndex);
            logger.info("Done starting up " + "appliance" + clusterIndex);
        }
    }

    public void tearDown() throws Exception {
        for (ExecuteWatchdog watchdog : executorWatchDogs) {
            // We brutally kill the process
            watchdog.destroyProcess();
            try {
                Thread.sleep(10 * 1000);
            } catch (Exception ex) {
            }
            assert (watchdog.killedProcess());
        }

        for (File cleanupFolder : cleanupFolders) {
            logger.debug("Cleaning up folder " + cleanupFolder.getAbsolutePath());
            FileUtils.deleteDirectory(cleanupFolder);
        }

        // So many timeouts; if we are running multiple tests in sequence; the Tomcat socket seems to wind up in a TIME_WAIT site for about 2 minutes.
        // Setting the net.netfilter.nf_conntrack_tcp_timeout_time_wait (using sysctl) seems to hurt other behaviours...
        // Sigh!
        try {
            Thread.sleep(3 * 60 * 1000);
        } catch (Exception ex) {
        }
    }

    private void createAndStartTomcatInstance(String testName, final String applianceName, int port,
            int startupPort) throws IOException {
        File workFolder = makeTomcatFolders(testName, applianceName, port, startupPort);
        HashMap<String, String> environment = createEnvironment(testName, applianceName);
        File logsFolder = new File(workFolder, "logs");
        assert (logsFolder.exists());

        CommandLine cmdLine = new CommandLine(
                System.getenv("TOMCAT_HOME") + File.separator + "bin" + File.separator + "catalina.sh");
        cmdLine.addArgument("run");
        DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
        final CountDownLatch latch = new CountDownLatch(1);
        PumpStreamHandler pump = new PumpStreamHandler(new LogOutputStream() {
            @Override
            protected void processLine(String msg, int level) {
                if (msg != null && msg.contains("All components in this appliance have started up")) {
                    logger.info(applianceName + " has started up.");
                    latch.countDown();
                }
                System.out.println(msg);
            }
        }, System.err);

        ExecuteWatchdog watchdog = new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT);
        Executor executor = new DefaultExecutor();
        executor.setExitValue(1);
        executor.setWatchdog(watchdog);
        executor.setStreamHandler(pump);
        executor.setWorkingDirectory(logsFolder);
        executor.execute(cmdLine, environment, resultHandler);
        executorWatchDogs.add(watchdog);

        // We wait for some time to make sure the server started up
        try {
            latch.await(2, TimeUnit.MINUTES);
        } catch (InterruptedException ex) {
        }
        logger.info("Done starting tomcat for the testing.");

    }

    /**
     * @param testName
     * @param applianceName
     * @param port
     * @param startupPort
     * @return Returns the CATALINA_BASE for this instance
     * @throws IOException
     */
    private File makeTomcatFolders(String testName, String applianceName, int port, int startupPort)
            throws IOException {
        File testFolder = new File("tomcat_" + testName);
        assert (testFolder.exists());
        File workFolder = new File(testFolder, applianceName);
        assert (workFolder.mkdir());

        File webAppsFolder = new File(workFolder, "webapps");
        assert (webAppsFolder.mkdir());
        File logsFolder = new File(workFolder, "logs");
        assert (logsFolder.mkdir());
        FileUtils.copyFile(new File("log4j.properties"), new File(logsFolder, "log4j.properties"));
        File tempFolder = new File(workFolder, "temp");
        tempFolder.mkdir();

        logger.debug("Copying the webapps wars to " + webAppsFolder.getAbsolutePath());
        FileUtils.copyFile(new File("../mgmt.war"), new File(webAppsFolder, "mgmt.war"));
        FileUtils.copyFile(new File("../retrieval.war"), new File(webAppsFolder, "retrieval.war"));
        FileUtils.copyFile(new File("../etl.war"), new File(webAppsFolder, "etl.war"));
        FileUtils.copyFile(new File("../engine.war"), new File(webAppsFolder, "engine.war"));

        File confOriginal = new File(System.getenv("TOMCAT_HOME"), "conf_original");
        File confFolder = new File(workFolder, "conf");
        logger.debug("Copying the config from " + confOriginal.getAbsolutePath() + " to "
                + confFolder.getAbsolutePath());
        if (!confOriginal.exists()) {
            throw new IOException(
                    "For the tomcat tests to work, we expect that when you extract the tomcat distrbution to "
                            + System.getenv("TOMCAT_HOME")
                            + ", you copy the pristine conf folder to a folder called conf_original. This folder "
                            + confOriginal.getAbsolutePath() + " does not seem to exist.");
        }
        // We expect that when you set up TOMCAT_HOME, 
        FileUtils.copyDirectory(confOriginal, confFolder);

        // We then replace the server.xml with one we generate.
        // TomcatSampleServer.xml is a simple MessageFormat template with entries for 
        // 0) server http port
        // 1) server startup port
        String serverXML = new String(
                Files.readAllBytes(Paths.get("./src/test/org/epics/archiverappliance/TomcatSampleServer.xml")));
        String formattedServerXML = MessageFormat.format(serverXML, port, startupPort);
        Files.write(Paths.get(confFolder.getAbsolutePath(), "server.xml"), formattedServerXML.getBytes(),
                StandardOpenOption.TRUNCATE_EXISTING);
        logger.debug("Done generating server.xml");

        return workFolder;
    }

    private HashMap<String, String> createEnvironment(String testName, String applianceName) {
        HashMap<String, String> environment = new HashMap<String, String>();
        environment.putAll(System.getenv());
        environment.remove("CLASSPATH");
        environment.put("CATALINA_HOME", System.getenv("TOMCAT_HOME"));
        File workFolder = new File("tomcat_" + testName + File.separator + applianceName);
        assert (workFolder.exists());
        environment.put("CATALINA_BASE", workFolder.getAbsolutePath());

        environment.put(ConfigService.ARCHAPPL_CONFIGSERVICE_IMPL, ConfigServiceForTests.class.getName());
        environment.put(DefaultConfigService.SITE_FOR_UNIT_TESTS_NAME,
                DefaultConfigService.SITE_FOR_UNIT_TESTS_VALUE);

        environment.put(ConfigService.ARCHAPPL_MYIDENTITY, applianceName);
        if (!System.getProperties().containsKey(ConfigService.ARCHAPPL_APPLIANCES)) {
            environment.put(ConfigService.ARCHAPPL_APPLIANCES,
                    new File("./src/sitespecific/tests/classpathfiles/appliances.xml").getAbsolutePath());
        } else {
            environment.put(ConfigService.ARCHAPPL_APPLIANCES,
                    (String) System.getProperties().get(ConfigService.ARCHAPPL_APPLIANCES));
        }

        if (!System.getProperties().containsKey(ConfigService.ARCHAPPL_PERSISTENCE_LAYER) || System.getProperties()
                .get(ConfigService.ARCHAPPL_PERSISTENCE_LAYER).equals(InMemoryPersistence.class.getName())) {
            environment.put(ConfigService.ARCHAPPL_PERSISTENCE_LAYER,
                    "org.epics.archiverappliance.config.persistence.InMemoryPersistence");
        } else {
            String persistenceFile = (String) System.getProperties().get(JDBM2Persistence.ARCHAPPL_JDBM2_FILENAME);
            logger.info("Persistence layer is provided by "
                    + System.getProperties().get(ConfigService.ARCHAPPL_PERSISTENCE_LAYER));
            assert (persistenceFile != null);
            String persistenceFileForMember = persistenceFile.replace(".jdbm2", "_" + applianceName + ".jdbm2");
            environment.put(ConfigService.ARCHAPPL_PERSISTENCE_LAYER,
                    (String) System.getProperties().get(ConfigService.ARCHAPPL_PERSISTENCE_LAYER));
            environment.put(JDBM2Persistence.ARCHAPPL_JDBM2_FILENAME, persistenceFileForMember);
            logger.info("Persistence file for member " + persistenceFileForMember);
        }

        overrideEnvWithSystemProperty(environment, "ARCHAPPL_SHORT_TERM_FOLDER");
        overrideEnvWithSystemProperty(environment, "ARCHAPPL_MEDIUM_TERM_FOLDER");
        overrideEnvWithSystemProperty(environment, "ARCHAPPL_LONG_TERM_FOLDER");

        if (logger.isDebugEnabled()) {
            for (String key : environment.keySet()) {
                logger.debug("Env " + key + "=" + environment.get(key));
            }
        }

        return environment;
    }

    private static void overrideEnvWithSystemProperty(HashMap<String, String> environment, String key) {
        if (System.getProperties().containsKey(key)) {
            String value = (String) System.getProperties().get(key);
            logger.debug("Overriding " + key + " from the system properties " + value);
            environment.put(key, value);
        }

    }

}