diff --git a/jetty-spdy/spdy-core/pom.xml b/jetty-spdy/spdy-core/pom.xml index 218da99801d..935c4846da6 100644 --- a/jetty-spdy/spdy-core/pom.xml +++ b/jetty-spdy/spdy-core/pom.xml @@ -6,9 +6,9 @@ 8.1.13-SNAPSHOT - 4.0.0 - spdy-core - Jetty :: SPDY :: Core + 4.0.0 + spdy-core + Jetty :: SPDY :: Core http://www.eclipse.org/jetty @@ -16,21 +16,21 @@ jetty-util ${project.version} - - junit - junit - test - - - org.hamcrest - hamcrest-all - test - - - org.mockito - mockito-core - test - + + junit + junit + test + + + org.hamcrest + hamcrest-library + test + + + org.mockito + mockito-core + test + org.slf4j slf4j-log4j12 diff --git a/jetty-spdy/spdy-jetty/pom.xml b/jetty-spdy/spdy-jetty/pom.xml index 57e63f81c2e..bc3e4a53920 100644 --- a/jetty-spdy/spdy-jetty/pom.xml +++ b/jetty-spdy/spdy-jetty/pom.xml @@ -64,13 +64,13 @@ junit junit - test + test - org.hamcrest - hamcrest-all - test - + org.hamcrest + hamcrest-library + test + org.slf4j slf4j-log4j12 diff --git a/pom.xml b/pom.xml index fdea9aa440b..630ed953c9d 100644 --- a/pom.xml +++ b/pom.xml @@ -510,7 +510,7 @@ org.eclipse.jetty.toolchain jetty-test-helper - 2.0 + 2.5 org.slf4j @@ -530,17 +530,22 @@ junit junit - 4.8.1 + 4.11 - org.hamcrest - hamcrest-all - 1.1 + org.hamcrest + hamcrest-core + 1.3 + + + org.hamcrest + hamcrest-library + 1.3 org.mockito mockito-core - 1.8.5 + 1.9.5 diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/JmxServiceTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/JmxServiceTest.java index 251ba623ea7..6f771c0bed1 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/JmxServiceTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/JmxServiceTest.java @@ -18,21 +18,18 @@ package org.eclipse.jetty.test.monitor; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import javax.management.MBeanServerConnection; - import org.eclipse.jetty.client.ContentExchange; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.http.HttpMethods; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.monitor.JMXMonitor; -import org.eclipse.jetty.toolchain.jmx.JmxServiceConnection; -import org.eclipse.jetty.toolchain.test.JettyDistro; +import org.eclipse.jetty.test.support.JettyDistro; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/ProgramConfigTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/ProgramConfigTest.java index 840f3b05e3f..9d4e496b38a 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/ProgramConfigTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/ProgramConfigTest.java @@ -18,15 +18,13 @@ package org.eclipse.jetty.test.monitor; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.io.IOException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import javax.management.MBeanServerConnection; - import org.eclipse.jetty.client.ContentExchange; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.http.HttpMethods; @@ -40,8 +38,7 @@ import org.eclipse.jetty.monitor.jmx.MonitorAction; import org.eclipse.jetty.monitor.triggers.GreaterThanAttrEventTrigger; import org.eclipse.jetty.monitor.triggers.LessThanOrEqualToAttrEventTrigger; import org.eclipse.jetty.monitor.triggers.OrEventTrigger; -import org.eclipse.jetty.toolchain.jmx.JmxServiceConnection; -import org.eclipse.jetty.toolchain.test.JettyDistro; +import org.eclipse.jetty.test.support.JettyDistro; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.ExecutorThreadPool; diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/XmlConfigTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/XmlConfigTest.java index 916028aa264..17152b54bc4 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/XmlConfigTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/monitor/XmlConfigTest.java @@ -18,7 +18,7 @@ package org.eclipse.jetty.test.monitor; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.io.IOException; import java.util.Random; @@ -30,7 +30,7 @@ import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.http.HttpMethods; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.monitor.JMXMonitor; -import org.eclipse.jetty.toolchain.test.JettyDistro; +import org.eclipse.jetty.test.support.JettyDistro; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java new file mode 100644 index 00000000000..99ce5ffdc29 --- /dev/null +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/JettyDistro.java @@ -0,0 +1,874 @@ +// +// ======================================================================== +// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.test.support; +// +//======================================================================== +//------------------------------------------------------------------------ +//All rights reserved. This program and the accompanying materials +//are made available under the terms of the Eclipse Public License v1.0 +//and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +//You may elect to redistribute this code under either of these licenses. +//======================================================================== +// + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jetty.toolchain.test.FS; +import org.eclipse.jetty.toolchain.test.IO; +import org.eclipse.jetty.toolchain.test.JAR; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.OS; +import org.eclipse.jetty.toolchain.test.PathAssert; +import org.eclipse.jetty.toolchain.test.TestingDir; +import org.junit.Assert; + +/** + * Basic process based executor for using the Jetty Distribution along with custom configurations to perform basic + *

