Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Play2 Are Actors required for WebSockets implementation?

I am trying to implement a WebSockets based solution with Play2 and Java. Currently I'm not using Actor Model for concurrency management. Instead, it's just handling the WebSockets using callbacks.

The problem is the server is unable to 'broadcast' the messages across to multiple clients. For example, I need all the active browser sessions to receive a simple notification after an action is triggered through a client. Instead, I'm just able to simulate this for a single client session. In effect, it just acts as a regular Ajax request/response scenario.

My guess is the WebSockets solution with Play2 & Java with simple callbacks isn't able to broadcast the messages to all of the connected client browsers.

Callback server code

public static WebSocket<String> heartUpdate() {
        return new WebSocket<String>() {
            public void onReady(final WebSocket.In<String> in,
                    WebSocket.Out<String> out) {
                in.onMessage(new Callback<String>() {
                    @Override
                    public void invoke(String event) throws Throwable {
                        Product product = Product.find.byId(Long.decode(event));
                        // Other business stuff
                        out.write(entity.id + " " + decimalValue);
                    }
                });
                in.onClose(new Callback0() {
                    @Override
                    public void invoke() throws Throwable {

                    }
                });
                out.write(DELIM_USERID_REP);
            }
        };
    }

Routes entry

GET /repsocket   controllers.Application.heartUpdate

Client side

<script>
    // Onto the WebSockets now
    var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket;
    var socket = new WS("@routes.Application.heartUpdate.webSocketURL(request)");

    var receiveEvent = function(event) {
        alert(event.data);
        // Notification badge visible with a common text
        $('#notify').text(event.data);
        $('#notify').addClass('visible');
    }
        $('#heart').click(function(e) {
            e.preventDefault();
            socket.send(@product.id);
        });
    socket.onmessage = receiveEvent;
    </script>

In the above case, the action triggering client is the one that successfully receives the message from the server, while any other browser sessions do not.

I failed to find any documentation aligned with Play2 with Java & WebSockets that suggests a mandatory use of Actors to maintain a consistent communication with all connected active clients.

Question: Should WebSockets in Play2 with Java be implemented with Actor Model to broadcast the message to all active client sessions?

Edit: The screenshot below reflects the 2 states of browsers to which I have logged in to using 2 valid user credentials.

Ideally, WebSockets' response from the server should create the same notification badges on both the browsers.

enter image description here

like image 686
StoopidDonut Avatar asked Oct 05 '15 16:10

StoopidDonut


1 Answers

Looking at the code you posted, it seems the problem is that you are not saving a reference to each connection, so you can later broadcast messages. Something like this:

// Just an example: does not deal with concurrency problems
private static final Set<WebSocket.Out<String>> SOCKETS = new HashSet<>();

public static WebSocket<String> heartUpdate() {
    return new WebSocket<String>() {
        public void onReady(final WebSocket.In<String> in,
                    final WebSocket.Out<String> out) {
            SOCKETS.add(out);
            in.onMessage(new Callback<String>() {
                @Override
                public void invoke(String event) throws Throwable {
                    for(WebSocket.Out<String> s: SOCKETS) {
                        s.write("SOME MESSAGE");    
                    }
                }
            });
            in.onClose(new Callback0() {
                @Override
                public void invoke() throws Throwable {
                    SOCKETS.remove(out);
                }
            });
        }
    };
}

You can also use actors to model this problem: having an actor responsible to keep track of the connections and to deal with new sockets connection/close.

If you want to use Akka there are some activator templates to get you started.

like image 73
Salem Avatar answered Nov 19 '22 03:11

Salem