Tuesday Oct 16, 2012

WebSocket Applications using Java: JSR 356 Early Draft Now Available (TOTD #183)


WebSocket provide a full-duplex and bi-directional communication protocol over a single TCP connection. JSR 356 is defining a standard API for creating WebSocket applications in the Java EE 7 Platform. This Tip Of The Day (TOTD) will provide an introduction to WebSocket and how the JSR is evolving to support the programming model.

First, a little primer on WebSocket!

WebSocket is a combination of IETF RFC 6455 Protocol and W3C JavaScript API (still a Candidate Recommendation). The protocol defines an opening handshake and data transfer. The API enables Web pages to use the WebSocket protocol for two-way communication with the remote host.

Unlike HTTP, there is no need to create a new TCP connection and send a chock-full of headers for every message exchange between client and server. The WebSocket protocol defines basic message framing, layered over TCP. Once the initial handshake happens using HTTP Upgrade, the client and server can send messages to each other, independent from the other. There are no pre-defined message exchange patterns of request/response or one-way between client and and server. These need to be explicitly defined over the basic protocol.

The communication between client and server is pretty symmetric but there are two differences:
  • A client initiates a connection to a server that is listening for a WebSocket request.
  • A client connects to one server using a URI. A server may listen to requests from multiple clients on the same URI.

Other than these two difference, the client and server behave symmetrically after the opening handshake. In that sense, they are considered as "peers".

After a successful handshake, clients and servers transfer data back and forth in conceptual units referred as "messages". On the wire, a message is composed of one or more frames. Application frames carry payload intended for the application and can be text or binary data. Control frames carry data intended for protocol-level signaling.

Now lets talk about the JSR!

The Java API for WebSocket is worked upon as JSR 356 in the Java Community Process. This will define a standard API for building WebSocket applications. This JSR will provide support for:
  • Creating WebSocket Java components to handle bi-directional WebSocket conversations
  • Initiating and intercepting WebSocket events
  • Creation and consumption of WebSocket text and binary messages
  • The ability to define WebSocket protocols and content models for an application
  • Configuration and management of WebSocket sessions, like timeouts, retries, cookies, connection pooling
  • Specification of how WebSocket application will work within the Java EE security model
Tyrus is the Reference Implementation for JSR 356 and is already integrated in GlassFish 4.0 Promoted Builds.

And finally some code!

The API allows to create WebSocket endpoints using annotations and interface. This TOTD will show a simple sample using annotations. A subsequent blog will show more advanced samples.

A POJO can be converted to a WebSocket endpoint by specifying @WebSocketEndpoint and @WebSocketMessage.
@WebSocketEndpoint("/hello")
public class HelloBean {
    @WebSocketMessage
    public String sayHello(String name) {         return "Hello " + name + "!";     }
}
  • @WebSocketEndpoint marks this class as a WebSocket endpoint listening at URI defined by the value attribute.
  • The @WebSocketMessage identifies the method that will receive the incoming WebSocket message. This first method parameter is injected with payload of the incoming message. In this case it is assumed that the payload is text-based. It can also be of the type byte[] in case the payload is binary. A custom object may be specified if decoders attribute is specified in the @WebSocketEndpoint. This attribute will provide a list of classes that define how a custom object can be decoded.

    This method can also take an optional Session parameter. This is injected by the runtime and capture a conversation between two endpoints.
  • The return type of the method can be String, byte[] or a custom object. The encoders attribute on @WebSocketEndpoint need to define how a custom object can be encoded.
The client side is an index.jsp with embedded JavaScript. The JSP body looks like:

<div style="text-align: center;">
<form action="">
    <input onclick="say_hello()" value="Say Hello" type="button">
        <input id="nameField" name="name" value="WebSocket" type="text"><br>
   </form>
</div>
<div id="output"></div>
The code is relatively straight forward. It has an HTML form with a button that invokes say_hello() method and a text field named nameField. A div placeholder is available for displaying the output.

Now, lets take a look at some JavaScript code:

<script language="javascript" type="text/javascript">
var wsUri = "ws://localhost:8080/HelloWebSocket/hello";
    var websocket = new WebSocket(wsUri);
    websocket.onopen = function(evt) { onOpen(evt) };
    websocket.onmessage = function(evt) { onMessage(evt) };
    websocket.onerror = function(evt) { onError(evt) };
    function init() {         output = document.getElementById("output");     }     function say_hello() {      websocket.send(nameField.value);         writeToScreen("SENT: " + nameField.value);     }

  • This application is deployed as "HelloWebSocket.war" (download here) on GlassFish 4.0 promoted build 57. So the WebSocket endpoint is listening at "ws://localhost:8080/HelloWebSocket/hello". A new WebSocket connection is initiated by specifying the URI to connect to.
  • The JavaScript API defines callback methods that are invoked when the connection is opened (onOpen), closed (onClose), error received (onError), or a message from the endpoint is received (onMessage).
  • The client API has several send methods that transmit data over the connection. This particular script sends text data in the say_hello method using nameField's value from the HTML shown earlier.
  • Each click on the button sends the textbox content to the endpoint over a WebSocket connection and receives a response based upon implementation in the sayHello method shown above.
How to test this out ?
  1. Download the entire source project here or just the WAR file.
  2. Download GlassFish4.0 build 57 or later and unzip.
  3. Start GlassFish as "asadmin start-domain".
  4. Deploy the WAR file as "asadmin deploy HelloWebSocket.war".
  5. Access the application at http://localhost:8080/HelloWebSocket/index.jsp.

After clicking on "Say Hello" button, the output would look like:


Here are some references for you:
Subsequent blogs will discuss the following topics (not necessary in that order) ...
  • Binary data as payload
  • Custom payloads using encoder/decoder
  • Error handling
  • Interface-driven WebSocket endpoint
  • Java client API
  • Client and Server configuration
  • Security
  • Subprotocols
  • Extensions
  • Other topics from the API
  • Capturing WebSocket on-the-wire messages

Monday Jun 18, 2012

WebSocket and Java EE 7 - Getting Ready for JSR 356 (TOTD #181)


WebSocket is developed as part of HTML 5 specification and provides a bi-directional, full-duplex communication channel over a single TCP socket. It provides dramatic improvement over the traditional approaches of Polling, Long-Polling, and Streaming for two-way communication. There is no latency from establishing new TCP connections for each HTTP message.

There is a WebSocket API and the WebSocket Protocol. The Protocol defines "handshake" and "framing". The handshake defines how a normal HTTP connection can be upgraded to a WebSocket connection. The framing defines wire format of the message. The design philosophy is to keep the framing minimum to avoid the overhead. Both text and binary data can be sent using the API.

WebSocket may look like a competing technology to Server-Sent Events (SSE), but they are not. Here are the key differences:
  1. WebSocket can send and receive data from a client. A typical example of WebSocket is a two-player game or a chat application. Server-Sent Events can only push data data to the client. A typical example of SSE is stock ticker or news feed. With SSE, XMLHttpRequest can be used to send data to the server.
  2. For server-only updates, WebSockets has an extra overhead and programming can be unecessarily complex. SSE provides a simple and easy-to-use model that is much better suited.
  3. SSEs are sent over traditional HTTP and so no modification is required on the server-side. WebSocket require servers that understand the protocol.
  4. SSE have several features that are missing from WebSocket such as automatic reconnection, event IDs, and the ability to send arbitrary events.
    1. The client automatically tries to reconnect if the connection is closed. The default wait before trying to reconnect is 3 seconds and can be configured by including "retry: XXXX\n" header where XXXX is the milliseconds to wait before trying to reconnect.
    2. Event stream can include a unique event identifier. This allows the server to determine which events need to be fired to each client in case the connection is dropped in between.
    3. The data can span multiple lines and can be of any text format as long as EventSource message handler can process it.
  5. WebSockets provide true real-time updates, SSE can be configured to provide close to real-time by setting appropriate timeouts.
OK, so all excited about WebSocket ? Want to convert your POJOs into WebSockets endpoint ?

websocket-sdk and GlassFish 4.0 is here to help!

The complete source code shown in this project can be downloaded here.

On the server-side, the WebSocket SDK converts a POJO into a WebSocket endpoint using simple annotations. Here is how a WebSocket endpoint will look like:

@WebSocket(path="/echo")
public class EchoBean {

@WebSocketMessage
public String echo(String message) {
return message + " (from your server)";
}
}

In this code
  1. "@WebSocket" is a class-level annotation that declares a POJO to accept WebSocket messages. The path at which the messages are accepted is specified in this annotation.
  2. "@WebSocketMessage" indicates the Java method that is invoked when the endpoint receives a message. This method implementation echoes the received message concatenated with an additional string.

The client-side HTML page looks like

<div style="text-align: center;">
<form action="">
<input onclick="send_echo()" value="Press me" type="button">
<input id="textID" name="message" value="Hello WebSocket!" type="text"><br>
</form>
</div>
<div id="output"></div>

WebSocket allows a full-duplex communication. So the client, a browser in this case, can send a message to a server, a WebSocket endpoint in this case. And the server can send a message to the client at the same time. This is unlike HTTP which follows a "request" followed by a "response". In this code, the "send_echo" method in the JavaScript is invoked on the button click. There is also a <div> placeholder to display the response from the WebSocket endpoint.

The JavaScript looks like:

<script language="javascript" type="text/javascript">
var wsUri = "ws://localhost:8080/websockets/echo";
var websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };

function init() {
output = document.getElementById("output");
}

function send_echo() {
websocket.send(textID.value);
writeToScreen("SENT: " + textID.value);
}

function onOpen(evt) {
writeToScreen("CONNECTED");
}

function onMessage(evt) {
writeToScreen("RECEIVED: " + evt.data);
}

function onError(evt) {
writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}

function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}

