Uploaded image for project: 'Quarkus'
  1. Quarkus
  2. QUARKUS-1222

Regression: Undertow Websockets: Could not create an endpoint dynamically

XMLWordPrintable

    • False
    • False
    • +
    • Undertow Websockets: Could not create an endpoint dynamically
    • Hide
      import java.io.IOException;
      import java.util.Collections;
      import java.util.IdentityHashMap;
      import java.util.Set;
      
      import javax.enterprise.inject.spi.DeploymentException;
      import javax.servlet.ServletContextEvent;
      import javax.servlet.ServletContextListener;
      import javax.servlet.annotation.WebListener;
      import javax.websocket.CloseReason;
      import javax.websocket.Endpoint;
      import javax.websocket.EndpointConfig;
      import javax.websocket.Session;
      import javax.websocket.server.ServerContainer;
      import javax.websocket.server.ServerEndpointConfig;
      
      import org.jboss.logging.Logger;
      
      @WebListener
      public class PongLeakSample implements ServletContextListener {
          private static final Logger LOG = Logger.getLogger(PongLeakSample.class);
      
          private Thread pingThread;
          private Set<WebsockEndpoint> endpoints;
      
          public PongLeakSample() {
              endpoints = Collections.synchronizedSet(Collections.newSetFromMap(new IdentityHashMap<>()));
          }
      
          @Override
          public void contextDestroyed(ServletContextEvent sce) {
              pingThread.interrupt();
              endpoints.forEach(WebsockEndpoint::close);
              try {
                  pingThread.join();
              } catch (InterruptedException e) {
                  Thread.currentThread().interrupt();
              }
          }
      
          /**
           * @see ServletContextListener#contextInitialized(ServletContextEvent)
           */
          @Override
          public void contextInitialized(ServletContextEvent sce) {
              try {
                  ((ServerContainer) sce.getServletContext().getAttribute(ServerContainer.class.getName()))
                          .addEndpoint(ServerEndpointConfig.Builder.create(WebsockEndpoint.class, "/simple")
                                  .configurator(new WebsockEndpointConfigurator(this)).build());
                  
              } catch (DeploymentException | javax.websocket.DeploymentException e) {
                  e.printStackTrace();
              }
              pingThread = new Thread(this::pingRoutine);
              pingThread.setDaemon(true);
              pingThread.start();
          }
      
          private void pingRoutine() {
              try {
                  while (true) {
                      Thread.sleep(300);
                      endpoints.forEach(WebsockEndpoint::ping);
                  }
              } catch (InterruptedException e) {
                  Thread.currentThread().interrupt();
              }
          }
      
          public static class WebsockEndpoint extends Endpoint {
      
              private static final String PONG = "PONG";
              private final PongLeakSample filter;
              private Session session;
      
              public WebsockEndpoint(PongLeakSample filter) {
                  this.filter = filter;
              }
      
              @Override
              public void onOpen(Session session, EndpointConfig config) {
                  this.session = session;
                  filter.endpoints.add(this);
              }
      
              @Override
              public void onClose(Session session, CloseReason closeReason) {
                  filter.endpoints.remove(this);
              }
      
              public void ping() {
                  LOG.info("ping-pong invoked!");
                  session.getAsyncRemote().sendText(PONG);
              }
      
              public void close() {
                  try {
                      session.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      
          public static class WebsockEndpointConfigurator extends ServerEndpointConfig.Configurator {
              private PongLeakSample filter;
      
              WebsockEndpointConfigurator(PongLeakSample filter) {
                  this.filter = filter;
              }
      
              @Override
              public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
                  WebsockEndpoint endpoint = new WebsockEndpoint(filter);
                  if (endpointClass.isInstance(endpoint))
                      return endpointClass.cast(endpoint);
                  throw new InstantiationException(
                          "Requested class is not assignable from " + WebsockEndpoint.class.getName());
              }
          }
      }
      

      Run: 

      mvn quarkus:dev
      

      Test: 

      curl --include \\n --no-buffer \\n --header "Connection: Upgrade" \\n --header "Upgrade: websocket" \\n --header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \\n --header "Sec-WebSocket-Version: 13" \\n http://localhost:8080/simple
      

       

      Show
      import java.io.IOException; import java.util.Collections; import java.util.IdentityHashMap; import java.util.Set; import javax.enterprise.inject.spi.DeploymentException; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; import javax.websocket.CloseReason; import javax.websocket.Endpoint; import javax.websocket.EndpointConfig; import javax.websocket.Session; import javax.websocket.server.ServerContainer; import javax.websocket.server.ServerEndpointConfig; import org.jboss.logging.Logger; @WebListener public class PongLeakSample implements ServletContextListener { private static final Logger LOG = Logger.getLogger(PongLeakSample.class); private Thread pingThread; private Set<WebsockEndpoint> endpoints; public PongLeakSample() { endpoints = Collections.synchronizedSet(Collections.newSetFromMap( new IdentityHashMap<>())); } @Override public void contextDestroyed(ServletContextEvent sce) { pingThread.interrupt(); endpoints.forEach(WebsockEndpoint::close); try { pingThread.join(); } catch (InterruptedException e) { Thread .currentThread().interrupt(); } } /** * @see ServletContextListener#contextInitialized(ServletContextEvent) */ @Override public void contextInitialized(ServletContextEvent sce) { try { ((ServerContainer) sce.getServletContext().getAttribute(ServerContainer. class. getName())) .addEndpoint(ServerEndpointConfig.Builder.create(WebsockEndpoint.class, "/simple" ) .configurator( new WebsockEndpointConfigurator( this )).build()); } catch (DeploymentException | javax.websocket.DeploymentException e) { e.printStackTrace(); } pingThread = new Thread ( this ::pingRoutine); pingThread.setDaemon( true ); pingThread.start(); } private void pingRoutine() { try { while ( true ) { Thread .sleep(300); endpoints.forEach(WebsockEndpoint::ping); } } catch (InterruptedException e) { Thread .currentThread().interrupt(); } } public static class WebsockEndpoint extends Endpoint { private static final String PONG = "PONG" ; private final PongLeakSample filter; private Session session; public WebsockEndpoint(PongLeakSample filter) { this .filter = filter; } @Override public void onOpen(Session session, EndpointConfig config) { this .session = session; filter.endpoints.add( this ); } @Override public void onClose(Session session, CloseReason closeReason) { filter.endpoints.remove( this ); } public void ping() { LOG.info( "ping-pong invoked!" ); session.getAsyncRemote().sendText(PONG); } public void close() { try { session.close(); } catch (IOException e) { e.printStackTrace(); } } } public static class WebsockEndpointConfigurator extends ServerEndpointConfig.Configurator { private PongLeakSample filter; WebsockEndpointConfigurator(PongLeakSample filter) { this .filter = filter; } @Override public <T> T getEndpointInstance( Class <T> endpointClass) throws InstantiationException { WebsockEndpoint endpoint = new WebsockEndpoint(filter); if (endpointClass.isInstance(endpoint)) return endpointClass. cast (endpoint); throw new InstantiationException( "Requested class is not assignable from " + WebsockEndpoint. class. getName()); } } } Run:  mvn quarkus:dev Test:  curl --include \\n --no-buffer \\n --header "Connection: Upgrade" \\n --header "Upgrade: websocket" \\n --header "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \\n --header "Sec-WebSocket-Version: 13" \\n http: //localhost:8080/simple  

      This issue comes from upstream

      Extensions: io.quarkus:quarkus-undertow-websockets

      On version 1.11.7.Final I could create an endpoint dynamically as follow:

       

      ((ServerContainer) sce.getServletContext().getAttribute(ServerContainer.class.getName()))
      .addEndpoint(ServerEndpointConfig.Builder.create(WebsockEndpoint.class, "/simple").configurator(new WebsockEndpointConfigurator(this)).build());
      

       

      However after upgrade to version 1.13.7.Final I got a NullPointerException

       

      Caused by: java.lang.RuntimeException: java.lang.RuntimeException: java.lang.NullPointerException
      	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder.bootServletContainer(UndertowDeploymentRecorder.java:528)
      	at io.quarkus.deployment.steps.UndertowBuildStep$build-649634386.deploy_0(UndertowBuildStep$build-649634386.zig:238)
      	at io.quarkus.deployment.steps.UndertowBuildStep$build-649634386.deploy(UndertowBuildStep$build-649634386.zig:40)
      	at io.quarkus.runner.ApplicationImpl.<clinit>(ApplicationImpl.zig:169)
      	... 43 more
      Caused by: java.lang.RuntimeException: java.lang.NullPointerException
      	at io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:253)
      	at io.quarkus.undertow.runtime.UndertowDeploymentRecorder.bootServletContainer(UndertowDeploymentRecorder.java:517)
      	... 46 more
      Caused by: java.lang.NullPointerException
      	at io.quarkus.qe.undertow.PongLeakSample.contextInitialized(PongLeakSample.java:51)
      

      If instead of creating the endpoint as we described above we do by this way:

       

       

      ServerContainer container = (ServerContainer) ContainerProvider.getWebSocketContainer();
      
      container.addEndpoint(ServerEndpointConfig.Builder.create(WebsockEndpoint.class, "/simple").configurator(new WebsockEndpointConfigurator(this)).build());
      

      Then the nullpointer exception is not thrown but the endpoint is not created.

       

       

              sdouglas1@redhat.com Stuart Douglas (Inactive)
              rhn-support-pagonzal Pablo Gonzalez Granados (Inactive)
              Pablo Gonzalez Granados Pablo Gonzalez Granados (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated:
                Resolved: