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?

Second2None
  • 462
  • 2
  • 8
  • 22
  • 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? – Itherael Jul 30 '17 at 00:25
  • I'm using the Wildfly 10.0.1 application server. tried closing the websocket calling ws.close() and closing the browser. – Second2None Jul 30 '17 at 06:21
  • Possible duplicate with: https://stackoverflow.com/questions/24717403/java-websockets-onclose-method-is-not-called-when-closing-websocket-connection – Duong Nguyen Jul 31 '17 at 09:26
  • @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. – Second2None Jul 31 '17 at 09:34
  • Using this [quickstart](https://github.com/darranl/wildfly-quickstart/tree/master/helloworld-websocket) 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? – Adonis Jul 31 '17 at 10:45
  • The `SessionHandler` is just a class providing a map that holds all registered sessions and operations on that map. – Second2None Jul 31 '17 at 11:02
  • 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. – Adonis Jul 31 '17 at 14:36
  • 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!! – Second2None Aug 01 '17 at 08:14
  • 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) – Adonis Aug 01 '17 at 08:33

1 Answers1

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.

Adonis
  • 4,670
  • 3
  • 37
  • 57