March 2014

(For more resources related to this topic, see here.)

Traditionally, web applications have been developed using the request/response model followed by the HTTP protocol. In this model, the request is always initiated by the client and then the server returns a response back to the client.

There has never been any way for the server to send data to the client independently (without having to wait for a request from the browser) until now. The WebSocket protocol allows full-duplex, two-way communication between the client (browser) and the server.

Java EE 7 introduces the Java API for WebSocket, which allows us to develop WebSocket endpoints in Java. The Java API for WebSocket is a brand-new technology in the Java EE Standard.

A socket is a two-way pipe that stays alive longer than a single request. Applied to an HTML5-compliant browser, this would allow for continuous communication to or from a web server without the need to load a new page (similar to AJAX).

Developing a WebSocket server endpoint

A WebSocket server endpoint is a Java class deployed to the application server that handles WebSocket requests.

There are two ways in which we can implement a WebSocket server endpoint via the Java API for WebSocket: either by developing an endpoint programmatically, in which case we need to extend the javax.websocket.Endpoint class, or by decorating Plain Old Java Objects (POJOs) with WebSocket-specific annotations. The two approaches are very similar; therefore, we will be discussing only the annotation approach in detail and briefly explaining the second approach, that is, developing WebSocket server endpoints programmatically, later in this section.

In this article, we will develop a simple web-based chat application, taking full advantage of the Java API for WebSocket.

Developing an annotated WebSocket server endpoint

The following Java class code illustrates how to develop a WebSocket server endpoint by annotating a Java class:

package net.ensode.glassfishbook.websocketchat.serverendpoint; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/websocketchat") public class WebSocketChatEndpoint { private static final Logger LOG = Logger.getLogger(WebSocketChatEndpoint
.class.getName()); @OnOpen public void connectionOpened() { LOG.log(Level.INFO, "connection opened"); } @OnMessage public synchronized void processMessage(Session session, String message) { LOG.log(Level.INFO, "received message: {0}", message); try { for (Session sess : session.getOpenSessions()) { if (sess.isOpen()) { sess.getBasicRemote().sendText(message); } } } catch (IOException ioe) { LOG.log(Level.SEVERE, ioe.getMessage()); } } @OnClose public void connectionClosed() { LOG.log(Level.INFO, "connection closed"); } }

The class-level @ServerEndpoint annotation indicates that the class is a WebSocket server endpoint. The URI (Uniform Resource Identifier) of the server endpoint is the value specified within the parentheses following the annotation (which is "/websocketchat" in this example)—WebSocket clients will use this URI to communicate with our endpoint.

The @OnOpen annotation is used to decorate a method that needs to be executed whenever a WebSocket connection is opened by any of the clients. In our example, we are simply sending some output to the server log, but of course, any valid server-side Java code can be placed here.

Any method annotated with the @OnMessage annotation will be invoked whenever our server endpoint receives a message from a client. Since we are developing a chat application, our code simply broadcasts the message it receives to all connected clients.

In our example, the processMessage() method is annotated with @OnMessage, and takes two parameters: an instance of a class implementing the javax.websocket.Session interface and a String parameter containing the message that was received. Since we are developing a chat application, our WebSocket server endpoint simply broadcasts the received message to all connected clients.

The getOpenSessions() method of the Session interface returns a set of session objects representing all open sessions. We iterate through this set to broadcast the received message to all connected clients by invoking the getBasicRemote() method on each session instance and then invoking the sendText() method on the resulting RemoteEndpoint.Basic implementation returned by calling the previous method.

The getOpenSessions() method on the Session interface returns all the open sessions at the time it was invoked. It is possible for one or more of the sessions to have closed after the method was invoked; therefore, it is recommended to invoke the isOpen() method on a Session implementation before attempting to return data back to the client. An exception may be thrown if we attempt to access a closed session.

Finally, we need to decorate a method with the @OnClose annotation in case we need to handle the event when a client disconnects from the server endpoint. In our example, we simply log a message into the server log.

There is one additional annotation that we didn't use in our example—the @OnError annotation; it is used to decorate a method that needs to be invoked in case there's an error while sending or receiving data to or from the client.

As we can see, developing an annotated WebSocket server endpoint is straightforward. We simply need to add a few annotations, and the application server will invoke our annotated methods as necessary.

If we wish to develop a WebSocket server endpoint programmatically, we need to write a Java class that extends javax.websocket.Endpoint. This class has the onOpen(), onClose(), and onError() methods that are called at appropriate times during the endpoint's life cycle. There is no method equivalent to the @OnMessage annotation to handle incoming messages from clients. The addMessageHandler() method needs to be invoked in the session, passing an instance of a class implementing the javax.websocket.MessageHandler interface (or one of its subinterfaces) as its sole parameter.

In general, it is easier and more straightforward to develop annotated WebSocket endpoints compared to their programmatic counterparts. Therefore, we recommend that you use the annotated approach whenever possible.

