/******************************************************************************* * 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); } } }