Search code examples
javawebsocketencoderoncloselistener

WebSocket: OnClose() is never called


I'm implementing an application with a WebSocket endpoint. Here is some code:

@ApplicationScoped
@ServerEndpoint(value="/socket", encoders = {MessageEncoder.class, CommandEncoder.class})
public class SocketEndpoint {

    /** Default-Logger */
    private final static Logger LOG = LoggerFactory.getLogger(SocketEndpoint.class);

    @Inject
    SessionHandler sessionHandler;

    @OnOpen
    public void open(Session session, EndpointConfig config) {
        LOG.debug("Connected session => '{}' - '{}'", session, config);
        sessionHandler.initSession(session);
    }

    @OnMessage
    public void onMessage(Session session, String messageJson) {
        // do something
    }

    @OnClose
    public void onClose(Session session, CloseReason reason) {
        LOG.debug("Closing session => '{}' - '{}'", session, reason);
        sessionHandler.removeSession(session);
    }

    @OnError
    public void onError(Session session, Throwable ex) { 
        LOG.error("WebSocket error => '{}'", ex.getMessage()); 
    }
}

One of the encode class looks like this:

public class MessageEncoder implements Encoder.Text<Message> {

    /** Default-Logger */
    private final static Logger LOG = LoggerFactory.getLogger(MessageEncoder.class);

    @Override
    public void init(EndpointConfig config) {
        LOG.debug("Init MessageEncoder");
    }

    @Override
    public void destroy() {
        LOG.debug("Destroy MessageEncoder");
    }

    @Override
    public String encode(MessageE message) throws EncodeException {
        return message.toString();
    }
}

Opening the WebSocket calls SocketEndpoint.open() as expected. Closing the WebSocket only calls MessageEncoder.destroy() but not SocketEndpoint.close().

Can anyone give me an advise, what I did wrong? Without a solution I would have to check manually if registered sessions are still alive since MessageEncoder.destroy() has no parameters.

Thanks in advance!


UPDATE

Just implemented a dummy endpoint:

@ApplicationScoped
@ServerEndpoint("/dummy")
public class DummyEndpoint {

    /** Default-Logger */
    private final static Logger LOG = LoggerFactory.getLogger(DummyEndpoint.class);

    @OnOpen
    public void open(Session session, EndpointConfig config) {
        LOG.debug("Connected session with principal => '{}'", session.getId());
    }

    @OnMessage
    public void onMessage(Session session, String messageJson) {
        LOG.debug("on message => '{}' => '{}'", session.getId(), messageJson); 
    }

    @OnClose
    public void onClose(Session session, CloseReason reason) {
        LOG.debug("Closing session => '{}' - '{}'", session, reason);
    }

    @OnError
    public void onError(Session session, Throwable ex) { 
        LOG.error("WebSocket error => '{}' => '{}'", session, ex.getMessage()); 
    }
}

When using this dummy endpoint @OnClose is properly called. I can only see one major difference to the SocketEndpoint class: DummyEndpoint does not use any Encoder classes.

Any hints?


Solution

  • As mentioned in the comments, the code is working just fine. If we start from this wildfly-websocket-quickstart, adding an @OnClose decorated method on the ServerEndpoint, it works fine using Wildfly 10.x, and recent browsers (for instance Chrome v59.x). An example of ServerEndpoint working here (to use @Inject don't forget to add a beans.xml in WEB-INF folder):

    @ApplicationScoped
    @ServerEndpoint(value="/shout", encoders = {MessageEncoder.class})
    public class ShoutServerEndpoint {
    
        @Inject
        SessionHandler s;
    
        @OnOpen
        public void open(Session session, EndpointConfig config) throws Exception {
            s.initSession(session);
        }
    
        @OnMessage
        public void shout(String text, Session client) {
            System.out.println("Session: " + client + " has text: " + text);
            Message m = new Message();
            try {
                client.getBasicRemote().sendObject(m);//use the encoder to write some dummy message
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (EncodeException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            client.getAsyncRemote().sendText(text.toUpperCase());
        }
    
    
        @OnClose
        public void onClose(Session client, CloseReason reason){
            System.out.println("Session " + client + " closing for " + reason);
            s.destroySession(client);
    
        }
    
        @OnError
        public void onError(Session session, Throwable ex) { 
            System.out.println("error: " + ex.getMessage() );
        }
    }
    

    Thus the culprit seems to be that there is an older version of the code used by wildfly that was not cleaned during a redeployment of the webapp, for instance, using Eclipse, it is worth in case of strange behavior to use the Clean option on the server used (see: this Eclipse doc)

    If deploying directly with wildfly, you can clean your resources by deleting everything in (from this article):

    • /[wildfly-location]/standalone/data
    • /[wildfly-location]/standalone/deployments
    • /[wildfly-location]/standalone/tmp

    It ensures no older copies of your code remains during a future deployment.