+ * Allows for a test specific directory, that is a copied jetty-distribution, and then modified for the test specific testing required. + *

+ * Requires that you setup the maven-dependency-plugin appropriately for the base distribution you want to use, along with any other dependencies (wars, libs, + * etc..) that you may need from other maven projects. + *

+ * Maven Dependency Plugin Setup: + * + *

+ *  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ *    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ * 
+ *   <!-- Common Destination Directories -->
+ * 
+ *   <properties>
+ *     <test-wars-dir>${project.build.directory}/test-wars</test-wars-dir>
+ *     <test-libs-dir>${project.build.directory}/test-libs</test-libs-dir>
+ *     <test-distro-dir>${project.build.directory}/test-dist</test-distro-dir>
+ *   </properties>
+ * 
+ *   <build>
+ *     <plugins>
+ *       <plugin>
+ *         <groupId>org.apache.maven.plugins</groupId>
+ *         <artifactId>maven-dependency-plugin</artifactId>
+ *         <version>2.1</version>
+ *         <executions>
+ * 
+ *           <!-- Copy LIB and WAR dependencies into place that JettyDistro can use them -->
+ * 
+ *           <execution>
+ *             <id>test-lib-war-copy</id>
+ *             <phase>process-test-resources</phase>
+ *             <goals>
+ *               <goal>copy</goal>
+ *             </goals>
+ *             <configuration>
+ *               <artifactItems>
+ *                 <artifactItem>
+ *                   <groupId>org.mortbay.jetty.testwars</groupId>
+ *                   <artifactId>test-war-java_util_logging</artifactId>
+ *                   <version>7.3.0</version>
+ *                   <type>war</type>
+ *                   <outputDirectory>${test-wars-dir}</outputDirectory>
+ *                 </artifactItem>
+ *                 <artifactItem>
+ *                   <groupId>org.mortbay.jetty</groupId>
+ *                   <artifactId>jetty-aspect-servlet-api-2.5</artifactId>
+ *                   <version>7.3.0</version>
+ *                   <type>jar</type>
+ *                   <outputDirectory>${test-libs-dir}</outputDirectory>
+ *                 </artifactItem>
+ *               </artifactItems>
+ *               <overWriteIfNewer>true</overWriteIfNewer>
+ *               <overWrite>true</overWrite>
+ *               <stripVersion>true</stripVersion>
+ *             </configuration>
+ *           </execution>
+ * 
+ *           <!-- Extract Jetty DISTRIBUTION into place that JettyDistro can use it -->
+ * 
+ *           <execution>
+ *             <id>unpack-test-dist</id>
+ *             <phase>process-test-resources</phase>
+ *             <goals>
+ *               <goal>unpack</goal>
+ *             </goals>
+ *             <configuration>
+ *               <artifactItems>
+ *                 <artifactItem>
+ *                   <groupId>org.eclipse.jetty</groupId>
+ *                   <artifactId>jetty-distribution</artifactId>
+ *                   <version>7.3.0</version>
+ *                   <type>zip</type>
+ *                   <overWrite>true</overWrite>
+ *                 </artifactItem>
+ *               </artifactItems>
+ *               <outputAbsoluteArtifactFilename>true</outputAbsoluteArtifactFilename>
+ *               <outputDirectory>${test-distro-dir}</outputDirectory>
+ *               <overWriteSnapshots>true</overWriteSnapshots>
+ *               <overWriteIfNewer>true</overWriteIfNewer>
+ *             </configuration>
+ *           </execution>
+ *         </executions>
+ *       </plugin>
+ *     </plugins>
+ *   </build>
+ * 
+ * </project>
+ * 
+ *

