November 19, 2017

Java EE 7 Simple WebSocket Example

One of the new technologies in Java EE 7 is WebSockets. A killer use case for web sockets is when a client need push notifications from the server. Previously such a client needed to constantly ask the server if there were any updates, which was ineffective and consumed a lot of server cpu. So lets implement a simple chat application that uses web sockets.

First the server. The whole layout or architecture behind web sockets reminds us very much as with jax-rs annotations. We start with a @ServerEndpoint class annotation, just like rest @Path.

Then you have only one mandatory method @OnMessage public void onMessage(String message, Session session). The method input parameters can be different, please read the javadoc.

Then you have 3 optional methods documented here. @OnOpen, @OnClose and @OnError

I will implement all 4 methods. I will also remember all sessions and will use CDI annotation @ApplicationScoped for that, but for it to work, we need to put the data in a separate POJO and annotate that also with @ApplicationScoped and let CDI handle the lifecycle via @Inject.


package se.magnuskkarlsson.example.javaee7.chat.boundary;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/chat")
@ApplicationScoped
public class ChatWebSocketServer {

    private final Logger log = Logger.getLogger(ChatWebSocketServer.class.getName());
    @Inject ChatSessions sessions;

    @OnOpen
    public void open(Session session) {
        sessions.getSessions().put(session.getId(), session);
        log.info("OPEN session " + session.getId());
        log.info("size " + sessions.getSessions().size());
    }

    @OnClose
    public void close(Session session) {
        sessions.getSessions().remove(session.getId());
        log.info("CLOSE session " + session.getId());
        log.info("size " + sessions.getSessions().size());
    }

    @OnError
    public void onError(Throwable error) {
        log.log(Level.SEVERE, "onError", error);
    }

    @OnMessage
    public void handleMessage(final String message, Session session) {
        log.info("size " + sessions.getSessions().size());
        sessions.getSessions().forEach((key, value) -> {
            try {
                value.getBasicRemote().sendText(message);
                log.info("SEND session " + value.getId() + ", message " + message);
            } catch (Exception e) {
                log.log(Level.SEVERE, "Failed to send text", e);
            }
        });
    }

}

package se.magnuskkarlsson.example.javaee7.chat.boundary;

import java.util.HashMap;
import java.util.Map;
import javax.enterprise.context.ApplicationScoped;
import javax.websocket.Session;

@ApplicationScoped
public class ChatSessions {
    
    private final Map<String, Session> sessions = new HashMap<>();

    public Map<String, Session> getSessions() {
        return sessions;
    }    
    
}

Now to the client. It is made up of a web page and javascript.


<!DOCTYPE html>
<html>
    <head>
        <title>Web Socket Demo</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="websocket.js"></script>
    </head>
    <body>
        <div>
            <form id="chatForm">
                Message: <input type="text" id="message" />
                <input type="button" value="Send" onclick="formSubmit()" />
            </form>
        </div>
        <div id="messages">
        </div>
    </body>
</html>


var socket = new WebSocket("ws://localhost:8080/example-javaee7/chat");
socket.onmessage = onMessage;

function onMessage(event) {
    var message = event.data;
    appendHtml("<--- Server : " + message);
}

function formSubmit() {
    var form = document.getElementById("chatForm");
    var message = form.elements["message"].value;
    socket.send(message);
    appendHtml("---> You : " + message);
}

function appendHtml(message) {
    var div = document.getElementById("messages")
    div.innerHTML += "<p>" + message + "</p>";
}

No comments: