5

I think this is more a design specific question, than direct coding issue.

I want to implement a websocket service which serves an updated dataset from a foreign http:// resource to the clients. but i want to have the data available before the first client connects, so @OnLoad notation won't do.

In HttpServlet world I would

@Override public void init() throws...

I could not figure out a suitable way for doing so just using JSR-356. I tried with custom ServerEndpointConfig.Configurator but that does not seem to give me access to method similar to init() from HttpServlet.

So my question is:

Letting the websocket Endpoint class extend HttpServlet gives me access to the init() method and I can place my initial logic there. Is that a suitable way for solving my problem, or have i missed something in JSR-356 which does the job elegantly and without importing mostly unused servlet packages?

Thank you very much!

raptaML
  • 140
  • 8

1 Answers1

3

A class annotated with @ServerEndpoint("/myEndPoint") is instantiated each time a new connection is created via @OnOpen. It is not a static class nor a singleton (e.g. not behaves as Spring @Service).

I have a similar problem to yours, I need to make a web socket the observer of a Spring web service (don't ask, I'm with you that is a bad architecture the problem). In order to make it an observer, I have to add it to the observable class, but because of the lack of an initialization for the web socket I don't have a clear spot where to add the observer, adding it in the @OnOpen method would repeatedly add it on each new connection.

The only solution I found is a workaround. Usually a web socket class has a static Set of the peers connected to it, you need something similar for your initialization. Either use a static block or a static flag in the constructor. In my case I solved with:

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

public MyWebSocket() {
    if (!observerFlag) {
        observable.addObserver(this);
        observerFlag = true;
    }
}

And to remove the observer:

@OnClose
public void onClose(Session peer) {
    peers.remove(peer);
    if (peers.isEmpty()) {
        observable.deleteObserver(this);
        observerFlag = false;
    }
}

I repeat that this is a workaround, I think that there is a more elegant solution.

Community
  • 1
  • 1
Narmer
  • 1,414
  • 7
  • 16
  • 1
    At least 2 synchronization errors: If the first few requests happen close enough together it could register more than once. Also in `onClose` a new peer may be added right after the `isEmpty()` call so there's a race condition. Easiest fix would be to wrap all access and modification of these static fields in blocks `synchronized` on some new Object used as a lock. The use of `synchronizedSet` is not sufficient because the `isEmpty` predicate is not locked subsequently; classic check-then-act race. – Chad N B May 10 '15 at 04:19