javax.websocket.DeploymentException при развертывании приложения Grails на Tomcat

У меня есть приложение Grails с конечной точкой веб-сокета, которое отлично работает в процессе разработки. Я пытаюсь выполнить развертывание на Tomcat, но по какой-то причине каждый раз при развертывании я получаю от 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)

У меня нет нескольких конечных точек, так как это единственный файл WAR, который я развертываю. Вот структура моего класса:

@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)
  }
}

Я зарегистрировал слушателя в моем файле web.xml, как показано ниже:

<listener>
    <listener-class>my.package.MyServletChatListenerAnnotated</listener-class>
</listener>

Я использую Grails 2.4.4, Tomcat 7.0.57 на Ubuntu 14.04 и Java 7u72, последнюю версию всех этих версий.

Я также пытался развернуть этот пример (который также работает в разработке): https://github.com/vahidhedayati/grails-websocket-example

И я получаю ту же самую ошибку. Кто-нибудь знает, почему это может происходить? Я явно не пытаюсь развернуть несколько конечных точек.


person blacktide    schedule 20.11.2014    source источник


Ответы (1)


Я решил проблему. Четвертая строка contextInitialized:

serverContainer.addEndpoint(MyServletChatListenerAnnotated)

Пытался добавить конечную точку во второй раз. Кажется, Grails нуждается в этой строке в разработке, чтобы добавить конечную точку, но Tomcat добавляет конечную точку на основе аннотации @ServerEndpoint. Оборачивая эту строку в:

if (Environment.current == Environment.DEVELOPMENT) {
    serverContainer.addEndpoint(MyServletChatListenerAnnotated)
}

Решает вопрос в разработке и производстве.

person blacktide    schedule 20.11.2014
comment
У меня аналогичная проблема с приложением spring-mvc. Какой пакет содержит класс Environment? - person bachr; 30.11.2014
comment
Класс Environment относится только к Grails. Я считаю, что Spring имеет аналогичную функциональность в виде профилей среды. - person blacktide; 30.11.2014