Issue #4919 - always stop SessionTracker before closing connections

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2020-06-02 15:37:55 +10:00
parent 753c8be6eb
commit bebe6fd138
9 changed files with 77 additions and 6 deletions

View File

@ -27,6 +27,7 @@ import java.util.Arrays;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
@ -468,12 +469,14 @@ public class Server extends HandlerWrapper implements Attributes
MultiException mex = new MultiException(); MultiException mex = new MultiException();
// Initiate graceful shutdown but only wait for it if stopTimeout is set.
CompletableFuture<Void> shutdown = Graceful.shutdown(this);
if (getStopTimeout() > 0) if (getStopTimeout() > 0)
{ {
long end = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(getStopTimeout()); long end = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(getStopTimeout());
try try
{ {
Graceful.shutdown(this).get(getStopTimeout(), TimeUnit.MILLISECONDS); shutdown.get(getStopTimeout(), TimeUnit.MILLISECONDS);
} }
catch (Throwable e) catch (Throwable e)
{ {

View File

@ -276,4 +276,11 @@ public class JavaxWebSocketClientContainer extends JavaxWebSocketContainer imple
return new AnnotatedClientEndpointConfig(anno); return new AnnotatedClientEndpointConfig(anno);
} }
@Override
protected void doStop() throws Exception
{
sessionTracker.stop();
super.doStop();
}
} }

View File

@ -40,8 +40,8 @@ import org.slf4j.LoggerFactory;
public abstract class JavaxWebSocketContainer extends ContainerLifeCycle implements javax.websocket.WebSocketContainer public abstract class JavaxWebSocketContainer extends ContainerLifeCycle implements javax.websocket.WebSocketContainer
{ {
private static final Logger LOG = LoggerFactory.getLogger(JavaxWebSocketContainer.class); private static final Logger LOG = LoggerFactory.getLogger(JavaxWebSocketContainer.class);
private final SessionTracker sessionTracker = new SessionTracker();
private final List<JavaxWebSocketSessionListener> sessionListeners = new ArrayList<>(); private final List<JavaxWebSocketSessionListener> sessionListeners = new ArrayList<>();
protected final SessionTracker sessionTracker = new SessionTracker();
protected final Configuration.ConfigurationCustomizer defaultCustomizer = new Configuration.ConfigurationCustomizer(); protected final Configuration.ConfigurationCustomizer defaultCustomizer = new Configuration.ConfigurationCustomizer();
protected final WebSocketComponents components; protected final WebSocketComponents components;

View File

@ -33,7 +33,7 @@ public class SessionTracker extends AbstractLifeCycle implements JavaxWebSocketS
{ {
private static final Logger LOG = LoggerFactory.getLogger(SessionTracker.class); private static final Logger LOG = LoggerFactory.getLogger(SessionTracker.class);
private CopyOnWriteArraySet<JavaxWebSocketSession> sessions = new CopyOnWriteArraySet<>(); private final CopyOnWriteArraySet<JavaxWebSocketSession> sessions = new CopyOnWriteArraySet<>();
public Set<Session> getSessions() public Set<Session> getSessions()
{ {

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.javax.server.internal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.function.Function; import java.util.function.Function;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
@ -33,6 +34,7 @@ import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.Graceful;
import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.WebSocketComponents;
import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient; import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient;
@ -45,7 +47,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ManagedObject("JSR356 Server Container") @ManagedObject("JSR356 Server Container")
public class JavaxWebSocketServerContainer extends JavaxWebSocketClientContainer implements javax.websocket.server.ServerContainer, LifeCycle.Listener public class JavaxWebSocketServerContainer extends JavaxWebSocketClientContainer implements javax.websocket.server.ServerContainer, LifeCycle.Listener, Graceful
{ {
public static final String JAVAX_WEBSOCKET_CONTAINER_ATTRIBUTE = javax.websocket.server.ServerContainer.class.getName(); public static final String JAVAX_WEBSOCKET_CONTAINER_ATTRIBUTE = javax.websocket.server.ServerContainer.class.getName();
private static final Logger LOG = LoggerFactory.getLogger(JavaxWebSocketServerContainer.class); private static final Logger LOG = LoggerFactory.getLogger(JavaxWebSocketServerContainer.class);
@ -260,4 +262,19 @@ public class JavaxWebSocketServerContainer extends JavaxWebSocketClientContainer
deferredEndpointConfigs.clear(); deferredEndpointConfigs.clear();
} }
} }
@Override
public CompletableFuture<Void> shutdown()
{
LifeCycle.stop(sessionTracker);
CompletableFuture<Void> shutdown = new CompletableFuture<>();
shutdown.complete(null);
return shutdown;
}
@Override
public boolean isShutdown()
{
return sessionTracker.isStopped();
}
} }

View File

@ -98,4 +98,23 @@ public class EventSocket
error = cause; error = cause;
errorLatch.countDown(); errorLatch.countDown();
} }
@ServerEndpoint("/")
@ClientEndpoint
public static class EchoSocket extends EventSocket
{
@Override
public void onMessage(String message) throws IOException
{
super.onMessage(message);
session.getBasicRemote().sendText(message);
}
@Override
public void onMessage(ByteBuffer message) throws IOException
{
super.onMessage(message);
session.getBasicRemote().sendBinary(message);
}
}
} }

