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:
Post a Comment