I am going to divide this post into following topics for ease of understanding to the readers:
- What is a WebSocket?
- JavaEE 7 Support for creating WebSocket end points
- Javascript support to create WebSocket client end point
What is a WebSocket?
It is a feature being introduced as part of the HTML5 specification. It introduces a way to create a full-duplex communication channel between client and server over TCP. This is in addition to the conventional Request-Response communication via the HTTP between client and server. To quote more better definitions from different sources:
From Introducing WebSockets on HTML5Rocks:
The WebSocket specification defines an API establishing “socket” connections between a web browser and a server. In plain words: There is an persistent connection between the client and the server and both parties can start sending data at any time.
From an article in MDN:
It is a technology that makes it possible to open an interactive communication session between the user’s browser and a server. Using a WebSocket connection, Web applications can perform real-time communication instead of having to poll for changes back and forth.
From Java EE 7 tutorial:
WebSocket is an application protocol that provides full-duplex communications between two peers over the TCP protocol.
In the traditional request-response model used in HTTP, the client requests resources and the server provides responses. The exchange is always initiated by the client; the server cannot send any data without the client requesting it first. This model worked well for the World Wide Web when clients made occasional requests for documents that changed infrequently, but the limitations of this approach are increasingly relevant as content changes quickly and users expect a more interactive experience on the web. The WebSocket protocol addresses these limitations by providing a full-duplex communication channel between the client and the server. Combined with other client technologies, such as JavaScript and HTML5, WebSocket enables web applications to deliver a richer user experience.
From WebSocket.org:
The HTML5 WebSockets specification defines an API that enables web pages to use the WebSockets protocol for two-way communication with a remote host. It introduces the WebSocket interface and defines a full-duplex communication channel that operates through a single socket over the Web. HTML5 WebSockets provide an enormous reduction in unnecessary network traffic and latency compared to the unscalable polling and long-polling solutions that were used to simulate a full-duplex connection by maintaining two connections.
The W3C committee has established an api for WebSocket which has to be supported to make use of the WebSocket feature. The API can be found here. So we make use of this API while using the WebSocket API from the Javascript.
To make use of this feature completely the server end point should support listening to WebSocket connections and should support sending and receiving the messages via the WebSocket communication channel. Prior to JavaEE 7 there was no support for this in the core Java EE SDK, but the JavaEE 7 and later comes with the support for creating WebSockets server end points.
JavaEE 7 Support for Creating WebSocket End Points
In each communication channel there will be two end points namely:
- Client end point
- Server end point.
The Client and Server end points can be created in 2 ways namely:
- Using Annotations
- Extending the class
javax.websocket.Endpoint
.
In this article I will focus on using Annotations to create Server End points.
We annotate the class which we want to serve as a server end point with @javax.websocket.server.ServerEndpoint
and pass in the URI to locate the end point. There are various parameters which the annotation takes in and one of them is the value
which holds the URI to locate the end point. Once we have an end point, this end point can receive data of formats namely String, Binary which are generated by the application. In this example I will look at how to receive String messages and then send back string messages to the client end point.
The method which receives the messages is to be annotated as @javax.websocket.OnMessage
. And in our example it will accept only text messages, as a result the method would accept a String parameter along with another parameter of type javax.websocket.Session
. So before I proceed further lets look at the classes involved in creating annotated server end point:
- ServerEndpoint: This class level annotation declares that the class it decorates is a web socket endpoint that will be deployed and made available in the URI-space of a web socket server. The annotation allows the developer to define the URL (or URI template) which this endpoint will be published, and other important properties of the endpoint to the websocket runtime, such as the encoders it uses to send messages.
- OnMessage: This method level annotation can be used to make a Java method receive incoming web socket messages. Each websocket endpoint may only have one message handling method for each of the native websocket message formats: text, binary and pong.
- Session: A Web Socket session represents a conversation between two web socket endpoints. As soon as the websocket handshake completes successfully, the web socket implementation provides the endpoint an open websocket session.
Lets look at the code for creating the Server End Point:
import java.io.IOException; import javax.websocket.OnMessage; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; /** * Creating a Server End point using Annotation. * This server end point is available at "/<app_path>/webSocket". */ @ServerEndpoint("/webSocket") public class WebSocketDemoEndPoint { /** * counter to track count of messages received. * This instance variable is specific to one client. Which means that * for each client connection one instance of WebSocketDemoEndPoint is * created. */ int counter = 1; /** * Method which receives the messages from the client and sends messages * back to the server. */ @OnMessage public void receiveMessage(String message, Session session) throws IOException{ System.out.println("From websocket: " + message); //Sending the messages/data to client synchronously. session.getBasicRemote().sendText("Sending message: "+ counter++ +" from server"); } }
Javascript Support to Create WebSocket Client End Point
Now that we have created the Server End Point, we will make use of the Javascript WebSocket API to create a client end point and connect to the server end point and exchange messages.
The JSP or the UI which I am creating in this example will have 3 buttons to create a websocket connection, to close a websocket connection and to send data on the websocket connection. And as and when the operations like Opening a WebSocket connection, closing a websocket connection and sending messages are performed we display the status of the operation on the UI so that the user is aware of what’s happening when they perform the operations. The JSP code is given below:
<!-- File Name: webSocketDemo.jsp --> <%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>WebSocket Demo</title> <script src="webSocket.js"></script> </head> <body> <input type="button" id="startWsConn" value="Connect via websocket" /> <input type="button" id="closeWsConn" value="Close the websocket" /> <div id="wsStatus"></div><br/> <input type="button" id="sendWsData" value="Send via Websocket" /> <div id="wsSendStatus"></div><br/> <h3>Messages sent from the server</h3> <ul id="msgServer"> </ul> </body> </html>
All the heavy duty work of setting up the event handlers and managing the WebSocket connection is done by the Javascript. Let me explain in brief about connecting to a WebSocket server end point. In our example the URI for the end point is: “/DemoApplication/webSocket”, so to connect to this URI from Javascript we have to do the following:
websocket = new WebSocket("ws://localhost:8080/DemoApplication/webSocket");
Look at the “ws” protocol used for WebSocket connections. Once we get an WebSocket object we can register different handlers for listening to events, like opening of socket, closing of socket, error occurred in socket connection and on receiving the message from other end point, as shown below:
websocket.onopen = function() { //Some event handler here. } websocket.onerror = function() { //Some event handler here. } websocket.onclose = function() { //Some event handler here. }
And the websocket can be closed by invoking the close()
method on the websocket object. One thing we missed is about sending messages to the other end point. In the WebSocket API we have 4 states for a websocket to being: CONNECTING, OPEN, CLOSING, CLOSED. And the state of the websocket can be obtained from the readyState
parameter as shown below:
if (websocket.readyState == WebSocket.OPEN) { websocket.send("Sending message number: " + counter + " from client"); msg = "Message sent!!"; } else if (websocket.readyState == WebSocket.CLOSING) { msg = "Websocket connection is CLOSING. Please start a new connection"; } else if (websocket.readyState == WebSocket.CLOSED) { msg = "Websocket connection is CLOSED. Please start a new connection"; } console.log(msg);
Lets look at the complete Javascript code for this sample:
//File Name: webSocket.js var websocket; //Counter used to keep track of the count of messages sent to server var counter; window.onload = function() { counter = 1; var wsStartBtn = document.getElementById("startWsConn"); var wsSendBtn = document.getElementById("sendWsData"); var wsStatusDiv = document.getElementById("wsStatus"); wsStartBtn.onclick = function() { //Websocket connection initialization. websocket = new WebSocket("ws://localhost:8080/DemoApplication/webSocket"); //registering the callback for various events. websocket.onopen = function() { wsStatusDiv.innerHTML = "Connected to the web socket"; var wsSendDiv = document.getElementById("wsSendStatus"); wsSendDiv.innerHTML = ""; } websocket.onerror = function() { wsStatusDiv.innerHTML = "ERROR while connecting to the web socket"; } websocket.onclose = function() { wsStatusDiv.innerHTML = "Connection to Web Socket CLOSED"; } websocket.onmessage = function(event) { //Showing the message from the server on the UI. var serverMsgList = document.getElementById("msgServer"); var aMessage = document.createElement("li"); aMessage.innerHTML = event.data; serverMsgList.appendChild(aMessage); } } wsSendBtn.onclick = function() { var wsSendDiv = document.getElementById("wsSendStatus"); var msg; if (websocket != null) { if (websocket.readyState == WebSocket.OPEN) { websocket.send("Sending message number: " + counter + " from client"); msg = "Message sent!!"; counter++; } else if (websocket.readyState == WebSocket.CLOSING) { msg = "Websocket connection is CLOSING. Please start a new connection"; } else if (websocket.readyState == WebSocket.CLOSED) { msg = "Websocket connection is CLOSED. Please start a new connection"; } wsSendDiv.innerHTML = msg; console.log(msg); } } var closeBtn = document.getElementById("closeWsConn"); closeBtn.onclick = function() { if (websocket != null) { websocket.close(); } } }
Once you have deployed your changes and load the webSocketDemo.jsp you will initially see this screen:
And once you connect via Web Socket and send some messages you can see server responding with some messages:
And you will also find the data sent from the client to the server printed in the server’s console logs:
I hope this article is helpful to understand the one of the greatest new feature introduced in the Java EE 7.0. With Web Socket, it is easy for the client and server communication to transfer multiple data requests. Please try out the example provided in this tutorial and post your comments. Also follow us in the social network to get the latest update on Java tutorials.