Developing WebSocket clients

Most WebSocket clients are implemented as HTML5 web pages, taking advantage of the JavaScript WebSocket API. As such, they must be accessed using an HTML5-compliant web browser (most modern web browsers are HTML5 compliant).

The Java API for WebSocket provides a client API that allows us to develop WebSocket clients as standalone Java applications. We will cover how to do this in a later section, Developing WebSocket clients in Java.

Developing JavaScript client-side WebSocket code

In this section, we will cover how to develop client-side JavaScript code to interact with the WebSocket endpoint we developed in the previous section.

The client page for our WebSocket example is implemented as a JSF page using HTML5-friendly markup.

Our client page consists of a text area where we can see what the users of our application are saying (it is, after all, a chat application) and an input text we can use to send a message to the other users, as shown in the following screenshot:

The markup for our client page looks like the following:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"xmlns:jsf="http://xmlns.jcp.org/jsf"> <head> <title>WebSocket Chat</title> <meta name="viewport" content="width=device-width"/> <script type="text/javascript"> var websocket; function init() { websocket = new WebSocket('ws://localhost:8080/websocketchat
websocket.onopen = function(event) { websocketOpen(event) }; websocket.onmessage = function(event) { websocketMessage(event) }; websocket.onerror = function(event) { websocketError(event) }; } function websocketOpen(event) { console.log("webSocketOpen invoked"); } function websocketMessage(event) { console.log("websocketMessage invoked"); document.getElementById('chatwindow').value += '\r' + event.data; } function websocketError(event) { console.log("websocketError invoked"); } function sendMessage() { var userName = document.getElementById('userName').value; var msg = document.getElementById('chatinput').value; websocket.send(userName + ": " + msg); } function closeConnection(){ websocket.close(); } window.addEventListener("load", init); </script> </head> <body> <form jsf:prependId="false"> <input type="hidden" id="userName" value="#{user.userName}"/> <table border="0"> <tbody> <tr> <td> <label for="chatwindow"> Chat Window </label> </td> <td> <textArea id="chatwindow" rows="10"/> </td> </tr> <tr> <td> <label for="chatinput"> Type Something Here </label> </td> <td> <input type="text" id="chatinput"/> <input id="sendBtn" type="button" value="Send"
</td> </tr> <tr> <td></td> <td> <input type="button" id="exitBtn" value="Exit"
</td> </tr> </tbody> </table> </form> </body> </html>

The last line of our JavaScript code (window.addEventListener("load", init);) sets our JavaScript init() function to be executed as soon as the page loads.

Within the init() method, we initialize a new JavaScript websocket object, passing the URI of our server endpoint as a parameter. This tells our JavaScript code the location of our server endpoint.

The JavaScript websocket object has a number of function types used to handle different events, such as opening the connection, receiving a message, and handling errors. We need to set these types to our own JavaScript functions so that we can handle these events, which is what we do in our init() method right after invoking the constructor for the JavaScript websocket object. In our example, the functions we assigned to the websocket object simply delegate their functionality to standalone JavaScript functions.

Our websocketOpen() function is called every time the WebSocket connection is opened. In our example, we simply send a message to the browser's JavaScript console.

The webSocketMessage() function is invoked every time the browser receives a WebSocket message from our WebSocket endpoint. In our example, we updated the contents of the text area whose id is chatWindow and the contents of the message.

The websocketError() function is called every time there is a WebSocket-related error. In our example, we simply send a message to the browser's JavaScript console.

The JavaScript sendMessage() function sends a message to the WebSocket server endpoint, containing both the username and the contents of the text input whose id is chatinput. This function is called when the user clicks on the button whose id is sendBtn.

The closeConnection() JavaScript function closes the connection to our WebSocket server endpoint. This function is called when the user clicks on the button whose id is exitBtn.

As we can see from this example, writing client-side JavaScript code to interact with WebSocket endpoints is fairly straightforward.

Developing WebSocket clients in Java

Although developing web-based WebSocket clients is currently the most common way of developing WebSocket clients, the Java API for WebSocket provides a client API that we can use to develop WebSocket clients in Java.

In this section, we will be developing a simple WebSocket client using the client API of the Java API for WebSocket. The final product looks as shown in the following screenshot:

However, we won't be covering the GUI code in this section (developed using the Swing framework), since it is not relevant to this discussion. The complete code for the example, including the GUI code, can be downloaded from the Packt Publishing website at www.packtpub.com.

Just as with WebSocket server endpoints, Java WebSocket clients can be developed either programmatically or using annotations. Once again, we will cover only the annotation approach: developing a programmatic client is very similar to the way programmatic server endpoints are developed, that is, programmatic clients must extend javax.websocket.Endpoint and override the appropriate methods.

Without further ado, the following is the code for our Java WebSocket client:

