Affects: 5.3.23
I have a working websocket configured and running on Spring 5.3.23 with Jetty 9.4.48.v20220622. When I attempt to upgrade to Jetty 10.0.12, the server responds with 500 Server Error when clients try to open a websocket connection due to the following exception:
org.sf.web.socket.server.HandshakeFailureException: Failed to upgrade; nested exception is java.lang.IllegalArgumentException: org.eclipse.jetty.websocket.server.JettyWebSocketCreator referenced from a method is not visible from class loader at org.sf.web.socket.server.jetty.Jetty10RequestUpgradeStrategy.upgrade(Jetty10RequestUpgradeStrategy.java:124) at org.sf.web.socket.server.support.AbstractHandshakeHandler.doHandshake(AbstractHandshakeHandler.java:297) at org.sf.web.socket.server.support.WebSocketHttpRequestHandler.handleRequest(WebSocketHttpRequestHandler.java:178) at org.sf.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:52) at org.sf.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071) … at org.eclipse.jetty.io.ssl.SslConnection$1.run(SslConnection.java:132) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:933) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1077) at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.IllegalArgumentException: org.eclipse.jetty.websocket.server.JettyWebSocketCreator referenced from a method is not visible from class loader at java.base/java.lang.reflect.Proxy$ProxyBuilder.ensureVisible(Proxy.java:883) at java.base/java.lang.reflect.Proxy$ProxyBuilder.validateProxyInterfaces(Proxy.java:721) at java.base/java.lang.reflect.Proxy$ProxyBuilder.
(Proxy.java:648) at java.base/java.lang.reflect.Proxy.lambda$getProxyConstructor$1(Proxy.java:440) at java.base/jdk.internal.loader.AbstractClassLoaderValue$Memoizer.get(AbstractClassLoaderValue.java:329) at java.base/jdk.internal.loader.AbstractClassLoaderValue.computeIfAbsent(AbstractClassLoaderValue.java:205) at java.base/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:438) at java.base/java.lang.reflect.Proxy.newProxyInstance(Proxy.java:1037) at org.sf.aop.framework.JdkDynamicAopProxy.getProxy(JdkDynamicAopProxy.java:126) at org.sf.aop.framework.JdkDynamicAopProxy.getProxy(JdkDynamicAopProxy.java:118) at org.sf.aop.framework.ProxyFactory.getProxy(ProxyFactory.java:97) at org.sf.web.socket.server.jetty.Jetty10RequestUpgradeStrategy.createJettyWebSocketCreator(Jetty10RequestUpgradeStrategy.java:134) at org.sf.web.socket.server.jetty.Jetty10RequestUpgradeStrategy.upgrade(Jetty10RequestUpgradeStrategy.java:116) ... 116 more
The IllegalArgumentException
originates in java.lang.reflect.Proxy.ensureVisible
, which is attempting to ensure the visibility of JettyWebSocketCreator
to a org.eclipse.jetty.webapp.WebAppClassLoader
. Note that JettyWebSocketCreator
has already been successfully loaded by the main class loader. For some reason though, the WebAppClassLoader
can't find it.
Comment From: rstoyanchev
@2is10, I've updated how JettyWebSocketCreator
is created to refer to the same ClassLoader
as the one in which the Class
itself was loaded. If you're able to give it a try and confirm it works for you with a 5.3.26-SNAPSHOT
that would be great.
Comment From: 2is10
@rstoyanchev Missed the earlier notification. Will try this out. Thank you!
Comment From: rstoyanchev
Thanks, that would be great! I can't reproduce the issue myself, but I did try with a sample to confirm the change works.
Comment From: 2is10
Hmm. 5.3.26-SNAPSHOT
is not found in our configured maven repositories. I may just wait for release 5.3.26 to confirm the fix.
Comment From: bclozel
@2is10 You can configure additional repositories like this:
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
or this:
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
}
Comment From: 2is10
Thanks @bclozel. I just tried again with 5.3.26-SNAPSHOT. JettyWebSocketCreator
is now accessible, but a NullPointerException
occurs in Jetty10RequestUpgradeStrategy
a few lines later. The container
returned here is null
:
Object container = ReflectionUtils.invokeMethod(getContainerMethod, null, servletContext);
The method invoked by reflection there is JettyWebSocketServerContainer.getContainer
:
public static JettyWebSocketServerContainer getContainer(ServletContext servletContext)
{
return (JettyWebSocketServerContainer)servletContext.getAttribute(JETTY_WEBSOCKET_CONTAINER_ATTRIBUTE);
}
That returns null
if JettyWebSocketServerContainer.ensureContainer
is never called. There is a single caller of that method in that library (websocket-jetty-server-10.0.14.jar): JettyWebSocketServletContainerInitializer.initialize
. Its doc says it should be invoked via JettyWebSocketServletContainerInitializer.configure
. Is that Spring’s job or mine?
Comment From: bclozel
Rossen did verify the fix with a sample, but maybe your application has a different setup or use case. Could you share a minimal sample that demonstrates the issue?
Comment From: 2is10
Our server code is nearly 10 years old, quite large, and doesn’t use Spring Boot. It would take me hours to produce a minimal sample. Here’s an idea. Could you run your sample with a breakpoint in JettyWebSocketServletContainerInitializer.initialize
and share the stack trace? That would help me understand how you expect it to get called.