This post is part of my short series about WebSockets with Java EE.

  1. Technical aspects of the WebSocket protocol
  2. WebSockets in a Java EE 7 application
  3. JSF 2.3 and WebSockets

As I mentioned before, there are personal reasons which reduce my current time. Thus I divided part 2 into smaller sections. This is the second portion of the second part of my three part series.

So far, we created a very simple chat application using Java EE 7 technologies. In the first version of the simple chat, any message had been broad-casted by a tyrus session. Today I’ll show how to handle the broadcast by yourself.

An instance of our server endpoint is created every time the URL is opened within your browser. If you stay on that page, you may use the existing endpoint. If you like to verify that behavior, simply add a default constructor and report the construction.

  public SimpleChat() {
    System.out.println("ctor SimpleChat");
  }

Using Glassfish / Payara, the output is logged. You may call the logger instead. Using NetBeans you may observe this log directly in the output window of the IDE.

If an instance of the endpoint is created for every call to the URL, then we need to register all peer sessions who want to participate the chat. The simplest way to perform this task, is to use a static set of peer sessions.

  private static final Set<Session> peers = 
        Collections.synchronizedSet(new HashSet<Session>());

The server endpoint supports the annotations @OnOpen and @OnClose. The methods which are annotated with it, are called when the websocket is opened and closed and take a session as argument. Thus, these methods are the perfect candidates to register and unregister the peer sessions.

  @OnOpen
  public void onOpen(Session peer) {
    peers.add(peer);
  }

  @OnClose
  public void onClose(Session peer) {
    peers.remove(peer);
  }

Once we have registered all peers, we imply need to iterate them during @OnMessage and send the message to every peer. To perform that, we use getBasicRemote() to get a reference to the remote endpoint we want to send data synchronously. There is a second method getAsyncRemote() which can be used to send data asynchronously.

  @OnMessage
  public void onMessage(String message) {
    for (Session peer : peers) {
      try {
        peer.getBasicRemote().sendObject(message);
      } catch (IOException | EncodeException ex) {
        // log, handle, or ingnore exceptions here
      }
    }
  }

You may have recognized the different signature of the onMessage() method.  We don’t need the own session, thus we can use the overloaded method signature without it.

For your convenience, this is the whole endpoint class (without imports).

@ServerEndpoint("/simplechat")
public class SimpleChat {

  private static final Set<Session> peers = 
        Collections.synchronizedSet(new HashSet<Session>());

  @OnMessage
  public void onMessage(String message) {
    for (Session peer : peers) {
      try {
        peer.getBasicRemote().sendObject(message);
      } catch (IOException | EncodeException ex) {
        // log, handle, or ingnore exceptions here
      }
    }
  }

  @OnOpen
  public void onOpen(Session peer) {
    peers.add(peer);
  }

  @OnClose
  public void onClose(Session peer) {
    peers.remove(peer);
  }
}

To enhance the chat, we want to add the name of the sender to each message. And the user may choose a different chat room.

Time is over for today. I’ll write about this in the next (last) portion of part 2.

Stay tuned!