Merge branch `jetty-9.4.x` into `jetty-10.0.x`
Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com> # Conflicts: # jetty-server/src/main/config/etc/jetty-ssl-context.xml # jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java # jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/ServerContainer.java # jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java # jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java
This commit is contained in:
commit
a305660c54
|
@ -40,6 +40,7 @@ import org.eclipse.jetty.deploy.graph.Path;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||||
import org.eclipse.jetty.util.AttributesMap;
|
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.ManagedAttribute;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||||
|
@ -68,6 +69,7 @@ import org.eclipse.jetty.xml.XmlConfiguration;
|
||||||
public class DeploymentManager extends ContainerLifeCycle
|
public class DeploymentManager extends ContainerLifeCycle
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(DeploymentManager.class);
|
private static final Logger LOG = Log.getLogger(DeploymentManager.class);
|
||||||
|
private MultiException onStartupErrors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a single tracked app within the deployment manager.
|
* Represents a single tracked app within the deployment manager.
|
||||||
|
@ -237,6 +239,12 @@ public class DeploymentManager extends ContainerLifeCycle
|
||||||
{
|
{
|
||||||
startAppProvider(provider);
|
startAppProvider(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (onStartupErrors != null)
|
||||||
|
{
|
||||||
|
onStartupErrors.ifExceptionThrow();
|
||||||
|
}
|
||||||
|
|
||||||
super.doStart();
|
super.doStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,8 +527,22 @@ public class DeploymentManager extends ContainerLifeCycle
|
||||||
// The runBindings failed for 'failed' node
|
// The runBindings failed for 'failed' node
|
||||||
LOG.ignore(ignore);
|
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
|
* Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||||
|
|
||||||
|
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||||
|
<Set name="contextPath">/badapp</Set>
|
||||||
|
<Set name="war"><Property name="jetty.webapps"/>/badapp.war</Set>
|
||||||
|
<Set name="throwUnavailableOnStartupException">true</Set>
|
||||||
|
</Configure>
|
|
@ -28,7 +28,6 @@ import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import javax.servlet.AsyncContext;
|
import javax.servlet.AsyncContext;
|
||||||
import javax.servlet.ServletConfig;
|
import javax.servlet.ServletConfig;
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
|
@ -563,13 +562,18 @@ public abstract class AbstractProxyServlet extends HttpServlet
|
||||||
boolean aborted = proxyRequest.abort(failure);
|
boolean aborted = proxyRequest.abort(failure);
|
||||||
if (!aborted)
|
if (!aborted)
|
||||||
{
|
{
|
||||||
int status = failure instanceof TimeoutException ?
|
int status = clientRequestStatus(failure);
|
||||||
HttpStatus.REQUEST_TIMEOUT_408 :
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR_500;
|
|
||||||
sendProxyResponseError(clientRequest, proxyResponse, status);
|
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)
|
protected void onServerResponseHeaders(HttpServletRequest clientRequest, HttpServletResponse proxyResponse, Response serverResponse)
|
||||||
{
|
{
|
||||||
for (HttpField field : serverResponse.getHeaders())
|
for (HttpField field : serverResponse.getHeaders())
|
||||||
|
@ -634,9 +638,7 @@ public abstract class AbstractProxyServlet extends HttpServlet
|
||||||
if (_log.isDebugEnabled())
|
if (_log.isDebugEnabled())
|
||||||
_log.debug(getRequestId(clientRequest) + " proxying failed", failure);
|
_log.debug(getRequestId(clientRequest) + " proxying failed", failure);
|
||||||
|
|
||||||
int status = failure instanceof TimeoutException ?
|
int status = proxyResponseStatus(failure);
|
||||||
HttpStatus.GATEWAY_TIMEOUT_504 :
|
|
||||||
HttpStatus.BAD_GATEWAY_502;
|
|
||||||
int serverStatus = serverResponse == null ? status : serverResponse.getStatus();
|
int serverStatus = serverResponse == null ? status : serverResponse.getStatus();
|
||||||
if (expects100Continue(clientRequest) && serverStatus >= HttpStatus.OK_200)
|
if (expects100Continue(clientRequest) && serverStatus >= HttpStatus.OK_200)
|
||||||
status = serverStatus;
|
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)
|
protected int getRequestId(HttpServletRequest clientRequest)
|
||||||
{
|
{
|
||||||
return System.identityHashCode(clientRequest);
|
return System.identityHashCode(clientRequest);
|
||||||
|
|
|
@ -58,7 +58,7 @@ etc/jetty-ssl-context.xml
|
||||||
|
|
||||||
## The Endpoint Identification Algorithm
|
## The Endpoint Identification Algorithm
|
||||||
## Same as javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)
|
## Same as javax.net.ssl.SSLParameters#setEndpointIdentificationAlgorithm(String)
|
||||||
#jetty.sslContext.endpointIdentificationAlgorithm=HTTPS
|
#jetty.sslContext.endpointIdentificationAlgorithm=
|
||||||
|
|
||||||
## SSL JSSE Provider
|
## SSL JSSE Provider
|
||||||
# jetty.sslContext.provider=
|
# jetty.sslContext.provider=
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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<JsrSession> sessions = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
|
public Set<javax.websocket.Session> 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<WebSocketSession> sessions = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
|
public Set<WebSocketSession> 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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'
|
# This is equivalent to 'mvn release:perform'
|
||||||
if proceedyn "Build/Deploy from tag $TAG_NAME? (Y/n)" y; then
|
if proceedyn "Build/Deploy from tag $TAG_NAME? (Y/n)" y; then
|
||||||
git checkout $TAG_NAME
|
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
|
-Peclipse-release $DEPLOY_OPTS
|
||||||
reportMavenTestFailures
|
reportMavenTestFailures
|
||||||
git checkout $GIT_BRANCH_ID
|
git checkout $GIT_BRANCH_ID
|
||||||
|
|
|
@ -78,7 +78,6 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||||
<artifactId>jetty-test-helper</artifactId>
|
<artifactId>jetty-test-helper</artifactId>
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
|
|
@ -62,6 +62,8 @@ import org.eclipse.aether.spi.connector.transport.TransporterFactory;
|
||||||
import org.eclipse.aether.transfer.AbstractTransferListener;
|
import org.eclipse.aether.transfer.AbstractTransferListener;
|
||||||
import org.eclipse.aether.transport.file.FileTransporterFactory;
|
import org.eclipse.aether.transport.file.FileTransporterFactory;
|
||||||
import org.eclipse.aether.transport.http.HttpTransporterFactory;
|
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.IO;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
@ -140,6 +142,9 @@ public class DistributionTester
|
||||||
commands.add("-Djava.io.tmpdir=" + workDir.toAbsolutePath().toString());
|
commands.add("-Djava.io.tmpdir=" + workDir.toAbsolutePath().toString());
|
||||||
commands.add("-jar");
|
commands.add("-jar");
|
||||||
commands.add(config.jettyHome.toAbsolutePath() + "/start.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);
|
commands.addAll(args);
|
||||||
|
|
||||||
LOGGER.info("Executing: {}", commands);
|
LOGGER.info("Executing: {}", commands);
|
||||||
|
@ -166,6 +171,21 @@ public class DistributionTester
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs content from {@code src/test/resources/<testResourcePath>} into {@code ${jetty.base}/<baseResourcePath>}
|
||||||
|
*
|
||||||
|
* @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.
|
* 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
|
public void installWarFile(File warFile, String context) throws IOException
|
||||||
{
|
{
|
||||||
//webapps
|
//webapps
|
||||||
Path webapps = Paths.get(config.jettyBase.toString(), "webapps", context);
|
Path webapps = config.jettyBase.resolve("webapps").resolve(context);
|
||||||
if (!Files.exists(webapps))
|
if (!Files.exists(webapps))
|
||||||
Files.createDirectories(webapps);
|
Files.createDirectories(webapps);
|
||||||
unzip(warFile, webapps.toFile());
|
unzip(warFile, webapps.toFile());
|
||||||
|
@ -213,7 +233,9 @@ public class DistributionTester
|
||||||
|
|
||||||
if (config.jettyBase == null)
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -225,12 +247,12 @@ public class DistributionTester
|
||||||
private String getJavaExecutable()
|
private String getJavaExecutable()
|
||||||
{
|
{
|
||||||
String[] javaExecutables = new String[]{"java", "java.exe"};
|
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)
|
for (String javaExecutable : javaExecutables)
|
||||||
{
|
{
|
||||||
File javaFile = new File(javaHomeDir, "bin" + File.separator + javaExecutable);
|
Path javaFile = javaBinDir.resolve(javaExecutable);
|
||||||
if (javaFile.exists() && javaFile.isFile())
|
if (Files.exists(javaFile) && Files.isRegularFile(javaFile))
|
||||||
return javaFile.getAbsolutePath();
|
return javaFile.toAbsolutePath().toString();
|
||||||
}
|
}
|
||||||
return "java";
|
return "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 <Set name="throwUnavailableOnStartupException">true</Set>}
|
||||||
|
*
|
||||||
|
* 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 <Set name="throwUnavailableOnStartupException">false</Set>}
|
||||||
|
*
|
||||||
|
* 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/"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -242,7 +242,8 @@ public class DistributionTests extends AbstractDistributionTest
|
||||||
};
|
};
|
||||||
try (DistributionTester.Run run1 = distribution.start(args1))
|
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());
|
assertEquals(0, run1.getExitValue());
|
||||||
|
|
||||||
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
|
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||||
|
|
||||||
|
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||||
|
<Set name="contextPath">/badapp</Set>
|
||||||
|
<Set name="war"><Property name="jetty.webapps"/>/badapp.war</Set>
|
||||||
|
<Set name="throwUnavailableOnStartupException">false</Set>
|
||||||
|
</Configure>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||||
|
|
||||||
|
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||||
|
<Set name="contextPath">/badapp</Set>
|
||||||
|
<Set name="war"><Property name="jetty.webapps"/>/badapp.war</Set>
|
||||||
|
<Set name="throwUnavailableOnStartupException">true</Set>
|
||||||
|
</Configure>
|
Loading…
Reference in New Issue