Broadcasting messages to other users

In this tutorial we will create an application where any user can send a broadcast message to all other active users. We will start from a project where push has been enabled (see Enabling server push for details).

For simplicity, we will use a static Broadcaster which is shared between all users and all sessions. Each UI will register itself to this broadcast when initialized and unregister when detached. The broadcaster will take care of sending events to the registered UI:s as needed. In a real world scenario you probably want to use something else than a shared static class (e.g. JMS or some other messaging system) but the same ideas apply.

So, let’s start from a simple Broadcaster class and a listener interface. We need the possibility to register and unregister listeners and to broadcast a message to the listeners so we end up with the following class:

public class Broadcaster {

  private static final List<BroadcastListener> listeners = new CopyOnWriteArrayList<BroadcastListener>();

  public static void register(BroadcastListener listener) {
    listeners.add(listener);
  }

  public static void unregister(BroadcastListener listener) {
    listeners.remove(listener);
  }

  public static void broadcast(final String message) {
    for (BroadcastListener listener : listeners) {
      listener.receiveBroadcast(message);
    }
  }

  public interface BroadcastListener {
    public void receiveBroadcast(String message);
  }
}

As Broadcast will be used by many threads simultaneously, we need to ensure that it is thread-safe. We will do it here by using the thread-safe CopyOnWriteArrayList class for keeping track of the listeners.

Now that we have the Broadcaster implemented we can use it in our UI for instance as follows:

@Push
public class BroadcasterUI extends UI implements BroadcastListener {

  @Override
  protected void init(VaadinRequest request) {
    [...]
    // Register broadcast listener
    Broadcaster.register(this);
  }

  @Override
  public void detach() {
    Broadcaster.unregister(this);
    super.detach();
  }

  @Override
  public void receiveBroadcast(final String message) {
    access(new Runnable() {
      @Override
      public void run() {
        Notification n = new Notification("Message received",
            message, Type.TRAY_NOTIFICATION);
        n.show(getPage());
      }
    });
  }

We register the UI in the init method and unregister it in the detach method to avoid receiving messages for UIs no longer in use (and ensuring that the detached UI can be garbage collected).

When we receive a broadcast message we need to use the access method as this call comes from a thread where the UI is not locked. access(Runnable) will take care of locking the UI for us so we can update it. In the wrapped run method we can do whatever we like with the received message, for instance show it as a tray notification as done here.

To send a broadcast message we can create a simple user interface in our UI init method:

protected void init(VaadinRequest request) {
  final VerticalLayout layout = new VerticalLayout();
  layout.setMargin(true);
  setContent(layout);

  final TextArea message = new TextArea("",
      "The system is going down for maintenance in 10 minutes");
  layout.addComponent(message);

  final Button button = new Button("Broadcast");
  layout.addComponent(button);
  button.addClickListener(new Button.ClickListener() {
    @Override
    public void buttonClick(ClickEvent event) {
      Broadcaster.broadcast(message.getValue());
    }
  });

  // Register broadcast listener
  Broadcaster.register(this);
}

Now if you deploy the application and open it in a couple of browser tabs or separate browsers you are able to send messages between the instances.