+ * If you have a specific configuration you want to setup, you'll want to prepare this configuration in an overlay directory underneath the + * src/test/resources/ directory.
+ * Notes: + *

    + *
  1. The {@link JettyDistro} sets up a unique test directory (based on the constructor {@link #JettyDistro(Class)} or {@link #JettyDistro(TestingDir)}), by + * ensuring the directory is empty, then copying the target/test-dist directory into this new testing directory prior to the test specific changes + * to the configuration.
    + * Note: this testing directory is a complete jetty distribution, suitable for executing via the command line for additional testing needs.
  2. + *
  3. The directory name you choose in src/test/resources will be the name you use in the {@link #overlayConfig(String)} method to provide + * replacement configurations for the Jetty Distribution.
  4. + *
  5. You'll want to {@link #delete(String)} any files and/or directories from the standard distribution prior to using the {@link #overlayConfig(String)} + * method.
  6. + *
  7. Use the {@link #copyLib(String, String)} method to copy JAR files from the target/test-libs directory (created and managed above using the + * maven-dependency-plugin) to copy the lib into the test specific.
  8. + *
  9. Use the {@link #copyTestWar(String)} method to copy WAR files from the target/test-wars directory (created and managed above using the + * maven-dependency-plugin) to copy the WAR into the test specific directory.
  10. + *
+ *

+ * Next you'll want to use Junit 4.8+ and the @BeforeClass and @AfterClass annotations to setup the JettyDistro + * class for setting up your testing configuration. + *

+ * Example Test Case using {@link JettyDistro} class + * + *

+ * public class MySampleTest
+ * {
+ *     private static JettyDistro jetty;
+ * 
+ *     @BeforeClass
+ *     public static void initJetty() throws Exception
+ *     {
+ *         jetty = new JettyDistro(MySampleTest.class);
+ * 
+ *         jetty.copyTestWar("test-war-java_util_logging.war");
+ *         jetty.copyTestWar("test-war-policy.war");
+ * 
+ *         jetty.delete("webapps/test.war");
+ *         jetty.delete("contexts/test.d");
+ *         jetty.delete("contexts/javadoc.xml");
+ *         jetty.delete("contexts/test.xml");
+ * 
+ *         jetty.overlayConfig("no_security");
+ * 
+ *         jetty.setDebug(true);
+ * 
+ *         jetty.start();
+ *     }
+ * 
+ *     @AfterClass
+ *     public static void shutdownJetty() throws Exception
+ *     {
+ *         if (jetty != null)
+ *         {
+ *             jetty.stop();
+ *         }
+ *     }
+ * 
+ *     @Test
+ *     public void testRequest() throws Exception
+ *     {
+ *         SimpleRequest request = new SimpleRequest(jetty.getBaseUri());
+ *         String path = "/test-war-policy/security/PRACTICAL/testFilsystem");
+ *         String response = request.getString(path);
+ *         Assert.assertEquals("Success", response);
+ *     }
+ * }
+ * 
+ */ +public class JettyDistro +{ + private String artifactName = "jetty-distribution"; + private long startTime = 60; + private TimeUnit timeUnit = TimeUnit.SECONDS; + + private File jettyHomeDir; + private Process pid; + private URI baseUri; + + private String jmxUrl; + + private boolean _debug = false; + + /** + * Setup the JettyHome as belonging in a testing directory associated with a testing clazz. + * + * @param clazz + * the testing class using this JettyDistro + * @throws IOException + * if unable to copy unpacked distribution into place for the provided testing directory + */ + public JettyDistro(Class clazz) throws IOException + { + this(clazz,null); + } + + /** + * Setup the JettyHome as belonging in a testing directory associated with a testing clazz. + * + * @param clazz + * the testing class using this JettyDistro + * @param artifact + * name of jetty distribution artifact + * @throws IOException + * if unable to copy unpacked distribution into place for the provided testing directory + */ + public JettyDistro(Class clazz, String artifact) throws IOException + { + this.jettyHomeDir = MavenTestingUtils.getTargetTestingDir(clazz,"jettyHome"); + if (artifact != null) + { + this.artifactName = artifact; + } + + copyBaseDistro(); + } + + /** + * Setup the JettyHome as belonging to a specific testing method directory + * + * @param testdir + * the testing directory to use as the JettyHome for this JettyDistro + * @throws IOException + * if unable to copy unpacked distribution into place for the provided testing directory + */ + public JettyDistro(TestingDir testdir) throws IOException + { + this.jettyHomeDir = testdir.getDir(); + copyBaseDistro(); + } + + /** + * Setup the JettyHome as belonging to a specific testing method directory + * + * @param testdir + * the testing directory to use as the JettyHome for this JettyDistro + * @param artifact + * name of jetty distribution artifact + * @throws IOException + * if unable to copy unpacked distribution into place for the provided testing directory + */ + public JettyDistro(TestingDir testdir, String artifact) throws IOException + { + this.jettyHomeDir = testdir.getDir(); + if (artifact != null) + { + this.artifactName = artifact; + } + + copyBaseDistro(); + } + + /** + * + * @throws IOException + * if unable to copy unpacked distribution into place for the provided testing directory + */ + private void copyBaseDistro() throws IOException + { + // The outputDirectory for the maven side dependency:unpack goal. + File distroUnpackDir = MavenTestingUtils.getTargetFile("test-dist"); + PathAssert.assertDirExists(artifactName + " dependency:unpack",distroUnpackDir); + + // The actual jetty-distribution-${version} directory is under this directory. + // Lets find it. + File subdirs[] = distroUnpackDir.listFiles(new FileFilter() + { + public boolean accept(File path) + { + if (!path.isDirectory()) + { + return false; + } + + return path.getName().startsWith(artifactName + "-"); + } + }); + + if (subdirs.length == 0) + { + // No jetty-distribution found. + StringBuilder err = new StringBuilder(); + err.append("No target/test-dist/"); + err.append(artifactName); + err.append("-${version} directory found."); + err.append("\n To fix this, run 'mvn process-test-resources' to create the directory."); + throw new IOException(err.toString()); + } + + if (subdirs.length != 1) + { + // Too many jetty-distributions found. + StringBuilder err = new StringBuilder(); + err.append("Too many target/test-dist/"); + err.append(artifactName); + err.append("-${version} directories found."); + for (File dir : subdirs) + { + err.append("\n ").append(dir.getAbsolutePath()); + } + err.append("\n To fix this, run 'mvn clean process-test-resources' to recreate the target/test-dist directory."); + throw new IOException(err.toString()); + } + + File distroSrcDir = subdirs[0]; + FS.ensureEmpty(jettyHomeDir); + System.out.printf("Copying Jetty Distribution: %s%n",distroSrcDir.getAbsolutePath()); + System.out.printf(" To Testing Dir: %s%n",jettyHomeDir.getAbsolutePath()); + IO.copyDir(distroSrcDir,jettyHomeDir); + } + + /** + * Return the $(jetty.home) directory being used for this JettyDistro + * + * @return the jetty.home directory being used + */ + public File getJettyHomeDir() + { + return this.jettyHomeDir; + } + + /** + * Copy a war file from ${project.basedir}/target/test-wars/${testWarFilename} into the ${jetty.home}/webapps/ directory + * + * @param testWarFilename + * the war file to copy (must exist) + * @throws IOException + * if unable to copy the war file. + */ + public void copyTestWar(String testWarFilename) throws IOException + { + File srcWar = MavenTestingUtils.getTargetFile("test-wars/" + testWarFilename); + File destWar = new File(jettyHomeDir,OS.separators("webapps/" + testWarFilename)); + FS.ensureDirExists(destWar.getParentFile()); + IO.copyFile(srcWar,destWar); + } + + /** + * Copy an arbitrary file from src/test/resources/${resourcePath} to the testing directory. + * + * @param resourcePath + * the relative path for file content within the src/test/resources directory. + * @param outputPath + * the testing directory relative output path for the file output (will result in a file with the outputPath name being created) + * @throws IOException + * if unable to copy resource file + */ + public void copyResource(String resourcePath, String outputPath) throws IOException + { + File srcFile = MavenTestingUtils.getTestResourceFile(resourcePath); + File destFile = new File(jettyHomeDir,OS.separators(outputPath)); + FS.ensureDirExists(destFile.getParentFile()); + IO.copyFile(srcFile,destFile); + } + + /** + * Copy an arbitrary file from target/test-libs/${libFilename} to the testing directory. + * + * @param libFilename + * the target/test-libs/${libFilename} to copy + * @param outputPath + * the destination testing directory relative output path for the lib. (will result in a file with the outputPath name being created) + * @throws IOException + * if unable to copy lib + */ + public void copyLib(String libFilename, String outputPath) throws IOException + { + File srcLib = MavenTestingUtils.getTargetFile("test-libs/" + libFilename); + File destLib = new File(jettyHomeDir,OS.separators(outputPath)); + FS.ensureDirExists(destLib.getParentFile()); + IO.copyFile(srcLib,destLib); + } + + /** + * Copy the ${project.basedir}/src/main/config/ tree into the testing directory. + * + * @throws IOException + * if unable to copy the directory tree + */ + public void copyProjectMainConfig() throws IOException + { + File srcDir = MavenTestingUtils.getProjectDir("src/main/config"); + IO.copyDir(srcDir,jettyHomeDir); + } + + /** + * Create a ${jetty.home}/lib/self/${jarFilename} jar file from the content in the ${project.basedir}/target/classes/ directory. + * + * @throws IOException + * if unable to copy the directory tree + */ + public void createProjectLib(String jarFilename) throws IOException + { + File srcDir = MavenTestingUtils.getTargetFile("classes"); + File libSelfDir = new File(jettyHomeDir,OS.separators("lib/self")); + FS.ensureDirExists(libSelfDir); + File jarFile = new File(libSelfDir,jarFilename); + JAR.create(srcDir,jarFile); + } + + /** + * Unpack an arbitrary config from target/test-configs/${configFilename} to the testing directory. + * + * @param configFilename + * the target/test-configs/${configFilename} to copy + * @throws IOException + * if unable to unpack config file + */ + public void unpackConfig(String configFilename) throws IOException + { + File srcConfig = MavenTestingUtils.getTargetFile("test-configs/" + configFilename); + JAR.unpack(srcConfig,jettyHomeDir); + } + + /** + * Delete a File or Directory found in the ${jetty.home} directory. + * + * @param path + * the path to delete. (can be a file or directory) + */ + public void delete(String path) + { + File jettyPath = new File(jettyHomeDir,OS.separators(path)); + FS.delete(jettyPath); + } + + /** + * Return the baseUri being used for this Jetty Process Instance. + * + * @return the base URI for this Jetty Process Instance. + */ + public URI getBaseUri() + { + return this.baseUri; + } + + /** + * Return the JMX URL being used for this Jetty Process Instance. + * + * @return the JMX URL for this Jetty Process Instance. + */ + public String getJmxUrl() + { + return this.jmxUrl; + } + + /** + * Take the directory contents from ${project.basedir}/src/test/resources/${testConfigName}/ and copy it over whatever happens to be at ${jetty.home} + * + * @param testConfigName + * the src/test/resources/ directory name to use as the source diretory for the configuration we are interested in. + * @throws IOException + * if unable to copy directory. + */ + public void overlayConfig(String testConfigName) throws IOException + { + File srcDir = MavenTestingUtils.getTestResourceDir(testConfigName); + IO.copyDir(srcDir,jettyHomeDir); + } + + /** + * Start the jetty server + * + * @throws IOException + * if unable to start the server. + */ + public void start() throws IOException + { + List commands = new ArrayList(); + commands.add(getJavaBin()); + + commands.add("-Djetty.home=" + jettyHomeDir.getAbsolutePath()); + + // Do a dry run first to get the exact command line for Jetty process + commands.add("-jar"); + commands.add("start.jar"); + commands.add("jetty.port=0"); + if (_debug) + { + commands.add("-D.DEBUG=true"); + } + commands.add("--dry-run"); + + ProcessBuilder pbCmd = new ProcessBuilder(commands); + pbCmd.directory(jettyHomeDir); + + String cmdLine = null; + Process pidCmd = pbCmd.start(); + try + { + cmdLine = readOutputLine(pidCmd); + } + finally + { + pidCmd.destroy(); + } + + if (cmdLine == null || !cmdLine.contains("XmlConfiguration")) + { + Assert.fail("Unable to get Jetty command line"); + } + + // Need to breakdown commandline into parts, as spaces in command line will cause failures. + List execCommands = splitAndUnescapeCommandLine(cmdLine); + + System.out.printf("Executing: %s%n",cmdLine); + System.out.printf("Working Dir: %s%n",jettyHomeDir.getAbsolutePath()); + + pbCmd = new ProcessBuilder(execCommands); + pid = pbCmd.start(); + + ConsoleParser parser = new ConsoleParser(); + List jmxList = parser.newPattern("JMX Remote URL: (.*)",0); + List connList = parser.newPattern("Started [A-Za-z]*Connector@([0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*):([0-9]*)",1); + // DISABLED: This is what exists in Jetty 9+ + // List connList = parser.newPattern("Started [A-Za-z]*Connector@.*[\\({]([0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*):([0-9]*)[\\)}].*",1); + + startPump("STDOUT",parser,this.pid.getInputStream()); + startPump("STDERR",parser,this.pid.getErrorStream()); + + try + { + parser.waitForDone(this.startTime,this.timeUnit); + + if (!jmxList.isEmpty()) + { + this.jmxUrl = jmxList.get(0)[0]; + System.out.printf("## Found JMX connector at %s%n",this.jmxUrl); + } + + if (!connList.isEmpty()) + { + String[] params = connList.get(0); + if (params.length == 2) + { + this.baseUri = URI.create("http://localhost:" + params[1] + "/"); + } + System.out.printf("## Found Jetty connector at host: %s port: %s%n",(Object[])params); + } + + } + catch (InterruptedException e) + { + pid.destroy(); + Assert.fail("Unable to get required information within time limit"); + } + } + + public static List splitAndUnescapeCommandLine(CharSequence rawCmdLine) + { + List cmds = new ArrayList(); + + int len = rawCmdLine.length(); + StringBuilder arg = new StringBuilder(); + boolean escaped = false; + boolean inQuote = false; + char c; + for (int i = 0; i < len; i++) + { + c = rawCmdLine.charAt(i); + if (escaped) + { + switch (c) + { + case 'r': + arg.append('\r'); + break; + case 'f': + arg.append('\f'); + break; + case 't': + arg.append('\t'); + break; + case 'n': + arg.append('\n'); + break; + case 'b': + arg.append('\b'); + break; + default: + arg.append(c); + break; + } + escaped = false; + continue; + } + + if (c == '\\') + { + escaped = true; + } + else + { + if ((c == ' ') && (!inQuote)) + { + // the delim! + cmds.add(String.valueOf(arg.toString())); + arg.setLength(0); + } + else if (c == '"') + { + inQuote = !inQuote; + } + else + { + arg.append(c); + } + } + } + cmds.add(String.valueOf(arg.toString())); + + return cmds; + } + + private String readOutputLine(Process pidCmd) throws IOException + { + InputStream in = null; + InputStreamReader reader = null; + BufferedReader buf = null; + try + { + in = pidCmd.getInputStream(); + reader = new InputStreamReader(in); + buf = new BufferedReader(reader); + return buf.readLine(); + } + finally + { + IO.close(buf); + IO.close(reader); + IO.close(in); + } + } + + private static class ConsoleParser + { + private List patterns = new ArrayList(); + private CountDownLatch latch; + private int count; + + public List newPattern(String exp, int cnt) + { + ConsolePattern pat = new ConsolePattern(exp,cnt); + patterns.add(pat); + count += cnt; + + return pat.getMatches(); + } + + public void parse(String line) + { + for (ConsolePattern pat : patterns) + { + Matcher mat = pat.getMatcher(line); + if (mat.find()) + { + int num = 0, count = mat.groupCount(); + String[] match = new String[count]; + while (num++ < count) + { + match[num - 1] = mat.group(num); + } + pat.getMatches().add(match); + + if (pat.getCount() > 0) + { + getLatch().countDown(); + } + } + } + } + + public void waitForDone(long timeout, TimeUnit unit) throws InterruptedException + { + getLatch().await(timeout,unit); + } + + private CountDownLatch getLatch() + { + synchronized (this) + { + if (latch == null) + { + latch = new CountDownLatch(count); + } + } + + return latch; + } + } + + private static class ConsolePattern + { + private Pattern pattern; + private List matches; + private int count; + + ConsolePattern(String exp, int cnt) + { + pattern = Pattern.compile(exp); + matches = new ArrayList(); + count = cnt; + } + + public Matcher getMatcher(String line) + { + return pattern.matcher(line); + } + + public List getMatches() + { + return matches; + } + + public int getCount() + { + return count; + } + } + + private void startPump(String mode, ConsoleParser parser, InputStream inputStream) + { + ConsoleStreamer pump = new ConsoleStreamer(mode,inputStream); + pump.setParser(parser); + Thread thread = new Thread(pump,"ConsoleStreamer/" + mode); + thread.start(); + } + + /** + * enable debug on the jetty process + * + * @param debug + */ + public void setDebug(boolean debug) + { + _debug = debug; + } + + private String getJavaBin() + { + String javaexes[] = new String[] + { "java", "java.exe" }; + + File javaHomeDir = new File(System.getProperty("java.home")); + for (String javaexe : javaexes) + { + File javabin = new File(javaHomeDir,OS.separators("bin/" + javaexe)); + if (javabin.exists() && javabin.isFile()) + { + return javabin.getAbsolutePath(); + } + } + + Assert.fail("Unable to find java bin"); + return "java"; + } + + /** + * Stop the jetty server + */ + public void stop() + { + System.out.println("Stopping JettyDistro ..."); + if (pid != null) + { + // TODO: maybe issue a STOP instead? + pid.destroy(); + } + } + + /** + * Simple streamer for the console output from a Process + */ + private static class ConsoleStreamer implements Runnable + { + private String mode; + private BufferedReader reader; + private ConsoleParser parser; + + public ConsoleStreamer(String mode, InputStream is) + { + this.mode = mode; + this.reader = new BufferedReader(new InputStreamReader(is)); + } + + public void setParser(ConsoleParser connector) + { + this.parser = connector; + } + + public void run() + { + String line; + // System.out.printf("ConsoleStreamer/%s initiated%n",mode); + try + { + while ((line = reader.readLine()) != (null)) + { + if (parser != null) + { + parser.parse(line); + } + System.out.println("[" + mode + "] " + line); + } + } + catch (IOException ignore) + { + /* ignore */ + } + finally + { + IO.close(reader); + } + // System.out.printf("ConsoleStreamer/%s finished%n",mode); + } + } + + public void setStartTime(long startTime, TimeUnit timeUnit) + { + this.startTime = startTime; + this.timeUnit = timeUnit; + } +} \ No newline at end of file