442495 - Bad Context ClassLoader in JSR356 WebSocket onOpen
+ Fixing onOpen context classloader to be that of the context that started the WebSocketUpgradeFilter (which will be the same as the WebAppContext in most cases)
This commit is contained in:
parent
d6082b2d65
commit
0dca1b0794
|
@ -131,105 +131,113 @@ public class WebSocketServerContainerInitializer implements ServletContainerInit
|
||||||
|
|
||||||
ServletContextHandler jettyContext = (ServletContextHandler)handler;
|
ServletContextHandler jettyContext = (ServletContextHandler)handler;
|
||||||
|
|
||||||
// Create the Jetty ServerContainer implementation
|
ClassLoader old = Thread.currentThread().getContextClassLoader();
|
||||||
ServerContainer jettyContainer = configureContext(context, jettyContext);
|
try
|
||||||
|
|
||||||
// Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
|
|
||||||
context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer);
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
{
|
{
|
||||||
LOG.debug("Found {} classes",c.size());
|
Thread.currentThread().setContextClassLoader(context.getClassLoader());
|
||||||
}
|
|
||||||
|
|
||||||
// Now process the incoming classes
|
// Create the Jetty ServerContainer implementation
|
||||||
Set<Class<? extends Endpoint>> discoveredExtendedEndpoints = new HashSet<>();
|
ServerContainer jettyContainer = configureContext(context,jettyContext);
|
||||||
Set<Class<?>> discoveredAnnotatedEndpoints = new HashSet<>();
|
|
||||||
Set<Class<? extends ServerApplicationConfig>> serverAppConfigs = new HashSet<>();
|
|
||||||
|
|
||||||
filterClasses(c,discoveredExtendedEndpoints,discoveredAnnotatedEndpoints,serverAppConfigs);
|
// Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
|
||||||
|
context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer);
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
|
||||||
{
|
|
||||||
LOG.debug("Discovered {} extends Endpoint classes",discoveredExtendedEndpoints.size());
|
|
||||||
LOG.debug("Discovered {} @ServerEndpoint classes",discoveredAnnotatedEndpoints.size());
|
|
||||||
LOG.debug("Discovered {} ServerApplicationConfig classes",serverAppConfigs.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the server app configs to determine endpoint filtering
|
|
||||||
boolean wasFiltered = false;
|
|
||||||
Set<ServerEndpointConfig> deployableExtendedEndpointConfigs = new HashSet<>();
|
|
||||||
Set<Class<?>> deployableAnnotatedEndpoints = new HashSet<>();
|
|
||||||
|
|
||||||
for (Class<? extends ServerApplicationConfig> clazz : serverAppConfigs)
|
|
||||||
{
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
{
|
{
|
||||||
LOG.debug("Found ServerApplicationConfig: {}",clazz);
|
LOG.debug("Found {} classes",c.size());
|
||||||
}
|
}
|
||||||
try
|
|
||||||
|
// Now process the incoming classes
|
||||||
|
Set<Class<? extends Endpoint>> discoveredExtendedEndpoints = new HashSet<>();
|
||||||
|
Set<Class<?>> discoveredAnnotatedEndpoints = new HashSet<>();
|
||||||
|
Set<Class<? extends ServerApplicationConfig>> serverAppConfigs = new HashSet<>();
|
||||||
|
|
||||||
|
filterClasses(c,discoveredExtendedEndpoints,discoveredAnnotatedEndpoints,serverAppConfigs);
|
||||||
|
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
{
|
{
|
||||||
ServerApplicationConfig config = clazz.newInstance();
|
LOG.debug("Discovered {} extends Endpoint classes",discoveredExtendedEndpoints.size());
|
||||||
|
LOG.debug("Discovered {} @ServerEndpoint classes",discoveredAnnotatedEndpoints.size());
|
||||||
|
LOG.debug("Discovered {} ServerApplicationConfig classes",serverAppConfigs.size());
|
||||||
|
}
|
||||||
|
|
||||||
Set<ServerEndpointConfig> seconfigs = config.getEndpointConfigs(discoveredExtendedEndpoints);
|
// Process the server app configs to determine endpoint filtering
|
||||||
if (seconfigs != null)
|
boolean wasFiltered = false;
|
||||||
|
Set<ServerEndpointConfig> deployableExtendedEndpointConfigs = new HashSet<>();
|
||||||
|
Set<Class<?>> deployableAnnotatedEndpoints = new HashSet<>();
|
||||||
|
|
||||||
|
for (Class<? extends ServerApplicationConfig> clazz : serverAppConfigs)
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
{
|
{
|
||||||
wasFiltered = true;
|
LOG.debug("Found ServerApplicationConfig: {}",clazz);
|
||||||
deployableExtendedEndpointConfigs.addAll(seconfigs);
|
|
||||||
}
|
}
|
||||||
|
try
|
||||||
Set<Class<?>> annotatedClasses = config.getAnnotatedEndpointClasses(discoveredAnnotatedEndpoints);
|
|
||||||
if (annotatedClasses != null)
|
|
||||||
{
|
{
|
||||||
wasFiltered = true;
|
ServerApplicationConfig config = clazz.newInstance();
|
||||||
deployableAnnotatedEndpoints.addAll(annotatedClasses);
|
|
||||||
|
Set<ServerEndpointConfig> seconfigs = config.getEndpointConfigs(discoveredExtendedEndpoints);
|
||||||
|
if (seconfigs != null)
|
||||||
|
{
|
||||||
|
wasFiltered = true;
|
||||||
|
deployableExtendedEndpointConfigs.addAll(seconfigs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Class<?>> annotatedClasses = config.getAnnotatedEndpointClasses(discoveredAnnotatedEndpoints);
|
||||||
|
if (annotatedClasses != null)
|
||||||
|
{
|
||||||
|
wasFiltered = true;
|
||||||
|
deployableAnnotatedEndpoints.addAll(annotatedClasses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InstantiationException | IllegalAccessException e)
|
||||||
|
{
|
||||||
|
throw new ServletException("Unable to instantiate: " + clazz.getName(),e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InstantiationException | IllegalAccessException e)
|
|
||||||
{
|
|
||||||
throw new ServletException("Unable to instantiate: " + clazz.getName(),e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default behavior if nothing filtered
|
// Default behavior if nothing filtered
|
||||||
if (!wasFiltered)
|
if (!wasFiltered)
|
||||||
{
|
{
|
||||||
deployableAnnotatedEndpoints.addAll(discoveredAnnotatedEndpoints);
|
deployableAnnotatedEndpoints.addAll(discoveredAnnotatedEndpoints);
|
||||||
// Note: it is impossible to determine path of "extends Endpoint" discovered classes
|
// Note: it is impossible to determine path of "extends Endpoint" discovered classes
|
||||||
deployableExtendedEndpointConfigs = new HashSet<>();
|
deployableExtendedEndpointConfigs = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
{
|
|
||||||
LOG.debug("Deploying {} ServerEndpointConfig(s)",deployableExtendedEndpointConfigs.size());
|
|
||||||
}
|
|
||||||
// Deploy what should be deployed.
|
|
||||||
for (ServerEndpointConfig config : deployableExtendedEndpointConfigs)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
jettyContainer.addEndpoint(config);
|
LOG.debug("Deploying {} ServerEndpointConfig(s)",deployableExtendedEndpointConfigs.size());
|
||||||
}
|
}
|
||||||
catch (DeploymentException e)
|
// Deploy what should be deployed.
|
||||||
|
for (ServerEndpointConfig config : deployableExtendedEndpointConfigs)
|
||||||
{
|
{
|
||||||
throw new ServletException(e);
|
try
|
||||||
|
{
|
||||||
|
jettyContainer.addEndpoint(config);
|
||||||
|
}
|
||||||
|
catch (DeploymentException e)
|
||||||
|
{
|
||||||
|
throw new ServletException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
{
|
|
||||||
LOG.debug("Deploying {} @ServerEndpoint(s)",deployableAnnotatedEndpoints.size());
|
|
||||||
}
|
|
||||||
for (Class<?> annotatedClass : deployableAnnotatedEndpoints)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
jettyContainer.addEndpoint(annotatedClass);
|
LOG.debug("Deploying {} @ServerEndpoint(s)",deployableAnnotatedEndpoints.size());
|
||||||
}
|
}
|
||||||
catch (DeploymentException e)
|
for (Class<?> annotatedClass : deployableAnnotatedEndpoints)
|
||||||
{
|
{
|
||||||
throw new ServletException(e);
|
try
|
||||||
|
{
|
||||||
|
jettyContainer.addEndpoint(annotatedClass);
|
||||||
|
}
|
||||||
|
catch (DeploymentException e)
|
||||||
|
{
|
||||||
|
throw new ServletException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
Thread.currentThread().setContextClassLoader(old);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc
|
||||||
private final LogicalConnection connection;
|
private final LogicalConnection connection;
|
||||||
private final SessionListener[] sessionListeners;
|
private final SessionListener[] sessionListeners;
|
||||||
private final Executor executor;
|
private final Executor executor;
|
||||||
|
private ClassLoader classLoader;
|
||||||
private ExtensionFactory extensionFactory;
|
private ExtensionFactory extensionFactory;
|
||||||
private String protocolVersion;
|
private String protocolVersion;
|
||||||
private Map<String, String[]> parameterMap = new HashMap<>();
|
private Map<String, String[]> parameterMap = new HashMap<>();
|
||||||
|
@ -78,6 +79,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc
|
||||||
throw new RuntimeException("Request URI cannot be null");
|
throw new RuntimeException("Request URI cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
this.requestURI = requestURI;
|
this.requestURI = requestURI;
|
||||||
this.websocket = websocket;
|
this.websocket = websocket;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
@ -183,6 +185,11 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc
|
||||||
return this.connection.getBufferPool();
|
return this.connection.getBufferPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClassLoader getClassLoader()
|
||||||
|
{
|
||||||
|
return this.getClass().getClassLoader();
|
||||||
|
}
|
||||||
|
|
||||||
public LogicalConnection getConnection()
|
public LogicalConnection getConnection()
|
||||||
{
|
{
|
||||||
return connection;
|
return connection;
|
||||||
|
@ -394,14 +401,16 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upgrade success
|
ClassLoader old = Thread.currentThread().getContextClassLoader();
|
||||||
connection.getIOState().onConnected();
|
try {
|
||||||
|
Thread.currentThread().setContextClassLoader(classLoader);
|
||||||
|
|
||||||
// Connect remote
|
// Upgrade success
|
||||||
remote = new WebSocketRemoteEndpoint(connection,outgoingHandler,getBatchMode());
|
connection.getIOState().onConnected();
|
||||||
|
|
||||||
|
// Connect remote
|
||||||
|
remote = new WebSocketRemoteEndpoint(connection,outgoingHandler,getBatchMode());
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Open WebSocket
|
// Open WebSocket
|
||||||
websocket.openSession(this);
|
websocket.openSession(this);
|
||||||
|
|
||||||
|
@ -425,6 +434,10 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc
|
||||||
|
|
||||||
close(statusCode,t.getMessage());
|
close(statusCode,t.getMessage());
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Thread.currentThread().setContextClassLoader(old);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExtensionFactory(ExtensionFactory extensionFactory)
|
public void setExtensionFactory(ExtensionFactory extensionFactory)
|
||||||
|
@ -506,4 +519,5 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc
|
||||||
builder.append("]");
|
builder.append("]");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
@ -70,6 +71,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(WebSocketServerFactory.class);
|
private static final Logger LOG = Log.getLogger(WebSocketServerFactory.class);
|
||||||
|
|
||||||
|
private final ClassLoader contextClassloader;
|
||||||
private final Map<Integer, WebSocketHandshake> handshakes = new HashMap<>();
|
private final Map<Integer, WebSocketHandshake> handshakes = new HashMap<>();
|
||||||
/**
|
/**
|
||||||
* Have the factory maintain 1 and only 1 scheduler. All connections share this scheduler.
|
* Have the factory maintain 1 and only 1 scheduler. All connections share this scheduler.
|
||||||
|
@ -107,6 +109,8 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
|
||||||
addBean(scheduler);
|
addBean(scheduler);
|
||||||
addBean(bufferPool);
|
addBean(bufferPool);
|
||||||
|
|
||||||
|
this.contextClassloader = Thread.currentThread().getContextClassLoader();
|
||||||
|
|
||||||
this.registeredSocketClasses = new ArrayList<>();
|
this.registeredSocketClasses = new ArrayList<>();
|
||||||
|
|
||||||
this.defaultPolicy = policy;
|
this.defaultPolicy = policy;
|
||||||
|
@ -151,8 +155,10 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
|
||||||
@Override
|
@Override
|
||||||
public boolean acceptWebSocket(WebSocketCreator creator, HttpServletRequest request, HttpServletResponse response) throws IOException
|
public boolean acceptWebSocket(WebSocketCreator creator, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
{
|
{
|
||||||
|
ClassLoader old = Thread.currentThread().getContextClassLoader();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Thread.currentThread().setContextClassLoader(contextClassloader);
|
||||||
ServletUpgradeRequest sockreq = new ServletUpgradeRequest(request);
|
ServletUpgradeRequest sockreq = new ServletUpgradeRequest(request);
|
||||||
ServletUpgradeResponse sockresp = new ServletUpgradeResponse(response);
|
ServletUpgradeResponse sockresp = new ServletUpgradeResponse(response);
|
||||||
|
|
||||||
|
@ -182,6 +188,10 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
|
||||||
{
|
{
|
||||||
throw new IOException("Unable to accept websocket due to mangled URI", e);
|
throw new IOException("Unable to accept websocket due to mangled URI", e);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Thread.currentThread().setContextClassLoader(old);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSessionFactory(SessionFactory sessionFactory)
|
public void addSessionFactory(SessionFactory sessionFactory)
|
||||||
|
|
|
@ -91,10 +91,7 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
||||||
String pathSpec = "/*";
|
String pathSpec = "/*";
|
||||||
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
|
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
|
||||||
boolean isMatchAfter = false;
|
boolean isMatchAfter = false;
|
||||||
String urlPatterns[] =
|
String urlPatterns[] = { pathSpec };
|
||||||
{
|
|
||||||
pathSpec
|
|
||||||
};
|
|
||||||
|
|
||||||
FilterRegistration.Dynamic dyn = context.addFilter(name,filter);
|
FilterRegistration.Dynamic dyn = context.addFilter(name,filter);
|
||||||
dyn.addMappingForUrlPatterns(dispatcherTypes,isMatchAfter,urlPatterns);
|
dyn.addMappingForUrlPatterns(dispatcherTypes,isMatchAfter,urlPatterns);
|
||||||
|
|
Loading…
Reference in New Issue