window.addEventListener("load", init, false);
</script>

In this code
  1. The URI to connect to on the server side is of the format

    ws://<HOST>:<PORT>/websockets/<PATH>

    "ws" is a new URI scheme introduced by the WebSocket protocol. <PATH> is the path on the endpoint where the WebSocket messages are accepted. In our case, it is

    ws://localhost:8080/websockets/echo

    WEBSOCKET_SDK-1 will ensure that context root is included in the URI as well.
  2. WebSocket is created as a global object so that the connection is created only once. This object establishes a connection with the given host, port and the path at which the endpoint is listening.
  3. The WebSocket API defines several callbacks that can be registered on specific events. The "onopen", "onmessage", and "onerror" callbacks are registered in this case. The callbacks print a message on the browser indicating which one is called and additionally also prints the data sent/received.
  4. On the button click, the WebSocket object is used to transmit text data to the endpoint. Binary data can be sent as one blob or using buffering.
The HTTP request headers sent for the WebSocket call are:

GET ws://localhost:8080/websockets/echo HTTP/1.1
Origin: http://localhost:8080
Connection: Upgrade
Sec-WebSocket-Extensions: x-webkit-deflate-frame
Host: localhost:8080
Sec-WebSocket-Key: mDbnYkAUi0b5Rnal9/cMvQ==
Upgrade: websocket
Sec-WebSocket-Version: 13

And the response headers received are

Connection:Upgrade
Sec-WebSocket-Accept:q4nmgFl/lEtU2ocyKZ64dtQvx10=
Upgrade:websocket
(Challenge Response):00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00

The headers are shown in Chrome as shown below:


The complete source code shown in this project can be downloaded here.

The builds from websocket-sdk are integrated in GlassFish 4.0 builds. Would you like to live on the bleeding edge ? Then follow the instructions below to check out the workspace and install the latest SDK:

  1. Check out the source code

    svn checkout https://svn.java.net/svn/websocket-sdk~source-code-repository
  2. Build and install the trunk in your local repository as:

    mvn install
  3. Copy "./bundles/websocket-osgi/target/websocket-osgi-0.3-SNAPSHOT.jar" to "glassfish3/glassfish/modules/websocket-osgi.jar" in your GlassFish 4 latest promoted build. Notice, you need to overwrite the JAR file.
Anybody interested in building a cool application using WebSocket and get it running on GlassFish ? :-)

This work will also feed into JSR 356 - Java API for WebSocket.

On a lighter side, there seems to be less agreement on the name. Here are some of the options that are prevalent:
I prefer "WebSocket" as that seems to be most common usage and used by the W3C API as well. What do you use ?

About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today