View File

@ -386,6 +386,13 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli
return stopAtShutdown; return stopAtShutdown;
} }
@Override
protected void doStop() throws Exception
{
sessionTracker.stop();
super.doStop();
}
@Override @Override
public String toString() public String toString()
{ {

View File

@ -29,7 +29,7 @@ import org.eclipse.jetty.websocket.api.WebSocketSessionListener;
public class SessionTracker extends AbstractLifeCycle implements WebSocketSessionListener public class SessionTracker extends AbstractLifeCycle implements WebSocketSessionListener
{ {
private List<Session> sessions = new CopyOnWriteArrayList<>(); private final List<Session> sessions = new CopyOnWriteArrayList<>();
public Collection<Session> getSessions() public Collection<Session> getSessions()
{ {

View File

@ -22,6 +22,7 @@ import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
@ -29,6 +30,7 @@ import javax.servlet.ServletContext;
import org.eclipse.jetty.http.pathmap.PathSpec; import org.eclipse.jetty.http.pathmap.PathSpec;
import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Graceful;
import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketBehavior; import org.eclipse.jetty.websocket.api.WebSocketBehavior;
@ -47,7 +49,7 @@ import org.eclipse.jetty.websocket.util.server.internal.WebSocketMapping;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class JettyWebSocketServerContainer extends ContainerLifeCycle implements WebSocketContainer, WebSocketPolicy, LifeCycle.Listener public class JettyWebSocketServerContainer extends ContainerLifeCycle implements WebSocketContainer, WebSocketPolicy, LifeCycle.Listener, Graceful
{ {
public static final String JETTY_WEBSOCKET_CONTAINER_ATTRIBUTE = WebSocketContainer.class.getName(); public static final String JETTY_WEBSOCKET_CONTAINER_ATTRIBUTE = WebSocketContainer.class.getName();
@ -118,6 +120,7 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements
frameHandlerFactory = factory; frameHandlerFactory = factory;
addSessionListener(sessionTracker); addSessionListener(sessionTracker);
addBean(sessionTracker);
} }
public void addMapping(String pathSpec, JettyWebSocketCreator creator) public void addMapping(String pathSpec, JettyWebSocketCreator creator)
@ -260,4 +263,19 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements
{ {
customizer.setAutoFragment(autoFragment); customizer.setAutoFragment(autoFragment);
} }
@Override
public CompletableFuture<Void> shutdown()
{
LifeCycle.stop(sessionTracker);
CompletableFuture<Void> shutdown = new CompletableFuture<>();
shutdown.complete(null);
return shutdown;
}
@Override
public boolean isShutdown()
{
return sessionTracker.isStopped();
}
} }