What's the best practice to broadcast a server-initiated WebSocket message in JSR-356?
To clarify, I know how a reply or even a broadcast works when using the @OnMessage
annotation, but I want to send an event from the server without a receiving a message from a client first. In other words, I suppose I need the reference to the MessageServerEndpoint
instance in the code below.
I've seen the following solution, but it uses a static method and is not very elegant.
@ServerEndpoint(value = "/echo")
public class MessageServerEndpoint {
private static Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());
@OnOpen
public void onOpen(Session session) {
sessions.add(session);
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
sessions.remove(session);
}
// Static method - I don't like this at all
public static void broadcast(String message) {
for (Session session : sessions) {
if (session.isOpen()) {
session.getBasicRemote().sendText(message);
}
}
}
}
public class OtherClass {
void sendEvent() {
MessageServerEndpoint.broadcast("test");
// How do I get a reference to the MessageServerEndpoint instance here instead?
}
}
I solved the problem by extending ServerEndpointConfig.Configurator
and overriding getEndpointInstance()
where I can save the endpoint instances:
public class MyEndpointConfigurator extends ServerEndpointConfig.Configurator
private Set<MyEndpoint> endpoints = Collections.synchronizedSet(new HashSet<>());
@Override
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
try {
T endpoint = endpointClass.newInstance();
MyEndpoint myEndpoint = (MyEndpoint) endpoint;
myEndpoint.setConfigurator(this);
endpoints.add(myEndpoint);
return endpoint;
} catch (IllegalAccessException e) {
throw new InstantiationException(e.getMessage());
}
}
// Call this from MyEndpoint.onClose()
public void removeInstance(MyEndpoint endpoint) {
endpoints.remove(endpoint);
}
}
Since I have the reference to MyEndpointConfigurator
, I also have the references to all the endpoints.
It still feels like a hack, but seems to do the trick.