package net.ensode.websocketjavaclient; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import javax.websocket.ClientEndpoint; import javax.websocket.CloseReason; import javax.websocket.ContainerProvider; import javax.websocket.DeploymentException; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.WebSocketContainer; @ClientEndpoint public class WebSocketClient { private String userName; private Session session; private final WebSocketJavaClientFrame webSocketJavaClientFrame; public WebSocketClient(WebSocketJavaClientFrame webSocketJavaClientFrame) { this.webSocketJavaClientFrame = webSocketJavaClientFrame; try { WebSocketContainer webSocketContainer =
webSocketContainer.connectToServer(this, new URI
} catch (DeploymentException | IOException | URISyntaxException ex) { ex.printStackTrace(); } } @OnOpen public void onOpen(Session session) { System.out.println("onOpen() invoked"); this.session = session; } @OnClose public void onClose(CloseReason closeReason) { System.out.println("Connection closed, reason: "+
closeReason.getReasonPhrase()); } @OnError public void onError(Throwable throwable) { System.out.println("onError() invoked"); throwable.printStackTrace(); } @OnMessage public void onMessage(String message, Session session) { System.out.println("onMessage() invoked"); webSocketJavaClientFrame.getChatWindowTextArea().setText(webSocket
JavaClientFrame.getChatWindowTextArea().getText() + "\n" + message); } public void sendMessage(String message) { try { System.out.println("sendMessage() invoked, message = " + message); session.getBasicRemote().sendText(userName + ": " + message); } catch (IOException ex) { ex.printStackTrace(); } } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }

The class-level @ClientEndPoint annotation denotes that our class is a WebSocket client—all Java WebSocket clients must be annotated with this annotation.

The code to establish a connection to the WebSocket server endpoint is in our class constructor. First, we need to invoke ContainerProvider.getWebSocketContainer() to obtain an instance of javax.websocket.WebSocketContainer. We then establish a connection by invoking the connectToServer() method on our WebSocketContainer instance; then we pass a class annotated with @ClientEndpoint as the first parameter (we use this in our example since the connection code is within our WebSocket Java client code); and then we pass a URI object containing the WebSocket server endpoint URI as the second parameter.

After the connection is established, we are ready to respond to WebSocket events. Alert readers may have noticed that the exact same annotations we used to develop our server endpoint are used again in our client code.

Any method annotated with the @OnOpen annotation will be invoked automatically when the connection to the WebSocket server endpoint is established. The method must return void and can have an optional parameter of the type javax.websocket.Session. In our example, we send some output to the console and initialize a class variable with the Session instance, which we received as a parameter.

Methods annotated with the @OnClose annotation are invoked whenever the WebSocket session is closed. The annotated method can have optional parameters of the types javax.websocket.Session and CloseReason. In our example, we chose to use only the CloseReason optional parameter since its class has a handy getReasonPhrase() method that provides a short explanation of why the session was closed.

The @OnError annotation is used to decorate any methods that are called when an error occurs. Methods annotated with @OnError must have a parameter of type java.lang.Throwable (the parent class of java.lang.Exception), and can have an optional parameter of type Session. In our example, we simply send the stack trace of the Throwable parameter to stderr.

Methods annotated with @OnMessage are invoked every time an incoming WebSocket message is received. The @OnMessage methods can have different parameters depending on the type of message received and how we wish to handle it. In our example, we used the most common case: receiving a text message. In this particular case, we need a String parameter that will hold the contents of the message, and an optional Session parameter.

Refer to the JavaDoc documentation for @OnMessage, available at http://docs.oracle.com/javaee/7/api/javax/websocket/OnMessage.html for information on how to handle other types of messages.

In our example, we simply update the Chat Window text area, appending the received message to its contents.

To send a WebSocket message, we invoke the getBasicRemote() method on our Session instance, then invoke the sendText() method on the resulting RemoteEndpoint.Basic implementation returned by this call (if this looks familiar, it is because we did the exact same thing in the WebSocket server endpoint code). In our example, we do this in the sendMessage() method.

Additional information about the Java API for WebSocket

In this article, we covered the bulk of the functionality provided by the Java API for WebSocket. For additional information, refer to the user guide for Tyrus, the Java API for WebSocket reference implementation, at https://tyrus.java.net/documentation/1.3.1/user-guide.html.


In this article, we covered the Java API for WebSocket, which is a new Java EE API to develop WebSocket server endpoints and clients.

We first saw how to develop WebSocket server endpoints by taking advantage of the Java API for WebSockets. We focused on developing annotation-based WebSocket endpoints.

Then, we covered how to develop web-based WebSocket clients using JavaScript and the JavaScript built-in WebSocket API.

Finally, we explained how to develop WebSocket client applications in Java via the @ClientEndpoint annotation.

Read the book to know more!

Resources for Article:

Further resources on this subject:

You've been reading and excerpt of:

Java EE 7 with GlassFish 4 Application Server

Explore Title
comments powered by Disqus