Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 502
Piotr Kochański Avatar asked Oct 16 '14 16:10

Piotr Kochański


3 Answers

Store the active sessionList in another class SessionManager.

List<Session> socketSessions = new ArrayList<>();

Add incoming session in @OnOpen to list. Remove session from list in @OnClose

@OnClose
public void close(Session session) {
  sessionManager.removeSession(session);
}

To send a message to everyone,

public void broadcast(String message){
    for(Session session: sessionList){
        session.getBasicRemote().sendText(message);
    }
}

You can use the sessionManager.broadcast() method wherever the event triggers.

Here is a complete example of how websocket can be used to make push with HTML5 websocket API. https://metamug.com/article/java-push-notification-with-websocket.php

like image 177
Sorter Avatar answered Oct 16 '22 07:10

Sorter


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);
        }
    }

}
like image 4
Pavel Polushkin Avatar answered Oct 16 '22 07:10

Pavel Polushkin


I do it this way (no client request is needed):

@ServerEndpoint("/hello")
public class HelloWebSocket {

   @OnOpen
   public void greetTheClient(Session session){
       try {
        session.getBasicRemote().sendText("Hello stranger");

    } catch (IOException ioe) {
        System.out.println(ioe.getMessage());
    }
   }
}
like image 1
Yakov Fain Avatar answered Oct 16 '22 07:10

Yakov Fain