MessageBroadcaster memory leak?

I’m toying around in V14 with the MessageBroadcaster example from https://vaadin.com/docs/v8/framework/advanced/advanced-push.html but apparently a registration like

@Override
protected void onAttach(AttachEvent attachEvent) {
	UI ui = attachEvent.getUI();
	broadcasterRegistration = MessageBroadcaster.register(message -> {
		ui.access(() -> {
			MessageDialog.of(message).open();
		});
	});
}

with an unregister in onDetach still leave some sort of reference since MAT reveals that stuff keep gathering in the static list. How should this be avoided in the app? Having WeakReferences in the MessageBroadcaster-list? Calling the MessageDialog differently? Pointers appreciated…

18546877.png

Having WeakReferences in the MessageBroadcaster-list?

Yes, that is at least needed, I have one example here using WeakHashMap for the listeners. At least in that Vaadin 8 example the problem is obvious, as there is public class PushAroundUI extends UI implements Broadcaster.BroadcastListener and Broadcaster.register(this);, where this is of course UI implementing BroadcastListener, thus UI’s build up in the LinkedList.

https://github.com/TatuLund/cdi-demo/blob/master/src/main/java/org/vaadin/cdidemo/beacon/BeaconImpl.java#L34

So better version of the Broadcaster class could be

public class Broadcaster implements Serializable {
    static ExecutorService executorService =
        Executors.newSingleThreadExecutor();

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

    private static WeakHashMap<BroadcastListener, Object> listeners =
        new WeakHashMap<BroadcastListener, Object>();

    public static synchronized void register(
            BroadcastListener listener) {
        listeners.put(listener, null);
    }

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

    public static synchronized void broadcast(
            final String message) {
        for (final BroadcastListener listener: listeners.keySet())
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    listener.receiveBroadcast(message);
                }
            });
    }
}