Bug 64153 - ServerContainer is not available in ServletContext
Summary: ServerContainer is not available in ServletContext
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 9
Classification: Unclassified
Component: WebSocket (show other bugs)
Version: 9.0.31
Hardware: PC Linux
: P2 normal (vote)
Target Milestone: -----
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-02-18 13:05 UTC by Boris Petrov
Modified: 2020-02-26 21:09 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Boris Petrov 2020-02-18 13:05:10 UTC
After updating Tomcat from 9.0.30 to 9.0.31 our integration tests don't even start. Running Tomcat 9.0.31 in production works fine. In the integration tests we use "org.apache.tomcat.embed:tomcat-embed-websocket" so I guess the issue is somewhere there.

We use CometD and upon boot, the following exception is raised:

```
java.lang.IllegalArgumentException: Missing WebSocket ServerContainer
        at org.cometd.websocket.server.WebSocketTransport.init(WebSocketTransport.java:72)
        at org.cometd.server.BayeuxServerImpl.initializeServerTransports(BayeuxServerImpl.java:255)
        at org.cometd.server.BayeuxServerImpl.doStart(BayeuxServerImpl.java:135)
        at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
        at org.cometd.server.CometDServlet.init(CometDServlet.java:64)
        at org.cometd.annotation.AnnotationCometDServlet.init(AnnotationCometDServlet.java:52)
        at javax.servlet.GenericServlet.init(GenericServlet.java:244)
        at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1134)
        at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1089)
        at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:983)
        at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4871)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5180)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
        at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:140)
        at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
        at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:841)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1384)
        at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1374)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at org.apache.tomcat.util.threads.InlineExecutorService.execute(InlineExecutorService.java:75)
        at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:140)
        at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:909)
        at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.StandardService.startInternal(StandardService.java:421)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:930)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
        at org.apache.catalina.startup.Tomcat.start(Tomcat.java:467)
        ...
```

The error is coming from this line:

https://github.com/cometd/cometd/blob/3.1.12/cometd-java/cometd-java-websocket/cometd-java-websocket-javax-server/src/main/java/org/cometd/websocket/server/WebSocketTransport.java#L70

This is our initialization code:

```
tomcat = Tomcat.new
tomcat.port = @port
tomcat.connector.add_upgrade_protocol(Http2Protocol.new) # this will create the default connector

base_path = java.nio.file.Path.of(Dir.tmpdir, 'project/webapp').to_string
root_ctx = tomcat.add_context('/project', base_path)
root_ctx.add_mime_mapping('css', 'text/css')
root_ctx.add_mime_mapping('js', 'application/javascript')
root_ctx.loader = WebappLoader.new(JRuby.runtime.jruby_class_loader)

ctx_cfg = ProjectContextConfig.new
ctx_cfg.default_web_xml = tomcat.no_default_web_xml_path
root_ctx.add_lifecycle_listener(ctx_cfg)

tomcat.start
```

I have no clue what could have changed and what is broken. Any suggestions as to how do I debug this would be appreciated.
Comment 1 Mark Thomas 2020-02-21 21:04:43 UTC
The change that looks most closely related is bug 64021 but I don;t see how that could cause the WebSocket implementation not to be found.

I'd start debugging around o.a.catalina.startup.WebappServiceLoader

Alternatively, if you provide a simple test case that reproduces the issue we can take a look.
Comment 2 Boris Petrov 2020-02-24 09:03:11 UTC
Mark, thanks for the support. Here is a reproduction project:

https://github.com/boris-petrov/tomcat-bug

Sorry for all the accidental complexity with Ruby and all that but it was the easiest way for me to reproduce the problem. Clone the repo and run "gradle integrationTest" in it. You'll see the error (there is also one about a missing "saxon.jar" - ignore that one). If you open "build.gradle" and change Tomcat's version from 9.0.31 to 9.0.30 and run "gradle integrationTest" again - you'll see the error gone (only the one for the missing jar will be there).

Please tell me if you need any more information from me.

I run this on:

%  gradle --version

------------------------------------------------------------
Gradle 6.1.1
------------------------------------------------------------

Build time:   2020-01-26 12:47:38 UTC
Revision:     <unknown>

Kotlin:       1.3.61
Groovy:       2.5.8
Ant:          Apache Ant(TM) version 1.10.7 compiled on September 1 2019
JVM:          13.0.2 (Oracle Corporation 13.0.2+8)
OS:           Linux 5.5.5-arch1-1 amd64
Comment 3 Mark Thomas 2020-02-25 21:09:46 UTC
nokogiri 1.10.8 is broken. It includes jing.jar in the lib directory that in turn has a reference to saxon.jar in its classpath. That JAR is missing. I wondered if that failure was causing the WebSocket container failure but that does not seem to be the case.

Still investigating.

This would be a lot easier with a WAR that I could deploy to my own Tomcat instance rather than trying to figure out how to get better logging and/or debugging working with a bunch of technologies I am unfamiliar with.
Comment 4 Boris Petrov 2020-02-25 21:17:49 UTC
Yes, I'm sorry the reproduction project is far from the best possible but it was easiest for me. You're more familiar with Tomcat itself and I believe that the important code is in `server_runner.rb` so if you could just use that as a blueprint to create a new project that reproduces the issue...? I guess the problem is somewhere in "org.apache.tomcat.embed:tomcat-embed-websocket" because that's what's missing when using a production/standalone Tomcat where the problem doesn't appear.

As for the missing JAR - as I said, that's not relevant here. In our own project we don't get that error. I'm not sure why it's here but it doesn't matter anyway - the "real" issue is visible even with it.
Comment 5 Mark Thomas 2020-02-25 23:13:02 UTC
It is the change for bug 64021.

It is caused by a difference between context.getParentClassLoader() and context.getLoader().getClassLoader().getParent().

I haven't got a fix yet but it should be fairly simple,
Comment 6 Mark Thomas 2020-02-26 17:16:20 UTC
As a work-around, use:

root_ctx.parentClassLoader = JRuby.runtime.jruby_class_loader

rather than

root_ctx.loader = WebappLoader.new(JRuby.runtime.jruby_class_loader)

The second approach is probably going to be deprecated.
Comment 7 Mark Thomas 2020-02-26 18:55:16 UTC
Fixed in:
- master for 10.0.0-M2 onwards
- 9.0.x for 9.0.32 onwards
- 8.5.x for 8.5.52 onwards
- 7.0.x for 7.0.101 onwards
Comment 8 Boris Petrov 2020-02-26 21:09:20 UTC
Thanks for the workaround, works like a charm! And thanks for the support and quick fix!