WebSocket: OnClose() is never called
Asked Answered
K

1

15

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?

Kerrill answered 25/7, 2017 at 14:54 Comment(9)
what application server are you using? and when you say closing the WebSocket what do you exactly mean? you are closing it from the client vi calling the websocket.close()? or closing the browser?Orphaorphan
I'm using the Wildfly 10.0.1 application server. tried closing the websocket calling ws.close() and closing the browser.Kerrill
Possible duplicate with: #24717903Ergo
@Duong Nguyen It's not a duplicate. Saw the post you mean before posting this one. I tested my code with Firefox, Chrom, IE, Edge and some FF/ Chrome Addons for Websockets... Additionally: my implemented '@OnError' method isn't called either. The only method that is called when closing a websocket is '@OnDestroy' in the encoder class.Kerrill
Using this quickstart and adding an OnClose annotation, the method is called when closing a tab. I used wildfly 10.1.0.Final, Java 8. What is this SessionHandler you use in your endpoint?Esophagitis
The SessionHandler is just a class providing a map that holds all registered sessions and operations on that map.Kerrill
With what you have provided above, I can create encoders, beans, inject them, and the onclose method is still called correctly, so the issue comes from somewhere else, it would be nice if you could point to a repository with your code to reproduce exactly the issue, as so far it is not feasible.Esophagitis
It's really weird! I just created a new class for the websocket endpoint and copy&pasted all stuff in there - curiously it's working now... Thanks for your attention!!Kerrill
Actually that makes me think about one thing I had issue with Wildfly recently because when restarting the server, it would not clean previously old resources (I am using Servers with Eclipse), I had to go to the server tabs and use the Clean option on my Wildfly. I'd suspect something like this is happening because your code is working well on my instance. (By the way, when responding please use @username so that I see your response in my inbox)Esophagitis
E
4

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.

Esophagitis answered 1/8, 2017 at 9:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.