I have a Grails application with a websocket endpoint that works perfectly fine in development. I am trying to deploy to Tomcat, but for some reason every time I deploy I receive the following stacktrace from Tomcat:
SEVERE: Exception sending context initialized event to listener instance of class my.package.MyServletChatListenerAnnotated
javax.websocket.DeploymentException: Multiple Endpoints may not be deployed to the same path [/chatroomServerEndpoint]
at org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java:207)
at org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java:271)
at javax.websocket.server.ServerContainer$addEndpoint.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
at grails.websocket.example.MyServletChatListenerAnnotated.contextInitialized(MyServletChatListenerAnnotated.groovy:36)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4994)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5492)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1081)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:553)
at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1668)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at org.apache.catalina.manager.ManagerServlet.check(ManagerServlet.java:1480)
at org.apache.catalina.manager.HTMLManagerServlet.upload(HTMLManagerServlet.java:286)
at org.apache.catalina.manager.HTMLManagerServlet.doPost(HTMLManagerServlet.java:206)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.filters.CsrfPreventionFilter.doFilter(CsrfPreventionFilter.java:213)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.filters.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:108)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:612)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1736)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1695)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
I do not have multiple endpoints, as this is the only WAR file that I am deploying. Here is the structure of my class:
@WebListener
@ServerEndpoint("/chatroomServerEndpoint")
public class MyServletChatListenerAnnotated implements ServletContextListener {
private final Logger log = LoggerFactory.getLogger(getClass().name)
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.servletContext
ServerContainer serverContainer = (ServerContainer) servletContext.getAttribute("javax.websocket.server.ServerContainer")
try {
serverContainer.addEndpoint(MyServletChatListenerAnnotated)
ApplicationContext ctx = (ApplicationContext) servletContext.getAttribute(GA.APPLICATION_CONTEXT)
GrailsApplication grailsApplication = ctx.grailsApplication
ConfigObject config = grailsApplication.config
Integer defaultMaxSessionIdleTimeout = config.myservlet.timeout ?: 0
serverContainer.defaultMaxSessionIdleTimeout = defaultMaxSessionIdleTimeout
} catch (IOException e) {
log.error(e.message, e)
}
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
// ...
}
@OnOpen
public void handleOpen(Session userSession) {
// ...
}
@OnMessage
public String handleMessage(String message, Session userSession) throws IOException {
// ...
}
@OnClose
public void handleClose(Session userSession) {
// ...
}
@OnError
public void handleError(Throwable t) {
log.error("An error occurred.", t)
}
}
I've registered the listener in my web.xml
file as shown below:
<listener>
<listener-class>my.package.MyServletChatListenerAnnotated</listener-class>
</listener>
I'm using Grails 2.4.4, Tomcat 7.0.57 on Ubuntu 14.04, and Java 7u72, the latest version of all of these.
I've also tried deploying this example (which also works in development): https://github.com/vahidhedayati/grails-websocket-example
And I receive the same exact error. Does anyone have any idea why this could be happening? I'm clearly not trying to deploy multiple endpoints.
I've resolved the issue. The fourth line of
contextInitialized
:Was attempting to add the endpoint a second time. It seems that Grails needs this line in development to add the endpoint, but Tomcat adds the endpoint based on the
@ServerEndpoint
annotation. Wrapping this line in:Resolves the issue in development and production.