javajettyjava-websocket

How do you close WebSocketContainer / WebSocketClient / Jetty client in Java?


I'm trying to use the Jetty's WebSocket client, but the application would just stuck there and won't exit. There is nothing fancy about it...

public class TestEndPoint extends Endpoint {
    private Session sess;

    public TestEndPoint(URI endpoint) throws Exception {

        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
        container.connectToServer(this, endpoint);
    }

    public void close() {
        try {
            this.sess.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    ...
}

I am connecting that to my PHP Ratchet WS, nothing special, no special logic, I just print out open when a client connects and close when client disconnects.

Then I run it like this:

public static void main(String[] args) {
    try {
        TestEndPoint test = new TestEndPoint(new URI("ws://localhost:8080/test"));
        Thread.sleep(5000);
        test.close();
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}

I can see that my WS would print "close", but then the Java application would never exit. so I took a thread dump and see that the jetty stuff is stuck there in some QueuedThreadPool...

Some of the thread dump:

"WebSocketContainer@1735934726-15" #15 daemon prio=5 os_prio=0 tid=0x0000000016cdf000 nid=0x1c5c runnable [0x000000001763e000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll0(Native Method)
    at sun.nio.ch.WindowsSelectorImpl$SubSelector.poll(WindowsSelectorImpl.java:296)
    at sun.nio.ch.WindowsSelectorImpl$SubSelector.access$400(WindowsSelectorImpl.java:278)
    at sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:159)
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)
    - locked <0x00000000ebf678c8> (a sun.nio.ch.Util$3)
    - locked <0x00000000ebf68128> (a java.util.Collections$UnmodifiableSet)
    - locked <0x00000000ebf67d40> (a sun.nio.ch.WindowsSelectorImpl)
    at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97)
    at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:101)
    at org.eclipse.jetty.io.ManagedSelector$SelectorProducer.select(ManagedSelector.java:346)
    at org.eclipse.jetty.io.ManagedSelector$SelectorProducer.produce(ManagedSelector.java:299)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:179)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:140)
    at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)
    at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:243)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:679)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:597)
    at java.lang.Thread.run(Thread.java:748)


"WebSocketContainer@1735934726-14" #14 daemon prio=5 os_prio=0 tid=0x0000000016cc7000 nid=0xfb4 waiting on condition [0x000000001753f000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000ebde1df8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
    at org.eclipse.jetty.util.BlockingArrayQueue.poll(BlockingArrayQueue.java:392)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.idleJobPoll(QueuedThreadPool.java:571)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.access$800(QueuedThreadPool.java:50)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:634)
    at java.lang.Thread.run(Thread.java:748)

Finally, I'm using these version of Jetty in my Gradle;

compile group: 'org.eclipse.jetty.websocket', name: 'websocket-api', version: '9.4.6.v20170531'
compile group: 'org.eclipse.jetty.websocket', name: 'websocket-client', version: '9.4.6.v20170531'
compile group: 'org.eclipse.jetty.websocket', name: 'javax-websocket-client-impl', version: '9.4.7.v20170914'

What's the right way to close a web-socket or client?


Solution

  • After much Googling and looking around many sample code, it looks like you need to manually stop the WebSocketContainer by casting it to the org.eclipse.jetty.util.component.LifeCycle!

    In order to stop the Jetty's WebSocket Client, you need to do the following, either via the Session or the WebSocketContainer itself. So in my close function:

    try {
        Session sess = getSession();
        WebSocketContainer container = sess.getContainer();
    
        // Need to cast the container to Jetty's LifeCycle
        if(container != null && container instanceof LifeCycle) { 
            Logger.debug("Stopping Jetty's WebSocket Client");
            ((LifeCycle) container).stop(); 
        }
    
        sess.close();
    } catch (Exception e) {
        Logger.error(e);
    }
    

    There is no documentation on this what-so-ever on Jetty's website either (http://www.eclipse.org/jetty/documentation/9.4.7.v20170914/)