Skip to content

Commit

Permalink
Merged branch 'jetty-10.0.x' into 'jetty-11.0.x'.
Browse files Browse the repository at this point in the history
Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
  • Loading branch information
sbordet committed May 20, 2021
2 parents 28562d6 + cd73338 commit 2d08ed6
Show file tree
Hide file tree
Showing 8 changed files with 377 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ protected void doStop() throws Exception
_houseKeeper.stop();
if (_ownHouseKeeper)
{
removeBean(_houseKeeper);
_houseKeeper = null;
}
_random = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,34 @@ public void prependFilterMapping(FilterMapping mapping)
}
}

public void removeFilterHolder(FilterHolder holder)
{
if (holder == null)
return;

try (AutoLock ignored = lock())
{
FilterHolder[] holders = Arrays.stream(getFilters())
.filter(h -> h != holder)
.toArray(FilterHolder[]::new);
setFilters(holders);
}
}

public void removeFilterMapping(FilterMapping mapping)
{
if (mapping == null)
return;

try (AutoLock ignored = lock())
{
FilterMapping[] mappings = Arrays.stream(getFilterMappings())
.filter(m -> m != mapping)
.toArray(FilterMapping[]::new);
setFilterMappings(mappings);
}
}

protected void updateNameMappings()
{
try (AutoLock ignored = lock())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@

package org.eclipse.jetty.websocket.core.server;

import java.util.Objects;

import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.DecoratedObjectFactory;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.compression.DeflaterPool;
Expand Down Expand Up @@ -92,17 +93,21 @@ public static WebSocketComponents ensureWebSocketComponents(Server server, Servl
if (server.contains(bufferPool))
serverComponents.unmanage(bufferPool);

servletContext.setAttribute(WEBSOCKET_COMPONENTS_ATTRIBUTE, serverComponents);
LifeCycle.start(serverComponents);
servletContext.addListener(new ServletContextListener()
// Stop the WebSocketComponents when the ContextHandler stops.
ContextHandler contextHandler = Objects.requireNonNull(ContextHandler.getContextHandler(servletContext));
contextHandler.addManaged(serverComponents);
contextHandler.addEventListener(new LifeCycle.Listener()
{
@Override
public void contextDestroyed(ServletContextEvent sce)
public void lifeCycleStopping(LifeCycle event)
{
LifeCycle.stop(serverComponents);
servletContext.removeAttribute(WEBSOCKET_COMPONENTS_ATTRIBUTE);
contextHandler.removeBean(serverComponents);
contextHandler.removeEventListener(this);
}
});

servletContext.setAttribute(WEBSOCKET_COMPONENTS_ATTRIBUTE, serverComponents);
return serverComponents;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,46 +60,60 @@ public static JakartaWebSocketServerContainer ensureContainer(ServletContext ser
if (contextHandler.getServer() == null)
throw new IllegalStateException("Server has not been set on the ServletContextHandler");

JakartaWebSocketServerContainer container = getContainer(servletContext);
if (container == null)
JakartaWebSocketServerContainer containerFromServletContext = getContainer(servletContext);
if (containerFromServletContext != null)
return containerFromServletContext;

Function<WebSocketComponents, WebSocketCoreClient> coreClientSupplier = (wsComponents) ->
{
Function<WebSocketComponents, WebSocketCoreClient> coreClientSupplier = (wsComponents) ->
WebSocketCoreClient coreClient = (WebSocketCoreClient)servletContext.getAttribute(WebSocketCoreClient.WEBSOCKET_CORECLIENT_ATTRIBUTE);
if (coreClient == null)
{
WebSocketCoreClient coreClient = (WebSocketCoreClient)servletContext.getAttribute(WebSocketCoreClient.WEBSOCKET_CORECLIENT_ATTRIBUTE);
if (coreClient == null)
{
// Find Pre-Existing (Shared?) HttpClient and/or executor
HttpClient httpClient = (HttpClient)servletContext.getAttribute(JakartaWebSocketServletContainerInitializer.HTTPCLIENT_ATTRIBUTE);
if (httpClient == null)
httpClient = (HttpClient)contextHandler.getServer().getAttribute(JakartaWebSocketServletContainerInitializer.HTTPCLIENT_ATTRIBUTE);

Executor executor = httpClient == null ? null : httpClient.getExecutor();
if (executor == null)
executor = (Executor)servletContext.getAttribute("org.eclipse.jetty.server.Executor");
if (executor == null)
executor = contextHandler.getServer().getThreadPool();

if (httpClient != null && httpClient.getExecutor() == null)
httpClient.setExecutor(executor);

// create the core client
coreClient = new WebSocketCoreClient(httpClient, wsComponents);
coreClient.getHttpClient().setName("Jakarta-WebSocketClient@" + Integer.toHexString(coreClient.getHttpClient().hashCode()));
if (executor != null && httpClient == null)
coreClient.getHttpClient().setExecutor(executor);
servletContext.setAttribute(WebSocketCoreClient.WEBSOCKET_CORECLIENT_ATTRIBUTE, coreClient);
}
return coreClient;
};

// Create the Jetty ServerContainer implementation
container = new JakartaWebSocketServerContainer(
WebSocketMappings.ensureMappings(servletContext),
WebSocketServerComponents.getWebSocketComponents(servletContext),
coreClientSupplier);
contextHandler.addManaged(container);
contextHandler.addEventListener(container);
}
// Find Pre-Existing (Shared?) HttpClient and/or executor
HttpClient httpClient = (HttpClient)servletContext.getAttribute(JakartaWebSocketServletContainerInitializer.HTTPCLIENT_ATTRIBUTE);
if (httpClient == null)
httpClient = (HttpClient)contextHandler.getServer().getAttribute(JakartaWebSocketServletContainerInitializer.HTTPCLIENT_ATTRIBUTE);

Executor executor = httpClient == null ? null : httpClient.getExecutor();
if (executor == null)
executor = (Executor)servletContext.getAttribute("org.eclipse.jetty.server.Executor");
if (executor == null)
executor = contextHandler.getServer().getThreadPool();

if (httpClient != null && httpClient.getExecutor() == null)
httpClient.setExecutor(executor);

// create the core client
coreClient = new WebSocketCoreClient(httpClient, wsComponents);
coreClient.getHttpClient().setName("Jakarta-WebSocketClient@" + Integer.toHexString(coreClient.getHttpClient().hashCode()));
if (executor != null && httpClient == null)
coreClient.getHttpClient().setExecutor(executor);
servletContext.setAttribute(WebSocketCoreClient.WEBSOCKET_CORECLIENT_ATTRIBUTE, coreClient);
}
return coreClient;
};

// Create the Jetty ServerContainer implementation
JakartaWebSocketServerContainer container = new JakartaWebSocketServerContainer(
WebSocketMappings.ensureMappings(servletContext),
WebSocketServerComponents.getWebSocketComponents(servletContext),
coreClientSupplier);

// Manage the lifecycle of the Container.
contextHandler.addManaged(container);
contextHandler.addEventListener(container);
contextHandler.addEventListener(new LifeCycle.Listener()
{
@Override
public void lifeCycleStopping(LifeCycle event)
{
servletContext.removeAttribute(JAKARTA_WEBSOCKET_CONTAINER_ATTRIBUTE);
contextHandler.removeBean(container);
contextHandler.removeEventListener(container);
contextHandler.removeEventListener(this);
}
});

// Store a reference to the ServerContainer per - jakarta.websocket spec 1.0 final - section 6.4: Programmatic Server Deployment
servletContext.setAttribute(JAKARTA_WEBSOCKET_CONTAINER_ATTRIBUTE, container);
return container;
Expand Down Expand Up @@ -141,18 +155,6 @@ public JakartaWebSocketServerContainer(WebSocketMappings webSocketMappings, WebS
this.frameHandlerFactory = new JakartaWebSocketServerFrameHandlerFactory(this);
}

@Override
public void lifeCycleStopping(LifeCycle context)
{
ContextHandler contextHandler = (ContextHandler)context;
JakartaWebSocketServerContainer container = contextHandler.getBean(JakartaWebSocketServerContainer.class);
if (container == this)
{
contextHandler.removeBean(container);
LifeCycle.stop(container);
}
}

@Override
public JakartaWebSocketServerFrameHandlerFactory getFrameHandlerFactory()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// ========================================================================
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.websocket.jakarta.tests;

import java.net.URI;
import java.util.concurrent.TimeUnit;

import jakarta.websocket.Session;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.core.server.WebSocketServerComponents;
import org.eclipse.jetty.websocket.jakarta.client.internal.JakartaWebSocketClientContainer;
import org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.jakarta.server.internal.JakartaWebSocketServerContainer;
import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class JakartaWebSocketRestartTest
{
private Server server;
private ServerConnector connector;
private JakartaWebSocketClientContainer client;
private ServletContextHandler contextHandler;

@BeforeEach
public void before() throws Exception
{
server = new Server();
connector = new ServerConnector(server);
server.addConnector(connector);

contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
contextHandler.setContextPath("/");
server.setHandler(contextHandler);

client = new JakartaWebSocketClientContainer();
client.start();
}

@AfterEach
public void stop() throws Exception
{
client.stop();
server.stop();
}

@Test
public void testWebSocketRestart() throws Exception
{
JakartaWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
container.addEndpoint(EchoSocket.class));
server.start();

int numEventListeners = contextHandler.getEventListeners().size();
for (int i = 0; i < 100; i++)
{
server.stop();
server.start();
testEchoMessage();
}

// We have not accumulated websocket resources by restarting.
assertThat(contextHandler.getEventListeners().size(), is(numEventListeners));
assertThat(contextHandler.getContainedBeans(JakartaWebSocketServerContainer.class).size(), is(1));
assertThat(contextHandler.getContainedBeans(WebSocketServerComponents.class).size(), is(1));
assertNotNull(contextHandler.getServletContext().getAttribute(WebSocketServerComponents.WEBSOCKET_COMPONENTS_ATTRIBUTE));
assertNotNull(contextHandler.getServletContext().getAttribute(JakartaWebSocketServerContainer.JAKARTA_WEBSOCKET_CONTAINER_ATTRIBUTE));

// We have one filter, and it is a WebSocketUpgradeFilter.
FilterHolder[] filters = contextHandler.getServletHandler().getFilters();
assertThat(filters.length, is(1));
assertThat(filters[0].getFilter(), instanceOf(WebSocketUpgradeFilter.class));

// After stopping the websocket resources are cleaned up.
server.stop();
assertThat(contextHandler.getEventListeners().size(), is(0));
assertThat(contextHandler.getContainedBeans(JakartaWebSocketServerContainer.class).size(), is(0));
assertThat(contextHandler.getContainedBeans(WebSocketServerComponents.class).size(), is(0));
assertNull(contextHandler.getServletContext().getAttribute(WebSocketServerComponents.WEBSOCKET_COMPONENTS_ATTRIBUTE));
assertNull(contextHandler.getServletContext().getAttribute(JakartaWebSocketServerContainer.JAKARTA_WEBSOCKET_CONTAINER_ATTRIBUTE));
assertThat(contextHandler.getServletHandler().getFilters().length, is(0));
}

private void testEchoMessage() throws Exception
{
// Test we can upgrade to websocket and send a message.
URI uri = URI.create("ws://localhost:" + connector.getLocalPort());
EventSocket socket = new EventSocket();
try (Session session = client.connectToServer(socket, uri))
{
session.getBasicRemote().sendText("hello world");
}
assertTrue(socket.closeLatch.await(10, TimeUnit.SECONDS));

String msg = socket.textMessages.poll();
assertThat(msg, is("hello world"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,23 +66,37 @@ public static JettyWebSocketServerContainer ensureContainer(ServletContext servl
if (contextHandler.getServer() == null)
throw new IllegalStateException("Server has not been set on the ServletContextHandler");

JettyWebSocketServerContainer container = getContainer(servletContext);
if (container == null)
// If we find a container in the servlet context return it.
JettyWebSocketServerContainer containerFromServletContext = getContainer(servletContext);
if (containerFromServletContext != null)
return containerFromServletContext;

// Find Pre-Existing executor.
Executor executor = (Executor)servletContext.getAttribute("org.eclipse.jetty.server.Executor");
if (executor == null)
executor = contextHandler.getServer().getThreadPool();

// Create the Jetty ServerContainer implementation.
WebSocketMappings mappings = WebSocketMappings.ensureMappings(servletContext);
WebSocketComponents components = WebSocketServerComponents.getWebSocketComponents(servletContext);
JettyWebSocketServerContainer container = new JettyWebSocketServerContainer(contextHandler, mappings, components, executor);

// Manage the lifecycle of the Container.
contextHandler.addManaged(container);
contextHandler.addEventListener(container);
contextHandler.addEventListener(new LifeCycle.Listener()
{
// Find Pre-Existing executor
Executor executor = (Executor)servletContext.getAttribute("org.eclipse.jetty.server.Executor");
if (executor == null)
executor = contextHandler.getServer().getThreadPool();

// Create the Jetty ServerContainer implementation
WebSocketMappings mappings = WebSocketMappings.ensureMappings(servletContext);
WebSocketComponents components = WebSocketServerComponents.getWebSocketComponents(servletContext);
container = new JettyWebSocketServerContainer(contextHandler, mappings, components, executor);
servletContext.setAttribute(JETTY_WEBSOCKET_CONTAINER_ATTRIBUTE, container);
contextHandler.addManaged(container);
contextHandler.addEventListener(container);
}
@Override
public void lifeCycleStopping(LifeCycle event)
{
contextHandler.getServletContext().removeAttribute(JETTY_WEBSOCKET_CONTAINER_ATTRIBUTE);
contextHandler.removeBean(container);
contextHandler.removeEventListener(container);
contextHandler.removeEventListener(this);
}
});

servletContext.setAttribute(JETTY_WEBSOCKET_CONTAINER_ATTRIBUTE, container);
return container;
}

Expand Down
Loading

0 comments on commit 2d08ed6

Please sign in to comment.