Timothy Ward created FELIX-5310:
-----------------------------------

             Summary: Felix HTTP Jetty WebSockets do not work out of the box
                 Key: FELIX-5310
                 URL: https://issues.apache.org/jira/browse/FELIX-5310
             Project: Felix
          Issue Type: Bug
          Components: HTTP Service
    Affects Versions: http.jetty-3.2.0
            Reporter: Timothy Ward


I have encountered two significant problems when trying to use the Jetty 
WebSocketServlet with the Jetty based Felix HTTP service whiteboard. Both 
problems occur in the init method of my servlet.

 1. I have to set the TCCL for the Jetty Web Socket implementation to be able 
to find its own internal implementation of Websockets (specifically 
org.eclipse.jetty.websocket.server.WebSocketServerFactory). This is really 
stupid, as the TCCL I have to use is the Felix HTTP Jetty bundle's class loader!

2. The Jetty Web Socket implementation does one (and only one) check to check 
that it's running on Jetty. It looks as though the only reason for this is to 
get hold of an Executor which is available via a getter. This check involves 
trying to establish the Jetty Context by casting the ServletContext to a Jetty 
internal type. This does not work as the HTTP Whiteboard wrappers the 
ServletContext. I can work around this by delaying initialisation to the first 
request so that there is a context to get hold of, but this also sucks :(


In summary, my servlet has to look like this, most of which should not be 
necessary!

    public class EchoServlet extends WebSocketServlet {
    
        private static final Logger LOGGER = 
LoggerFactory.getLogger(EchoServlet.class);
    
        private final AtomicBoolean firstCall = new AtomicBoolean(true); 
    
        private final CountDownLatch initBarrier = new CountDownLatch(1); 
    
        @Override
        public void init() throws ServletException {
            LOGGER.info("The Echo servlet has been initialized, but we delay 
initialization until the first request so that a Jetty Context is available");  
   
        }
        
        @Override
        public void service(ServletRequest arg0, ServletResponse arg1) throws 
ServletException, IOException {
            if(firstCall.compareAndSet(true, false)) {
                try {
                    delayedInit();
                } finally {
                    initBarrier.countDown();
                }
            } else {
                try {
                    initBarrier.await();
                } catch (InterruptedException e) {
                    throw new ServletException("Timed out waiting for 
initialisation", e);
                }
            }
                
            super.service(arg0, arg1);
        }

        private void delayedInit() throws ServletException {
            // Overide the TCCL so that the internal factory can be found
            // Jetty tries to use ServiceLoader, and their fallback is to
            // use TCCL, it would be better if we could provide a loader...
                
            Thread currentThread = Thread.currentThread();
            ClassLoader tccl = currentThread.getContextClassLoader();
            
currentThread.setContextClassLoader(WebSocketServlet.class.getClassLoader());
            try {
                super.init();
            } finally {
                currentThread.setContextClassLoader(tccl);
            }
        }

        @Override
        public void configure(WebSocketServletFactory wsf) {
            wsf.setCreator((req,res) -> new WebSocketAdaptor() {
                    public void onWebSocketText(String message) {
                        getRemote().sendStringByFuture("Echo: " + message);
                    }
                });
        }
    }



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to