I'm writing a container-agnostic Websocket server. My development environment uses IntelliJ IDEA with an embedded Jetty version 9.3.11.v20160721
(embed code below).
My code, however, uses the Tomcat Websocket libraries, tomcat-websocket-api
and tomcat-websocket
, version 8.5.4
.
I want to register the Websocket EndPoint
with a ServletContextListener
. Following some examples from the Tyrus project, as well as some answers on SO and in the Jetty mailing list by Joakim Erdfelt (@joakim-erdfelt), I wrote the following implementation and added it via a listener
in web.xml
.
The code enters the contextInitialized()
method so that part seems to work properly, but unfortunately the ServletContext does not contain the attribute javax.websocket.server.ServerContainer
and therefore it returns null.
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
final ServerContainer serverContainer = (ServerContainer) servletContextEvent
.getServletContext()
.getAttribute("javax.websocket.server.ServerContainer");
// *** serverContainer is null ***
try {
serverContainer.addEndpoint(ChatAnnotation.class);
}
catch (DeploymentException e) { e.printStackTrace(); }
}
The only available attributes are javax.servlet.context.tempdir
, org.eclipse.jetty.util.DecoratedObjectFactory
, org.eclipse.jetty.tlds
, org.eclipse.jetty.resources
, org.eclipse.jetty.server.Executor
, org.eclipse.jetty.webFragments
What am I doing wrong? Is this strictly an embedding-issue? Will it run properly in standard Jetty (i.e., not embedded)?
The embed code is below:
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/");
webapp.setResourceBase(webroot);
webapp.setDescriptor(webroot + "/WEB-INF/web.xml");
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8080);
server.setConnectors(new ServerConnector[] { connector });
server.setHandler(webapp);
server.start();
server.join();
The JSR ServerContainer isn't initialized automatically in embedded-jetty.
Use the WebSocketServerContainerInitializer
...
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/");
webapp.setResourceBase(webroot);
webapp.setDescriptor(webroot + "/WEB-INF/web.xml");
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8080);
server.setConnectors(new ServerConnector[] { connector });
server.setHandler(webapp);
// Add this line
WebSocketServerContainerInitializer.configureContext(webapp);
server.start();
server.join();
Note: The WebSocketServerContainerInitializer
is a javax.servlet.ServerContainerInitializer
and you could set up your embedded-jetty
to execute that ServerContainerInitializer
automatically.
If you heavily rely on bytecode scanning of annotations, or have a javax.websocket.server.ServerApplicationConfig
implementation, then you'll want to have the ServerContainerInitializer
execute automatically.
Note: the
javax.servlet.ServerContainerInitializer
is only valid for aorg.eclipse.jetty.webapp.WebAppContext
.If you use the
org.eclipse.jetty.servlet.ServletContextHandler
(the more common form in embedded-jetty) then you cannot use ajavax.servlet.ServerContainerInitializer
.
To set that up, do this:
private void enableAnnotationScanning(Server server)
{
Configuration.ClassList classlist = Configuration.ClassList.setServerDefault(server);
classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration",
"org.eclipse.jetty.plus.webapp.EnvConfiguration",
"org.eclipse.jetty.plus.webapp.PlusConfiguration");
classlist.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
"org.eclipse.jetty.annotations.AnnotationConfiguration");
}
// ... later on ...
Server server = new Server();
enableAnnotationScanning(server);