How to implement push to client using Java EE 7 WebSockets?

Piotr Kochański picture Piotr Kochański · Oct 16, 2014 · Viewed 8k times · Source

I've browsed a lot of Web Socket examples, presentation slides and they are mostly concentrated on a rather simple scenarios in which client-server communication is initiated by the client.

I am interested in another scenario, which seems to be equally practical: pure server push to client.

Example I have in mind is an application that updates stocks value on a website. Imagine there is an external system stock exchange system, which is sending a JMS message for every subscribed stock value change.

I would like to know how to translate such incoming JMS event into a server push and to it efficiently and idiomatically from a Java EE 7 point of view.

As far as I can understand the spec, I am supposed to write a web socket endpoint

@ServerEndpoint("/demo")
public class WSEndpoint {
  private static final Logger LOG = Logger.getLogger(WSEndpoint.class);

  @OnMessage
  public void onMessage(String message, Session session) {
    LOG.info("Received : " + message + ", session:" + session.getId());
  }

  @OnOpen
  public void open(Session session) {
    LOG.info("Open session:" + session.getId());         
  }

  @OnClose
  public void close(Session session, CloseReason c) {
    log.info("Close session:" + session.getId());
  }
}

Everything is easy when I am getting a message from the frontend, I can do whatever I like in the @OnMessage method. But in my example I will not get any message from the client, I'll get an event from some external system.

There are a few approaches. For instance I can create a thread in an @OnOpen method, as demonstrated in this blog. In practice that approach might show a shortcoming since for every client I would need to create a new, potentially long living thread.

One can do better using NIO channels with selectors, but this would demand some kind of "hand made" channels management. Doable, but rather cumbersome.

Another solution would be to ping some other system for updates, but again it would be kind of ugly. In addition I am also not sure if an @OnOpen method is meant to be used in that way.

Ideally an incoming JMS message would trigger a Web Socket push to the client. Any ideas how to implement something like this nicely?

Answer

Pavel Polushkin picture Pavel Polushkin · Dec 23, 2014

Probably this is not most elegant way but just to demonstrate idea. Method broadcast() will send message to all connected clients.

@ServerEndpoint("/echo")
public class ServerEndPoint {

    private static Set<Session> userSessions = Collections.newSetFromMap(new ConcurrentHashMap<Session, Boolean>());

    @OnOpen
    public void onOpen(Session userSession) {
        userSessions.add(userSession);
    }

    @OnClose
    public void onClose(Session userSession) {
        userSessions.remove(userSession);
    }

    @OnMessage
    public void onMessage(String message, Session userSession) {
        broadcast(message);
    }

    public static void broadcast(String msg) {
        for (Session session : userSessions) {
            session.getAsyncRemote().sendText(msg);
        }
    }

}