Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #5832 - shutdown Javax WSClientContainer with webapp if possible. #5840

Merged

Conversation

lachlan-roberts
Copy link
Contributor

Issue #5832

If ContainerProvider.getWebSocketContainer() is used within a webapp, we will attach its lifeCycle to the ContextHandler so that it shuts down with the webapp and not in the ShutdownThread.

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
@gregw
Copy link
Contributor

gregw commented Dec 30, 2020

So here is a better idea based on the javax approach that might also work on tomcat deployments:

  • in the same jar as the client container add a ServletContainerInitializer. This will be discovered only if the client is deployed in a context.
  • The SCI registers a ServletContextListener so that it can get a stop event - even if no clients are yet started.
  • The SCI can also set the ServletContext on a static
  • When a client is started, it can look for the static on the SCI and if set it can add itself as an attribute to the ServletContext (perhaps with a list for multiple clients???)
  • When the ServletContextListener destoy get's called, it looks for the attribute to find the clients and stops them.

My only concern is the static and how to make that not be nasty if we run embedded or with the jar from the server classpath ??? But I'm sure you'll work it out :)

@lachlan-roberts
Copy link
Contributor Author

@gregw This would require a dependency on javax.servlet for the websocket-javax-client.

And I'm not sure how this would work with multiple webapps using websocket client containers. We couldn't just use a simple static because we would have multiple instances of the ServletContainerInitializer tied to the life cycles of the different webapps. Then we also need to know if the ServletContainerInitializer is not discovered so we can register with the shutdown thread instead.

@gregw
Copy link
Contributor

gregw commented Jan 4, 2021

@lachlan-roberts it can be a provided or optional dependency. If you don't run in a servlet container, then nothing will find the SCI and nothing will load the classes that have the dependency.

Multiple webapps are not a problem, as you have multiple classloaders and thus multiple statics.... unless it is loaded from the system classloader. So maybe a static map of ServletContext to client container.. which would often only have a single entry... unless something strange is in the classloading hierarchy.

Note that this is worse than an exception when stopping the maven plugin. Currently I think we are leaking an entire classloader of the webapp over stop/start cycles because the started threads will hold them in memory. So we have to fix this.

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
…utility

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
@gregw
Copy link
Contributor

gregw commented Jan 12, 2021

@lachlan-roberts is this ready for re-review yet?

@lachlan-roberts
Copy link
Contributor Author

@gregw yes its ready for review.

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Copy link
Contributor

@janbartel janbartel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to take a look at the use-case for the ServletContextHandler.addServletContainerInitializer methods, and consider the wider picture of how this works with those that are discovered by the AnnotationConfiguration: we should be aiming for more code reuse rather than 2 separate mechanisms.

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
LOG.debug("contextDestroyed({}) {}", sce, this);

LifeCycle.stop(this);
removeBeans();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that you have implemented doClientStop() I don't think this is necessary.

@@ -95,6 +112,11 @@ public JavaxWebSocketClientContainer(WebSocketComponents components, Function<We
this.frameHandlerFactory = new JavaxWebSocketClientFrameHandlerFactory(this);
}

public void allowShutdownWithContextHandler(boolean allowShutdownWithContextHandler)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be a proper setter method, starting with set and the getter should be there too, and used in the code.

server.stop();
assertThat(clientContainer.isRunning(), is(false));
assertThat(server.getContainedBeans(WebSocketContainer.class), empty());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add two tests that prove the behavior in case of multiple web applications, since we want to make sure that the static field SHUTDOWN_CONTAINER is not overwritten by other instances.

One test should have the classes provided by the server, and one by the web applications (both).

@joakime
Copy link
Contributor

joakime commented Feb 11, 2021

This PR needs copyright header updates.

[INFO] --- license-maven-plugin:3.0:check (check-java-headers) @ jetty-project ---

[INFO] Checking licenses...
[WARNING] Missing header in: PR-5840/jetty-websocket/websocket-javax-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/JavaxClientShutdownWithServerTest.java
[WARNING] Missing header in: PR-5840/jetty-websocket/websocket-javax-client/src/main/java/org/eclipse/jetty/websocket/javax/client/JavaxWebSocketShutdownContainer.java

…5832-WebSocketShutdownThread

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
return;
}

ShutdownThread.register(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment here

@@ -52,6 +58,16 @@
@ManagedObject("JSR356 Client Container")
public class JavaxWebSocketClientContainer extends JavaxWebSocketContainer implements javax.websocket.WebSocketContainer
{
private static final Logger LOG = LoggerFactory.getLogger(JavaxWebSocketClientContainer.class);
private static final AtomicReference<ContainerLifeCycle> SHUTDOWN_CONTAINER = new AtomicReference<>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I comment here about the scope of this static with regards to server/context classloaders would be handy

Copy link
Contributor

@gregw gregw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a few more comments, but I think this is fine.

Copy link
Contributor

@janbartel janbartel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have expected to see:

context.addServletContainerInitializer(new CdiServletContainerInitializer())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants