Understanding WebSockets and Server-sent Events in Detail

Exclusive offer: get 80% off this eBook here
Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON

Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON — Save 80%

A complete and practical guide to building RESTful Web Services with the latest Java EE7 API with this book and ebook

₨739.00    ₨147.80
by Bhakti Mehta Masoud Kalali | October 2013 | Enterprise Articles Web Services Java

In this article by Masoud Kalali and Bhakti Mehta, the authors of Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON, we will cover the following:

  • Encoders and decoders in Java API for WebSockets
  • Java WebSockets Client API
  • Sending different types of data such as Blob and Binary using Java API for WebSockets
  • Security and WebSockets
  • Best practices for WebSockets-based applications
  • Developing Server-sent Events clients using Jersey API
  • Best practices for Server-sent Events

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

Encoders and decoders in Java API for WebSockets

As seen in the previous chapter, the class-level annotation @ServerEndpoint indicates that a Java class is a WebSocket endpoint at runtime. The value attribute is used to specify a URI mapping for the endpoint. Additionally the user can add encoder and decoder attributes to encode application objects into WebSocket messages and WebSocket messages into application objects.

The following table summarizes the @ServerEndpoint annotation and its attributes:

Annotation

Attribute

Description

@ServerEndpoint

 

This class-level annotation signifies that the Java class is a WebSockets server endpoint.

 

value

The value is the URI with a leading '/.'

 

encoders

The encoders contains a list of Java classes that act as encoders for the endpoint. The classes must implement the Encoder interface.

 

decoders

The decoders contains a list of Java classes that act as decoders for the endpoint. The classes must implement the Decoder interface.

 

configurator

The configurator attribute allows the developer to plug in their implementation of ServerEndpoint.Configurator that is used when configuring the server endpoint.

 

subprotocols

The sub protocols attribute contains a list of sub protocols that the endpoint can support.

In this section we shall look at providing encoder and decoder implementations for our WebSockets endpoint.

The preceding diagram shows how encoders will take an application object and convert it to a WebSockets message. Decoders will take a WebSockets message and convert to an application object. Here is a simple example where a client sends a WebSockets message to a WebSockets java endpoint that is annotated with @ServerEndpoint and decorated with encoder and decoder class. The decoder will decode the WebSockets message and send back the same message to the client. The encoder will convert the message to a WebSockets message. This sample is also included in the code bundle for the book.

Here is the code to define ServerEndpoint with value for encoders and decoders:

@ServerEndpoint(value="/book", encoders={MyEncoder.class},
decoders = {MyDecoder.class} ) public class BookCollection { @OnMessage public void onMessage(Book book,Session session) { try { session.getBasicRemote().sendObject(book); } catch (Exception ex) { ex.printStackTrace(); } } @OnOpen public void onOpen(Session session) { System.out.println("Opening socket" +session.getBasicRemote() ); } @OnClose public void onClose(Session session) { System.out.println("Closing socket" + session.getBasicRemote()); } }

In the preceding code snippet, you can see the class BookCollection is annotated with @ServerEndpoint. The value=/book attribute provides URI mapping for the endpoint. The @ServerEndpoint also takes the encoders and decoders to be used during the WebSocket transmission. Once a WebSocket connection has been established, a session is created and the method annotated with @OnOpen will be called. When the WebSocket endpoint receives a message, the method annotated with @OnMessage will be called. In our sample the method simply sends the book object using the Session.getBasicRemote() which will get a reference to the RemoteEndpoint and send the message synchronously.

Encoders can be used to convert a custom user-defined object in a text message, TextStream, BinaryStream, or BinaryMessage format.

An implementation of an encoder class for text messages is as follows:

public class MyEncoder implements Encoder.Text<Book> { @Override public String encode(Book book) throws EncodeException { return book.getJson().toString(); } }

As shown in the preceding code, the encoder class implements Encoder.Text<Book>. There is an encode method that is overridden and which converts a book and sends it as a JSON string. (More on JSON APIs is covered in detail in the next chapter)

Decoders can be used to decode WebSockets messages in custom user-defined objects. They can decode in text, TextStream, and binary or BinaryStream format.

Here is a code for a decoder class:

public class MyDecoder implements Decoder.Text<Book> { @Override public Book decode(String string) throws DecodeException { javax.json.JsonObject jsonObject = javax.json.Json.createReader
(new StringReader(string)).readObject(); return new Book(jsonObject); } @Override public boolean willDecode(String string) { try { javax.json.Json.createReader(new StringReader
(string)).readObject(); return true; } catch (Exception ex) { } return false; }

In the preceding code snippet, the Decoder.Text needs two methods to be overridden. The willDecode() method checks if it can handle this object and decode it. The decode() method decodes the string into an object of type Book by using the JSON-P API javax.json.Json.createReader().

The following code snippet shows the user-defined class Book:

public class Book { public Book() {} JsonObject jsonObject; public Book(JsonObject json) { this.jsonObject = json; } public JsonObject getJson() { return jsonObject; } public void setJson(JsonObject json) { this.jsonObject = json; } public Book(String message) { jsonObject = Json.createReader(new
StringReader(message)).readObject(); } public String toString () { StringWriter writer = new StringWriter(); Json.createWriter(writer).write(jsonObject); return writer.toString(); } }

The Book class is a user-defined class that takes the JSON object sent by the client. Here is an example of how the JSON details are sent to the WebSockets endpoints from JavaScript.

var json = JSON.stringify({ "name": "Java 7 JAX-WS Web Services", "author":"Deepak Vohra", "isbn": "123456789" }); function addBook() { websocket.send(json); }

The client sends the message using websocket.send() which will cause the onMessage() of the BookCollection.java to be invoked. The BookCollection.java will return the same book to the client. In the process, the decoder will decode the WebSockets message when it is received. To send back the same Book object, first the encoder will encode the Book object to a WebSockets message and send it to the client.

The Java WebSocket Client API

WebSockets and Server-sent Events , covered the Java WebSockets client API. Any POJO can be transformed into a WebSockets client by annotating it with @ClientEndpoint.

Additionally the user can add encoders and decoders attributes to the @ClientEndpoint annotation to encode application objects into WebSockets messages and WebSockets messages into application objects.

The following table shows the @ClientEndpoint annotation and its attributes:

Annotation

Attribute

Description

@ClientEndpoint

 

This class-level annotation signifies that the Java class is a WebSockets client that will connect to a WebSockets server endpoint.

 

value

The value is the URI with a leading /.

 

encoders

The encoders contain a list of Java classes that act as encoders for the endpoint. The classes must implement the encoder interface.

 

decoders

The decoders contain a list of Java classes that act as decoders for the endpoint. The classes must implement the decoder interface.

 

configurator

The configurator attribute allows the developer to plug in their implementation of ClientEndpoint.Configurator, which is used when configuring the client endpoint.

 

subprotocols

The sub protocols attribute contains a list of sub protocols that the endpoint can support.

Sending different kinds of message data: blob/binary

Using JavaScript we can traditionally send JSON or XML as strings. However, HTML5 allows applications to work with binary data to improve performance. WebSockets supports two kinds of binary data

  • Binary Large Objects (blob)
  • arraybuffer

A WebSocket can work with only one of the formats at any given time.

Using the binaryType property of a WebSocket, you can switch between using blob or arraybuffer:

websocket.binaryType = "blob"; // receive some blob data websocket.binaryType = "arraybuffer"; // now receive ArrayBuffer data

The following code snippet shows how to display images sent by a server using WebSockets.

Here is a code snippet for how to send binary data with WebSockets:

websocket.binaryType = 'arraybuffer';

The preceding code snippet sets the binaryType property of websocket to arraybuffer.

websocket.onmessage = function(msg) { var arrayBuffer = msg.data; var bytes = new Uint8Array(arrayBuffer); var image = document.getElementById('image'); image.src = 'data:image/png;base64,'+encode(bytes); }

When the onmessage is called the arrayBuffer is initialized to the message.data. The Uint8Array type represents an array of 8-bit unsigned integers. The image.src value is in line using the data URI scheme.

Security and WebSockets

WebSockets are secured using the web container security model. A WebSockets developer can declare whether the access to the WebSocket server endpoint needs to be authenticated, who can access it, or if it needs an encrypted connection.

A WebSockets endpoint which is mapped to a ws:// URI is protected under the deployment descriptor with http:// URI with the same hostname,port path since the initial handshake is from the HTTP connection. So, WebSockets developers can assign an authentication scheme, user roles, and a transport guarantee to any WebSockets endpoints.

We will take the same sample as we saw in , WebSockets and Server-sent Events , and make it a secure WebSockets application.

Here is the web.xml for a secure WebSocket endpoint:

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <security-constraint> <web-resource-collection> <web-resource-name>BookCollection</web-resource-name> <url-pattern>/index.jsp</url-pattern> <http-method>PUT</http-method> <http-method>POST</http-method> <http-method>DELETE</http-method> <http-method>GET</http-method> </web-resource-collection> <user-data-constraint> <description>SSL</description> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> </web-app>

As you can see in the preceding snippet, we used <transport-guarantee>CONFIDENTIAL</transport-guarantee>.

The Java EE specification followed by application servers provides different levels of transport guarantee on the communication between clients and application server. The three levels are:

  • Data Confidentiality (CONFIDENTIAL) : We use this level to guarantee that all communication between client and server goes through the SSL layer and connections won't be accepted over a non-secure channel.
  • Data Integrity (INTEGRAL) : We can use this level when a full encryption is not required but we want our data to be transmitted to and from a client in such a way that, if anyone changed the data, we could detect the change.
  • Any type of connection (NONE) : We can use this level to force the container to accept connections on HTTP and HTTPs.

The following steps should be followed for setting up SSL and running our sample to show a secure WebSockets application deployed in Glassfish.

  1. Generate the server certificate:

    keytool -genkey -alias server-alias -keyalg RSA
    -keypass changeit --storepass changeit -keystore keystore.jks

  2. Export the generated server certificate in keystore.jks into the file server.cer:

    keytool -export -alias server-alias -storepass
    changeit -file server.cer -keystore keystore.jks

  3. Create the trust-store file cacerts.jks and add the server certificate to the trust store:

    keytool -import -v -trustcacerts -alias server-alias -file server.cer
    -keystore cacerts.jks -keypass changeit -storepass changeit

  4. Change the following JVM options so that they point to the location and name of the new keystore. Add this in domain.xml under java-config:

    <jvm-options>-Djavax.net.ssl.keyStore=${com.sun.aas.instanceRoot}
    /config/keystore.jks</jvm-options> <jvm-options>-Djavax.net.ssl.trustStore=
    ${com.sun.aas.instanceRoot}/config/cacerts.jks</jvm-options>

  5. Restart GlassFish. If you go to https://localhost:8181/helloworld-ws/, you can see the secure WebSocket application.
  6. Here is how the the headers look under Chrome Developer Tools:

  7. Open Chrome Browser and click on View and then on Developer Tools .
  8. Click on Network .
  9. Select book under element name and click on Frames .

As you can see in the preceding screenshot, since the application is secured using SSL the WebSockets URI, it also contains wss://, which means WebSockets over SSL.

So far we have seen the encoders and decoders for WebSockets messages. We also covered how to send binary data using WebSockets. Additionally we have demonstrated a sample on how to secure WebSockets based application. We shall now cover the best practices for WebSocket based-applications.

Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON A complete and practical guide to building RESTful Web Services with the latest Java EE7 API with this book and ebook
Published: October 2013
eBook Price: ₨739.00
Book Price: ₨1,232.00
See more
Select your format and quantity:

Best practices for WebSockets based applications

This section will cover best practices for WebSockets based applications. The following topics will be covered:

  • Throttling the rate of sending data
  • Controlling the maximum size of the message
  • Working with proxy servers and WebSockets

Throttling the rate of sending data

After the WebSocket connection is opened, messages can be sent using the send function.

WebSockets have a bufferedAmount attribute that can be used to control the rate of sending data. Using the bufferedAmount attribute you can check the number of bytes that have been queued but not yet sent to the server.

Here is a snippet to test for the bufferedAmount attribute of WebSocket.

// This snippet checks for amount of data buffered but not sent yet // in case it is less than a predefined THRESHOLD the webSocket // can send the data if (webSocket.bufferedAmount < THRESHOLD) webSocket.send(someData); };

This can be done periodically using the setInterval function. As you can see, the developer can periodically check for the bufferedAmount attribute to see if the number of bytes in the queue to be sent to the server exceeds some threshold. In that case it should delay sending messages. Once the buffered amount is less than the threshold it should send more messages.

This is a good practice to check for the bufferedAmount and then send data.

Controlling the maximum size of the message

The maxMessageSize attribute on the @OnMessage annotation in Java class annotated with @ServerEndpoint or @ClientEndpoint allows the developer to specify the maximum size of message in bytes that can be handled by the ClientEndpoint or ServerEndpoint.

If the incoming message exceeds the maximum size then the connection is closed. This is a good practice to control the maximum size of a message so that the client does not deplete its resources while trying to handle a message, which it can't process.

Working with proxy servers and WebSockets

WebSockets and Server-sent Event , covered how the WebSocket upgrade handshake looks. Not all proxy servers may support WebSockets; thus, proxy servers may not allow unencrypted WebSocket traffic to flow through. The clients use a CONNECT call which would never be allowed. The correct approach would be to send the request over https on the standard port 443. Here is an example of the HTTP Connect sent by the browser client to foo.com on port 443.

CONNECT: foo.com:443 Host: foo.com

Since the traffic is encrypted there is a greater chance to pass through the proxy server. Then the CONNECT statements will work and there will be an end-to-end encrypted tunnel for WebSockets.

The following diagram shows how clients can send HTTPS requests which get past the proxy server and firewall; the WebSocket secure scheme will work:

It is a good practice to use WebSocket-based applications with SSL so that the Proxy server does not impede WebSocket communication.

Server-sent Events

We covered Server-sent Events in , WebSockets and Server-sent Events , and compared and contrasted client/server polling alternatives as well as WebSockets. In this chapter we will cover more advanced topics such as developing a Server-sent Events client using Jersey API and best practices for Server-sent Events.

Developing a Server-sent Event client using Jersey API

WebSockets and Server-sent Events , gave a brief introduction to the Server-sent Events and JavaScript API. In this chapter we will cover the Java Client API for Server-sent Events, which is provided by Jersey. Jersey is an implementation of JAX-RS 2.0. In addition to the features of the JAX-RS 2.0 specification, Jersey has provided support for Server-sent Events.

EventSource is the class for reading InboundEvents:

The following snippet shows how to use the EventSource API:

WebTarget webTarget = client.target(new URI(TARGET_URI)); EventSource eventSource = new EventSource(webTarget) { @Override public void onEvent(InboundEvent inboundEvent) { System.out.println("Data " + inboundEvent.getData(String.class); }

The EventSource object is created with WebTarget. We covered WebTarget in , Building RESTful Web Services using JAX-RS .

When a Server-sent Event is received by the client the onEvent() method of the EventSource is invoked. The InboundEvent has the getData() method that takes the String.class that is the type of the message data. You can add any custom defined class here. The JAX-RS MessagebodyReader will be used to read the type of the message. Thus you can see the similarity in the code between using JavaScript API and the Jersey Client API. , Restful Web Services by Example , will show a complete example using the Server Sent Event Jersey Client API.

Best practices for applications based on Server-sent Events

The following chapter covers the best practices for applications based on Server-sent Events. The following topics will be covered:

  • Checking if the event source's origin is as expected
  • Working with proxy servers and Server-sent Events
  • Handling fault tolerance for Server-sent Events

Checking if the event source's origin is as expected

The following snippet shows how to check for the origin of the event source so that it matches the application's origin.

if (e.origin != 'http://foo.com') { alert('Origin was not http://foo.com'); return;

An event stream from an origin distinct from the origin of the content consuming the event stream can result in information leakage. When the events are obtained from the server, it is good practice to check for the events originator to see if it is as expected.

Working with proxy servers and Server-sent Events

Proxy servers can drop HTTP connections after a short timeout. To avoid such dropped connections it may be a good idea to send a comment periodically.

This is how a comment is sent using Server-sent Events.

: this is a comment
OutboundEvent event = new OutboundEvent.Builder().comment("this is a
comment").build();

The Outboundevent.Builder API will send a comment to the client.

The comment will fire nothing yet will make sure that connections do not get dropped between client and server.

Handling fault tolerance for Server-sent Events

WebSockets and Server-sent Events , covered how you can associate IDs with events. The server can send event ids with events by using the following snippet:

id: 123\n data : This is an event stream \n\n

The client keeps the connection alive and tries to reconnect if the connection is dropped. Setting an ID lets the browser keep track of the last event fired so when the connection between the client and server is dropped, on reconnect by the client to the server the Last-Event-ID will be sent back to the server. This ensures the client does not miss any messages. The server can then send events that occur after the Last-Event-ID.

The server may need a message queue to keep track of the different clients connected, check for reconnections, and send messages based on the Last-Event-ID.

Summary

In this chapter we looked at advanced topics for WebSockets and Server-sent Events. We demonstrated with code snippets how to use encoders and decoders and how to receive different kinds of data using WebSockets. We also demonstrated a sample that showed how WebSockets will work with SSL so that when working with proxy servers, the communication is encrypted.

We also discussed best practices for implementing Server-sent Events and WebSockets. We learned how to ensure messages are not lost in Server-sent Events by associating IDs with events. We covered the Jersey Client API for Server-sent Events.

In the next chapter, we will cover more advanced topics such as JSON API in Java EE and the aspects of asynchronous programming to improve scalability with respect to various Java EE specifications such as JAX-RS 2.0, EJB and Servlets.

Resources for Article :


Further resources on this subject:


Developing RESTful Services with JAX-RS 2.0, WebSockets, and JSON A complete and practical guide to building RESTful Web Services with the latest Java EE7 API with this book and ebook
Published: October 2013
eBook Price: ₨739.00
Book Price: ₨1,232.00
See more
Select your format and quantity:

About the Author :


Bhakti Mehta

Bhakti Mehta is a Senior Technology Professional with over 12 years of experience in architecting, designing, and implementing Software Solutions on top of Java EE and other related technologies. On the platform level, she is well experienced in different areas of GlassFish Application Server and Java EE specifications.

Bhakti is experienced in developing open source software and working with open source communities and customers. She is a member of the GlassFish team at Oracle. Bhakti's primary areas of interest are server-side technologies, XML, Web Services, Java EE, and Cloud. She has a bachelors degree in Computer Engineering and a masters degree in Computer Science.

Bhakti is a regular speaker in various conferences along with having articles and enterprise tech tips at different portals. Her tweets can be followed at @bhakti_mehta.

Masoud Kalali

Masoud Kalali has been working on software development projects since 1998, which gives him a broad perspective on software development in general and changes in the software development landscape in the past 1.5 decades. Masoud has experience with a variety of technologies (.NET, J2EE, CORBA, and COM+) on diverse platforms (Solaris, Linux, and Windows). He has a masters degree in Information Systems with a bachelor degree in Software Engineering.

Masoud has authored a fair number of articles and other types of material, including several articles at Java.net and Dzone. He is the author of multiple refcardz, published by Dzone, including but not limited to Using XML in Java (http://refcardz.dzone.com/refcardz/using-xml-java) and Security and GlassFish v3 (http://refcardz.dzone.com/refcardz/getting-startedglassfish) refcardz. Masoud is one of the founding members of NetBeans Dream Team (http://wiki.netbeans.org/NetBeansDreamTeam) and a GlassFish community spotlighted developer (https://glassfish.java.net/public/developers.html). Masoud is the author of GlassFish Security (http://www.packtpub.com/glassfish-security/book) that was published in 2010, covering GlassFish v3 security and Java EE 6 security.

Masoud's main area of research and interest includes service-oriented architecture and large-scale systems development and deployment. In his spare time he enjoys photography, mountaineering, and climbing.

Masoud's Twitter handle is @MasoudKalali if you want to know what he is up to.

Books From Packt


RESTful Java Web Services
RESTful Java Web Services

Developing RESTful Web Services with Jersey 2.0
Developing RESTful Web Services with Jersey 2.0

RESTful PHP Web Services
RESTful PHP Web Services

 Spring Web Services 2 Cookbook
Spring Web Services 2 Cookbook

 Instant GSON [Instant]
Instant GSON [Instant]

 JavaScript and JSON Essentials
JavaScript and JSON Essentials

 Java 7 JAX-WS Web Services
Java 7 JAX-WS Web Services

Sencha Touch Cookbook
Sencha Touch Cookbook


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software