Issue #3343 - Add an API/tools to be able to test Jetty Distribution.

Refactored streamers into class Run.
Added javadocs.
Moved "maven.resolver.version" to main POM for consistency.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2019-02-12 09:04:45 +01:00
parent a1a02d9942
commit 2d88b3b959
4 changed files with 148 additions and 91 deletions

View File

@ -35,6 +35,7 @@
<cbi-plugins.version>1.1.5</cbi-plugins.version>
<junit.version>5.3.1</junit.version>
<maven.version>3.6.0</maven.version>
<maven.resolver.version>1.3.1</maven.resolver.version>
<javax.servlet.api.version>3.1.0</javax.servlet.api.version>
<weld.version>2.4.5.Final</weld.version>
<jetty.perf-helper.version>1.0.5</jetty.perf-helper.version>

View File

@ -12,7 +12,6 @@
<properties>
<bundle-symbolic-name>${project.groupId}.tests.distribution</bundle-symbolic-name>
<mavenResolver.version>1.3.1</mavenResolver.version>
</properties>
<dependencies>
@ -43,17 +42,17 @@
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-connector-basic</artifactId>
<version>${mavenResolver.version}</version>
<version>${maven.resolver.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-transport-file</artifactId>
<version>${mavenResolver.version}</version>
<version>${maven.resolver.version}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.resolver</groupId>
<artifactId>maven-resolver-transport-http</artifactId>
<version>${mavenResolver.version}</version>
<version>${maven.resolver.version}</version>
</dependency>
<dependency>

View File

@ -67,14 +67,46 @@ import org.eclipse.jetty.util.log.Logger;
/**
* <p>Helper class to test the Jetty Distribution</p>.
* <p>API can change without any further notice.</p>
* <p>Usage:</p>
* <pre>
* // Create the distribution.
* String jettyVersion = "9.4.14.v20181114";
* DistributionTester distribution = DistributionTester.Builder.newInstance()
* .jettyVersion(jettyVersion)
* .jettyBase(Paths.get("demo-base"))
* .build();
*
* // The first run initializes the Jetty Base.
* try (DistributionTester.Run run1 = distribution.start("--create-startd", "--add-to-start=http2c,jsp,deploy"))
* {
* assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
* assertEquals(0, run1.getExitValue());
*
* // Install a web application.
* File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
* distribution.installWarFile(war, "test");
*
* // The second run starts the distribution.
* int port = 9090;
* try (DistributionTester.Run run = distribution.start("jetty.http.port=" + port))
* {
* // Wait for Jetty to be fully started.
* assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS));
*
* // Make a HTTP request to the web application.
* HttpClient client = new HttpClient();
* client.start();
* ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp");
* assertEquals(HttpStatus.OK_200, response.getStatus());
* }
* }
* </pre>
*/
public class DistributionTester
{
private static final Logger LOGGER = Log.getLogger(DistributionTester.class);
private Config config;
private List<ConsoleStreamer> consoleStreamers = new ArrayList<>();
private List<String> logs = new ArrayList<>();
private DistributionTester(Config config)
{
@ -82,7 +114,7 @@ public class DistributionTester
}
/**
* Starts the instance with the given arguments
* Starts the distribution with the given arguments
*
* @param args arguments to use to start the distribution
*/
@ -92,16 +124,12 @@ public class DistributionTester
}
/**
* Start the instance with the arguments
* Start the distribution with the arguments
*
* @param args arguments to use to start the distribution
*/
public DistributionTester.Run start(List<String> args) throws Exception
{
// do we want to be sure and use "--testing-mode" to not break surefire with a System.exit ???
logs.clear();
List<String> commands = new ArrayList<>();
commands.add(getJavaExecutable());
commands.add("-jar");
@ -116,13 +144,14 @@ public class DistributionTester
pbCmd.directory(workingDir);
Process process = pbCmd.start();
consoleStreamers.add(startPump("STDOUT", process.getInputStream()));
consoleStreamers.add(startPump("STDERR", process.getErrorStream()));
return new Run(process);
}
public int randomPort() throws IOException
/**
* @return a free port chosen by the OS that can be used to listen to
* @throws IOException if a free port is not available
*/
public int freePort() throws IOException
{
try (ServerSocket server = new ServerSocket())
{
@ -132,6 +161,13 @@ public class DistributionTester
}
}
/**
* Installs in {@code ${jetty.base}/webapps} the given war file under the given context path.
*
* @param warFile the war file to install
* @param context the context path
* @throws IOException if the installation fails
*/
public void installWarFile(File warFile, String context) throws IOException
{
//webapps
@ -142,8 +178,11 @@ public class DistributionTester
}
/**
* Resolves an artifact given its Maven coordinates.
*
* @param coordinates <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>
* @return the artifact
* @see #installWarFile(File, String)
*/
public File resolveArtifact(String coordinates) throws ArtifactResolutionException
{
@ -191,14 +230,6 @@ public class DistributionTester
return "java";
}
private ConsoleStreamer startPump(String mode, InputStream stream)
{
ConsoleStreamer pump = new ConsoleStreamer(stream);
Thread thread = new Thread(pump, "ConsoleStreamer/" + mode);
thread.start();
return pump;
}
private void unzip(File zipFile, File output) throws IOException
{
try (InputStream fileInputStream = Files.newInputStream(zipFile.toPath());
@ -293,11 +324,6 @@ public class DistributionTester
return session;
}
public Path getJettyHome()
{
return config.jettyHome;
}
private static class Config
{
private Path jettyBase;
@ -309,51 +335,14 @@ public class DistributionTester
@Override
public String toString()
{
return "Config{" + "jettyBase=" + jettyBase + ", jettyHome=" + jettyHome + ", jettyVersion='" + jettyVersion
+ '\'' + ", mavenLocalRepository='" + mavenLocalRepository + '\'' + ", mavenRemoteRepositories="
+ mavenRemoteRepositories + '}';
}
}
/**
* Simple streamer for the console output from a Process
*/
private class ConsoleStreamer implements Runnable
{
private final BufferedReader reader;
private volatile boolean stop;
public ConsoleStreamer(InputStream stream)
{
this.reader = new BufferedReader(new InputStreamReader(stream));
}
@Override
public void run()
{
try
{
String line;
while ((line = reader.readLine()) != null && !stop)
{
LOGGER.info("{}", line);
DistributionTester.this.logs.add(line);
}
}
catch (IOException ignore)
{
// ignore
}
finally
{
IO.close(reader);
}
}
public void stop()
{
stop = true;
IO.close(reader);
return String.format("%s@%x{jettyBase=%s, jettyHome=%s, jettyVersion=%s, mavenLocalRepository=%s, mavenRemoteRepositories=%s}",
getClass().getSimpleName(),
hashCode(),
jettyBase,
jettyHome,
jettyVersion,
mavenLocalRepository,
mavenRemoteRepositories);
}
}
@ -377,17 +366,32 @@ public class DistributionTester
}
}
public class Run implements Closeable
/**
* A distribution run wraps the process that started the Jetty distribution.
*/
public static class Run implements Closeable
{
private final Process process;
private final List<ConsoleStreamer> consoleStreamers = new ArrayList<>();
private final List<String> logs = new ArrayList<>();
public Run(Process process)
private Run(Process process)
{
this.process = process;
consoleStreamers.add(startPump("STDOUT", process.getInputStream()));
consoleStreamers.add(startPump("STDERR", process.getErrorStream()));
}
private ConsoleStreamer startPump(String mode, InputStream stream)
{
ConsoleStreamer pump = new ConsoleStreamer(stream);
Thread thread = new Thread(pump, "ConsoleStreamer/" + mode);
thread.start();
return pump;
}
/**
* Waits for the given time for the distribution to stop.
* Waits for the given time for the distribution process to stop.
*
* @param time the time to wait
* @param unit the unit of time
@ -402,13 +406,17 @@ public class DistributionTester
return result;
}
public int getExitValue()
/**
* @return the distribution process exit value
* @throws IllegalThreadStateException if the distribution process is not terminated yet
*/
public int getExitValue() throws IllegalThreadStateException
{
return process.exitValue();
}
/**
* Stops the distribution.
* Stops the distribution process.
*
* @see #awaitFor(long, TimeUnit)
*/
@ -418,6 +426,9 @@ public class DistributionTester
stopConsoleStreamers();
}
/**
* Forcibly destroys the distribution process.
*/
public void destroy()
{
process.destroyForcibly();
@ -430,9 +441,7 @@ public class DistributionTester
}
/**
* Method to use in finally block of a test and when using @After in a unit test.
* if running, it stops the distribution.
* Cleanup JettyBase and JettyHome directories
* @see #destroy()
*/
@Override
public void close()
@ -440,6 +449,15 @@ public class DistributionTester
destroy();
}
/**
* Awaits the console logs to contain the given text, for the given amount of time.
*
* @param txt the text that must be present in the console logs
* @param time the time to wait
* @param unit the unit of time
* @return true if the text was found, false if the timeout elapsed
* @throws InterruptedException if the wait is interrupted
*/
public boolean awaitConsoleLogsFor(String txt, long time, TimeUnit unit) throws InterruptedException
{
long end = System.nanoTime() + unit.toNanos(time);
@ -452,6 +470,48 @@ public class DistributionTester
}
return false;
}
/**
* Simple streamer for the console output from a Process
*/
private class ConsoleStreamer implements Runnable
{
private final BufferedReader reader;
private volatile boolean stop;
public ConsoleStreamer(InputStream stream)
{
this.reader = new BufferedReader(new InputStreamReader(stream));
}
@Override
public void run()
{
try
{
String line;
while ((line = reader.readLine()) != null && !stop)
{
LOGGER.info("{}", line);
logs.add(line);
}
}
catch (IOException ignore)
{
// ignore
}
finally
{
IO.close(reader);
}
}
public void stop()
{
stop = true;
IO.close(reader);
}
}
}
public static class Builder

View File

@ -53,7 +53,7 @@ public class DistributionTests extends AbstractDistributionTest
assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());
int port = distribution.randomPort();
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
@ -90,7 +90,7 @@ public class DistributionTests extends AbstractDistributionTest
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
distribution.installWarFile(war, "test");
int port = distribution.randomPort();
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
@ -127,7 +127,7 @@ public class DistributionTests extends AbstractDistributionTest
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
distribution.installWarFile(war, "test");
int port = distribution.randomPort();
int port = distribution.freePort();
String[] args2 = {
"--jpms",
"jetty.http.port=" + port
@ -167,7 +167,7 @@ public class DistributionTests extends AbstractDistributionTest
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
distribution.installWarFile(war, "test");
int port = distribution.randomPort();
int port = distribution.freePort();
try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
@ -192,11 +192,8 @@ public class DistributionTests extends AbstractDistributionTest
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
.build();
int port = distribution.randomPort();
String[] args1 = {
"jetty.http.port=" + port
};
try (DistributionTester.Run run1 = distribution.start(args1))
int port = distribution.freePort();
try (DistributionTester.Run run1 = distribution.start("jetty.http.port=" + port))
{
assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS));