diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java
index 5160a2cd503..e15eccaf561 100644
--- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java
+++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java
@@ -40,6 +40,7 @@ import org.eclipse.jetty.deploy.graph.Path;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.AttributesMap;
+import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
@@ -68,6 +69,7 @@ import org.eclipse.jetty.xml.XmlConfiguration;
public class DeploymentManager extends ContainerLifeCycle
{
private static final Logger LOG = Log.getLogger(DeploymentManager.class);
+ private MultiException onStartupErrors;
/**
* Represents a single tracked app within the deployment manager.
@@ -237,6 +239,12 @@ public class DeploymentManager extends ContainerLifeCycle
{
startAppProvider(provider);
}
+
+ if (onStartupErrors != null)
+ {
+ onStartupErrors.ifExceptionThrow();
+ }
+
super.doStart();
}
@@ -519,9 +527,23 @@ public class DeploymentManager extends ContainerLifeCycle
// The runBindings failed for 'failed' node
LOG.ignore(ignore);
}
+
+ if (isStarting())
+ {
+ addOnStartupError(t);
+ }
}
}
+ private synchronized void addOnStartupError(Throwable cause)
+ {
+ if(onStartupErrors == null)
+ {
+ onStartupErrors = new MultiException();
+ }
+ onStartupErrors.add(cause);
+ }
+
/**
* Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step
* in the process to reach the desired state.
diff --git a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/BadAppDeployTest.java b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/BadAppDeployTest.java
new file mode 100644
index 00000000000..132b5cbe5f1
--- /dev/null
+++ b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/BadAppDeployTest.java
@@ -0,0 +1,174 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 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.deploy;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import javax.servlet.ServletException;
+
+import org.eclipse.jetty.deploy.providers.WebAppProvider;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.ContextHandlerCollection;
+import org.eclipse.jetty.server.handler.DefaultHandler;
+import org.eclipse.jetty.server.handler.HandlerCollection;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
+import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.StacklessLogging;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import static java.time.Duration.ofSeconds;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@ExtendWith(WorkDirExtension.class)
+public class BadAppDeployTest
+{
+ public WorkDir workDir;
+ private Server server;
+
+ @AfterEach
+ public void stopServer() throws Exception
+ {
+ if (server != null)
+ {
+ server.stop();
+ }
+ }
+
+ @Test
+ public void testBadApp_ThrowOnUnavailableTrue_XmlOrder() throws Exception
+ {
+ /* Non-working Bean Order as reported in Issue #3620
+ It is important that this Order be maintained for an accurate test case.
+ ### BEAN: QueuedThreadPool[qtp1327763628]@4f2410ac{STOPPED,8<=0<=200,i=0,r=-1,q=0}[NO_TRY]
+ ### BEAN: ServerConnector@16f65612{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
+ ### BEAN: HandlerCollection@5f150435{STOPPED}
+ ### BEAN: DeploymentManager@1c53fd30{STOPPED}
+ */
+
+ server = new Server();
+ ServerConnector connector = new ServerConnector(server);
+ connector.setPort(0);
+ server.addConnector(connector);
+
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
+ HandlerCollection handlers = new HandlerCollection();
+ handlers.addHandler(contexts);
+ handlers.addHandler(new DefaultHandler());
+ server.setHandler(handlers); // this should be done before addBean(deploymentManager)
+
+ DeploymentManager deploymentManager = new DeploymentManager();
+ deploymentManager.setContexts(contexts);
+ WebAppProvider webAppProvider = new WebAppProvider();
+ deploymentManager.addAppProvider(webAppProvider);
+
+ Path webappsDir = workDir.getEmptyPathDir().resolve("webapps").toAbsolutePath();
+
+ FS.ensureDirExists(webappsDir);
+
+ copyTestResource("webapps/badapp/badapp.war", webappsDir.resolve("badapp.war"));
+ copyTestResource("webapps/badapp/badapp.xml", webappsDir.resolve("badapp.xml"));
+
+ webAppProvider.setMonitoredDirName(webappsDir.toString());
+ webAppProvider.setScanInterval(1);
+
+ server.addBean(deploymentManager); // this should be done after setHandler(handlers)
+
+ assertTimeoutPreemptively(ofSeconds(10), () -> {
+
+ try (StacklessLogging ignore = new StacklessLogging(Log.getLogger(WebAppContext.class),
+ Log.getLogger(DeploymentManager.class),
+ Log.getLogger("org.eclipse.jetty.server.handler.ContextHandler.badapp")))
+ {
+ ServletException cause = assertThrows(ServletException.class, () -> server.start());
+ assertThat(cause.getMessage(), containsString("intentionally"));
+ assertTrue(server.isFailed(), "Server should be in failed state");
+ }
+ });
+ }
+
+ @Test
+ public void testBadApp_ThrowOnUnavailableTrue_EmbeddedOrder() throws Exception
+ {
+ /* Working Bean Order
+ ### BEAN: QueuedThreadPool[qtp1530388690]@5b37e0d2{STOPPED,8<=0<=200,i=0,r=-1,q=0}[NO_TRY]
+ ### BEAN: ServerConnector@5e265ba4{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
+ ### BEAN: DeploymentManager@3419866c{STOPPED}
+ ### BEAN: HandlerCollection@63e31ee{STOPPED}
+ */
+
+ server = new Server();
+ ServerConnector connector = new ServerConnector(server);
+ connector.setPort(0);
+ server.addConnector(connector);
+
+ ContextHandlerCollection contexts = new ContextHandlerCollection();
+
+ DeploymentManager deploymentManager = new DeploymentManager();
+ deploymentManager.setContexts(contexts);
+ WebAppProvider webAppProvider = new WebAppProvider();
+ deploymentManager.addAppProvider(webAppProvider);
+
+ Path webappsDir = workDir.getEmptyPathDir().resolve("webapps").toAbsolutePath();
+
+ FS.ensureDirExists(webappsDir);
+
+ copyTestResource("webapps/badapp/badapp.war", webappsDir.resolve("badapp.war"));
+ copyTestResource("webapps/badapp/badapp.xml", webappsDir.resolve("badapp.xml"));
+
+ webAppProvider.setMonitoredDirName(webappsDir.toString());
+ webAppProvider.setScanInterval(1);
+
+ server.addBean(deploymentManager); // this should be done before setHandler(handlers)
+
+ HandlerCollection handlers = new HandlerCollection();
+ handlers.addHandler(contexts);
+ handlers.addHandler(new DefaultHandler());
+ server.setHandler(handlers); // this should be done after addBean(deploymentManager)
+
+ assertTimeoutPreemptively(ofSeconds(10), () -> {
+
+ try (StacklessLogging ignore = new StacklessLogging(Log.getLogger(WebAppContext.class),
+ Log.getLogger(DeploymentManager.class),
+ Log.getLogger("org.eclipse.jetty.server.handler.ContextHandler.badapp")))
+ {
+ ServletException cause = assertThrows(ServletException.class, () -> server.start());
+ assertThat(cause.getMessage(), containsString("intentionally"));
+ assertTrue(server.isFailed(), "Server should be in failed state");
+ }
+ });
+ }
+
+ private void copyTestResource(String testResourceFile, Path webappsFile) throws IOException
+ {
+ Path srcFile = MavenTestingUtils.getTestResourcePathFile(testResourceFile);
+ Files.copy(srcFile, webappsFile);
+ }
+}
diff --git a/jetty-deploy/src/test/resources/webapps/badapp/badapp.war b/jetty-deploy/src/test/resources/webapps/badapp/badapp.war
new file mode 100644
index 00000000000..3fc1a60d5fe
Binary files /dev/null and b/jetty-deploy/src/test/resources/webapps/badapp/badapp.war differ
diff --git a/jetty-deploy/src/test/resources/webapps/badapp/badapp.xml b/jetty-deploy/src/test/resources/webapps/badapp/badapp.xml
new file mode 100644
index 00000000000..d085d7b9cec
--- /dev/null
+++ b/jetty-deploy/src/test/resources/webapps/badapp/badapp.xml
@@ -0,0 +1,8 @@
+
+
+
+
+ /badapp
+ /badapp.war
+ true
+
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java
index 115634513f2..5f2ece7cc0d 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java
@@ -28,7 +28,6 @@ import java.util.Locale;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
-
import javax.servlet.AsyncContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
@@ -563,13 +562,18 @@ public abstract class AbstractProxyServlet extends HttpServlet
boolean aborted = proxyRequest.abort(failure);
if (!aborted)
{
- int status = failure instanceof TimeoutException ?
- HttpStatus.REQUEST_TIMEOUT_408 :
- HttpStatus.INTERNAL_SERVER_ERROR_500;
+ int status = clientRequestStatus(failure);
sendProxyResponseError(clientRequest, proxyResponse, status);
}
}
+ protected int clientRequestStatus(Throwable failure)
+ {
+ return failure instanceof TimeoutException ?
+ HttpStatus.REQUEST_TIMEOUT_408 :
+ HttpStatus.INTERNAL_SERVER_ERROR_500;
+ }
+
protected void onServerResponseHeaders(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Response serverResponse)
{
for (HttpField field : serverResponse.getHeaders())
@@ -634,9 +638,7 @@ public abstract class AbstractProxyServlet extends HttpServlet
if (_log.isDebugEnabled())
_log.debug(getRequestId(clientRequest) + " proxying failed", failure);
- int status = failure instanceof TimeoutException ?
- HttpStatus.GATEWAY_TIMEOUT_504 :
- HttpStatus.BAD_GATEWAY_502;
+ int status = proxyResponseStatus(failure);
int serverStatus = serverResponse == null ? status : serverResponse.getStatus();
if (expects100Continue(clientRequest) && serverStatus >= HttpStatus.OK_200)
status = serverStatus;
@@ -644,6 +646,13 @@ public abstract class AbstractProxyServlet extends HttpServlet
}
+ protected int proxyResponseStatus(Throwable failure)
+ {
+ return failure instanceof TimeoutException ?
+ HttpStatus.GATEWAY_TIMEOUT_504 :
+ HttpStatus.BAD_GATEWAY_502;
+ }
+
protected int getRequestId(HttpServletRequest clientRequest)
{
return System.identityHashCode(clientRequest);
diff --git a/jetty-server/src/main/config/modules/ssl.mod b/jetty-server/src/main/config/modules/ssl.mod
index 1fa918eadf5..be27d35546f 100644
--- a/jetty-server/src/main/config/modules/ssl.mod
+++ b/jetty-server/src/main/config/modules/ssl.mod
@@ -58,7 +58,7 @@ etc/jetty-ssl-context.xml
## The Endpoint Identification Algorithm
## Same as javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)
-#jetty.sslContext.endpointIdentificationAlgorithm=HTTPS
+#jetty.sslContext.endpointIdentificationAlgorithm=
## SSL JSSE Provider
# jetty.sslContext.provider=
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSessionListener.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSessionListener.java
new file mode 100644
index 00000000000..c0444899a68
--- /dev/null
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSessionListener.java
@@ -0,0 +1,26 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 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.websocket.jsr356;
+
+public interface JsrSessionListener
+{
+ void onSessionOpened(JsrSession session);
+
+ void onSessionClosed(JsrSession session);
+}
diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSessionTracker.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSessionTracker.java
new file mode 100644
index 00000000000..be97c94d05b
--- /dev/null
+++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrSessionTracker.java
@@ -0,0 +1,58 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 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.websocket.jsr356;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.LifeCycle;
+
+public class JsrSessionTracker extends AbstractLifeCycle implements JsrSessionListener
+{
+ private CopyOnWriteArraySet sessions = new CopyOnWriteArraySet<>();
+
+ public Set getSessions()
+ {
+ return Collections.unmodifiableSet(sessions);
+ }
+
+ @Override
+ public void onSessionOpened(JsrSession session)
+ {
+ sessions.add(session);
+ }
+
+ @Override
+ public void onSessionClosed(JsrSession session)
+ {
+ sessions.remove(session);
+ }
+
+ @Override
+ protected void doStop() throws Exception
+ {
+ for (JsrSession session : sessions)
+ {
+ LifeCycle.stop(session);
+ }
+ super.doStop();
+ }
+}
\ No newline at end of file
diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/SessionTracker.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/SessionTracker.java
new file mode 100644
index 00000000000..f8a5ae08d4a
--- /dev/null
+++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/SessionTracker.java
@@ -0,0 +1,58 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 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.websocket.common;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.eclipse.jetty.util.component.AbstractLifeCycle;
+import org.eclipse.jetty.util.component.LifeCycle;
+
+public class SessionTracker extends AbstractLifeCycle implements WebSocketSessionListener
+{
+ private CopyOnWriteArraySet sessions = new CopyOnWriteArraySet<>();
+
+ public Set getSessions()
+ {
+ return Collections.unmodifiableSet(sessions);
+ }
+
+ @Override
+ public void onSessionOpened(WebSocketSession session)
+ {
+ sessions.add(session);
+ }
+
+ @Override
+ public void onSessionClosed(WebSocketSession session)
+ {
+ sessions.remove(session);
+ }
+
+ @Override
+ protected void doStop() throws Exception
+ {
+ for (WebSocketSession session : sessions)
+ {
+ LifeCycle.stop(session);
+ }
+ super.doStop();
+ }
+}
\ No newline at end of file
diff --git a/scripts/release-jetty.sh b/scripts/release-jetty.sh
index 697497991b2..eeddd4ab3dd 100755
--- a/scripts/release-jetty.sh
+++ b/scripts/release-jetty.sh
@@ -158,7 +158,7 @@ if proceedyn "Are you sure you want to release using above? (y/N)" n; then
# This is equivalent to 'mvn release:perform'
if proceedyn "Build/Deploy from tag $TAG_NAME? (Y/n)" y; then
git checkout $TAG_NAME
- mvn clean package source:jar javadoc:jar gpg:sign deploy \
+ mvn clean package source:jar javadoc:jar gpg:sign javadoc:aggregate-jar deploy \
-Peclipse-release $DEPLOY_OPTS
reportMavenTestFailures
git checkout $GIT_BRANCH_ID
diff --git a/tests/test-distribution/pom.xml b/tests/test-distribution/pom.xml
index f6b31cb7895..a6cf1ff70dc 100644
--- a/tests/test-distribution/pom.xml
+++ b/tests/test-distribution/pom.xml
@@ -78,7 +78,6 @@
org.eclipse.jetty.toolchain
jetty-test-helper
- test
org.eclipse.jetty
diff --git a/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java b/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java
index 44a2a9edda9..043f61eb6b9 100644
--- a/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java
+++ b/tests/test-distribution/src/main/java/org/eclipse/jetty/tests/distribution/DistributionTester.java
@@ -62,6 +62,8 @@ import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transfer.AbstractTransferListener;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
+import org.eclipse.jetty.toolchain.test.FS;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -140,6 +142,9 @@ public class DistributionTester
commands.add("-Djava.io.tmpdir=" + workDir.toAbsolutePath().toString());
commands.add("-jar");
commands.add(config.jettyHome.toAbsolutePath() + "/start.jar");
+ // we get artifacts from local repo first
+ args = new ArrayList<>(args);
+ args.add("maven.local.repo=" + System.getProperty("mavenRepoPath"));
commands.addAll(args);
LOGGER.info("Executing: {}", commands);
@@ -166,6 +171,21 @@ public class DistributionTester
}
}
+ /**
+ * Installs content from {@code src/test/resources/} into {@code ${jetty.base}/}
+ *
+ * @param testResourcePath the location of the source file in {@code src/test/resources}
+ * @param baseResourcePath the location of the destination file in {@code ${jetty.base}}
+ * @throws IOException if unable to copy file
+ */
+ public void installBaseResource(String testResourcePath, String baseResourcePath) throws IOException
+ {
+ Path srcFile = MavenTestingUtils.getTestResourcePath(testResourcePath);
+ Path destFile = config.jettyBase.resolve(baseResourcePath);
+
+ Files.copy(srcFile, destFile);
+ }
+
/**
* Installs in {@code ${jetty.base}/webapps} the given war file under the given context path.
*
@@ -176,7 +196,7 @@ public class DistributionTester
public void installWarFile(File warFile, String context) throws IOException
{
//webapps
- Path webapps = Paths.get(config.jettyBase.toString(), "webapps", context);
+ Path webapps = config.jettyBase.resolve("webapps").resolve(context);
if (!Files.exists(webapps))
Files.createDirectories(webapps);
unzip(warFile, webapps.toFile());
@@ -213,7 +233,9 @@ public class DistributionTester
if (config.jettyBase == null)
{
- config.jettyBase = Files.createTempDirectory("jetty_base_");
+ Path bases = MavenTestingUtils.getTargetTestingPath("bases");
+ FS.ensureDirExists(bases);
+ config.jettyBase = Files.createTempDirectory(bases, "jetty_base_");
}
else
{
@@ -225,12 +247,12 @@ public class DistributionTester
private String getJavaExecutable()
{
String[] javaExecutables = new String[]{"java", "java.exe"};
- File javaHomeDir = new File(System.getProperty("java.home"));
+ Path javaBinDir = Paths.get(System.getProperty("java.home")).resolve("bin");
for (String javaExecutable : javaExecutables)
{
- File javaFile = new File(javaHomeDir, "bin" + File.separator + javaExecutable);
- if (javaFile.exists() && javaFile.isFile())
- return javaFile.getAbsolutePath();
+ Path javaFile = javaBinDir.resolve(javaExecutable);
+ if (Files.exists(javaFile) && Files.isRegularFile(javaFile))
+ return javaFile.toAbsolutePath().toString();
}
return "java";
}
diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java
new file mode 100644
index 00000000000..b2cfa38b9e8
--- /dev/null
+++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java
@@ -0,0 +1,156 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 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.tests.distribution;
+
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpStatus;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests where the server is started with a Bad App that will fail in its init phase.
+ */
+public class BadAppTests extends AbstractDistributionTest
+{
+ /**
+ * Start a server where a bad webapp is being deployed.
+ * The badapp.war will throw a ServletException during its deploy/init.
+ * The badapp.xml contains a {@code true}
+ *
+ * It is expected that the server does not start and exits with an error code
+ */
+ @Test
+ public void testXml_ThrowOnUnavailable_True() throws Exception
+ {
+ String jettyVersion = System.getProperty("jettyVersion");
+ DistributionTester distribution = DistributionTester.Builder.newInstance()
+ .jettyVersion(jettyVersion)
+ .mavenLocalRepository(System.getProperty("mavenRepoPath"))
+ .build();
+
+ try (DistributionTester.Run run1 = distribution.start("--add-to-start=http,deploy"))
+ {
+ assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
+ assertThat(run1.getExitValue(), is(0));
+
+ // Setup webapps directory
+ distribution.installBaseResource("badapp/badapp.war",
+ "webapps/badapp.war");
+ distribution.installBaseResource("badapp/badapp_throwonunavailable_true.xml",
+ "webapps/badapp.xml");
+
+ int port = distribution.freePort();
+ try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
+ {
+ assertTrue(run2.awaitFor(5, TimeUnit.SECONDS), "Should have exited");
+ assertThat("Should have gotten a non-zero exit code", run2.getExitValue(), not(is(0)));
+ }
+ }
+ }
+
+ /**
+ * Start a server where a bad webapp is being deployed.
+ * The badapp.war will throw a ServletException during its deploy/init.
+ * The badapp.xml contains a {@code false}
+ *
+ * It is expected that the server does start and attempts to access the /badapp/ report
+ * that it is unavailable.
+ */
+ @Test
+ public void testXml_ThrowOnUnavailable_False() throws Exception
+ {
+ String jettyVersion = System.getProperty("jettyVersion");
+ DistributionTester distribution = DistributionTester.Builder.newInstance()
+ .jettyVersion(jettyVersion)
+ .mavenLocalRepository(System.getProperty("mavenRepoPath"))
+ .build();
+
+ try (DistributionTester.Run run1 = distribution.start("--add-to-start=http,deploy"))
+ {
+ assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
+ assertThat(run1.getExitValue(), is(0));
+
+ // Setup webapps directory
+ distribution.installBaseResource("badapp/badapp.war",
+ "webapps/badapp.war");
+ distribution.installBaseResource("badapp/badapp_throwonunavailable_false.xml",
+ "webapps/badapp.xml");
+
+ int port = distribution.freePort();
+ try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
+ {
+ assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
+
+ startHttpClient();
+ ContentResponse response = client.GET("http://localhost:" + port + "/badapp/");
+ assertEquals(HttpStatus.SERVICE_UNAVAILABLE_503, response.getStatus());
+ assertThat(response.getContentAsString(), containsString("Unavailable"));
+ assertThat(response.getContentAsString(), containsString("Problem accessing /badapp/"));
+ }
+ }
+ }
+
+ /**
+ * Start a server where a bad webapp is being deployed.
+ * The badapp.war will throw a ServletException during its deploy/init.
+ * No badapp.xml is used, relying on default values for {@code throwUnavailableOnStartupException}
+ *
+ * It is expected that the server does start and attempts to access the /badapp/ report
+ * that it is unavailable.
+ */
+ @Test
+ public void testNoXml_ThrowOnUnavailable_Default() throws Exception
+ {
+ String jettyVersion = System.getProperty("jettyVersion");
+ DistributionTester distribution = DistributionTester.Builder.newInstance()
+ .jettyVersion(jettyVersion)
+ .mavenLocalRepository(System.getProperty("mavenRepoPath"))
+ .build();
+
+ try (DistributionTester.Run run1 = distribution.start("--add-to-start=http,deploy"))
+ {
+ assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
+ assertThat(run1.getExitValue(), is(0));
+
+ // Setup webapps directory
+ distribution.installBaseResource("badapp/badapp.war",
+ "webapps/badapp.war");
+
+ int port = distribution.freePort();
+ try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port))
+ {
+ assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
+
+ startHttpClient();
+ ContentResponse response = client.GET("http://localhost:" + port + "/badapp/");
+ assertEquals(HttpStatus.SERVICE_UNAVAILABLE_503, response.getStatus());
+ assertThat(response.getContentAsString(), containsString("Unavailable"));
+ assertThat(response.getContentAsString(), containsString("Problem accessing /badapp/"));
+ }
+ }
+ }
+}
diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java
index 29c2fdeb92b..cb4d9287b45 100644
--- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java
+++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java
@@ -242,7 +242,8 @@ public class DistributionTests extends AbstractDistributionTest
};
try (DistributionTester.Run run1 = distribution.start(args1))
{
- assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
+ // Give it time to download the dependencies
+ assertTrue(run1.awaitFor(30, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
diff --git a/tests/test-distribution/src/test/resources/badapp/badapp.war b/tests/test-distribution/src/test/resources/badapp/badapp.war
new file mode 100644
index 00000000000..3fc1a60d5fe
Binary files /dev/null and b/tests/test-distribution/src/test/resources/badapp/badapp.war differ
diff --git a/tests/test-distribution/src/test/resources/badapp/badapp_throwonunavailable_false.xml b/tests/test-distribution/src/test/resources/badapp/badapp_throwonunavailable_false.xml
new file mode 100644
index 00000000000..54bc161f65b
--- /dev/null
+++ b/tests/test-distribution/src/test/resources/badapp/badapp_throwonunavailable_false.xml
@@ -0,0 +1,8 @@
+
+
+
+
+ /badapp
+ /badapp.war
+ false
+
diff --git a/tests/test-distribution/src/test/resources/badapp/badapp_throwonunavailable_true.xml b/tests/test-distribution/src/test/resources/badapp/badapp_throwonunavailable_true.xml
new file mode 100644
index 00000000000..d085d7b9cec
--- /dev/null
+++ b/tests/test-distribution/src/test/resources/badapp/badapp_throwonunavailable_true.xml
@@ -0,0 +1,8 @@
+
+
+
+
+ /badapp
+ /badapp.war
+ true
+