Collaborative Whiteboard using WebSocket in GlassFish 4 - Text/JSON and Binary/ArrayBuffer Data Transfer (TOTD #189)


This blog has published a few blogs on using JSR 356 Reference Implementation (Tyrus) as its integrated in GlassFish 4 promoted builds.
  • TOTD #183: Getting Started with WebSocket in GlassFish
  • TOTD #184: Logging WebSocket Frames using Chrome Developer Tools, Net-internals and Wireshark
  • TOTD #185: Processing Text and Binary (Blob, ArrayBuffer, ArrayBufferView) Payload in WebSocket
  • TOTD #186: Custom Text and Binary Payloads using WebSocket
One of the typical usecase for WebSocket is online collaborative games. This Tip Of The Day (TOTD) explains a sample that can be used to build such games easily.

The application is a collaborative whiteboard where different shapes can be drawn in multiple colors. The shapes drawn on one browser are automatically drawn on all other peer browsers that are connected to the same endpoint. The shape, color, and coordinates of the image are transfered using a JSON structure. A browser may opt-out of sharing the figures. Alternatively any browser can send a snapshot of their existing whiteboard to all other browsers. Take a look at this video to understand how the application work and the underlying code.



The Java API for WebSocket has evolved since this sample was first created. Here is the source code corresponding to different builds of GlassFish:
GlassFish4 4 Build Source Code
66 here
74 here
76 here
78 here
84 here
The code in the application is explained below.

The web page (index.jsp) has a HTML5 Canvas as shown:
<canvas id="myCanvas" width="150" height="150" style="border:1px solid #000000;"></canvas>
And some radio buttons to choose the color and shape. By default, the shape, color, and coordinates of any figure drawn on the canvas are put in a JSON structure and sent as a message to the WebSocket endpoint. The JSON structure looks like:
{
"shape": "square",
"color": "#FF0000",
"coords": {
"x": 31.59999942779541,
"y": 49.91999053955078
}
}
The endpoint definition looks like:
@WebSocketEndpoint(value = "websocket",
encoders = {FigureDecoderEncoder.class},
decoders = {FigureDecoderEncoder.class})
public class Whiteboard {

As you can see, the endpoint has decoder and encoder registered that decodes JSON to a Figure (a POJO class) and vice versa respectively. The decode method looks like:
public Figure decode(String string) throws DecodeException {
try {
JSONObject jsonObject = new JSONObject(string);
return new Figure(jsonObject);
} catch (JSONException ex) {
throw new DecodeException("Error parsing JSON", ex.getMessage(), ex.fillInStackTrace());
}
}
And the encode method looks like:
public String encode(Figure figure) throws EncodeException {
return figure.getJson().toString();
}
FigureDecoderEncoder implements both decoder and encoder functionality but thats purely for convenience. But the recommended design pattern is to keep them in separate classes. In certain cases, you may even need only one of them.

On the client-side, the Canvas is initialized as:
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
canvas.addEventListener("click", defineImage, false);

The defineImage method constructs the JSON structure as shown above and sends it to the endpoint using websocket.send().

An instant snapshot of the canvas is sent using binary transfer with WebSocket. The WebSocket is initialized as:
var wsUri = "ws://localhost:8080/whiteboard/websocket";
var websocket = new WebSocket(wsUri);
websocket.binaryType = "arraybuffer";

The important part is to set the binaryType property of WebSocket to arraybuffer. This ensures that any binary transfers using WebSocket are done using ArrayBuffer as the default type seem to be blob. The actual binary data transfer is done using the following:

var image = context.getImageData(0, 0, canvas.width, canvas.height);
var buffer = new ArrayBuffer(image.data.length);
var bytes = new Uint8Array(buffer);
for (var i=0; i<bytes.length; i++) {
bytes[i] = image.data[i];
}
websocket.send(bytes);

This comprehensive sample shows the following features of JSR 356 API:
  • Annotation-driven endpoints
  • Send/receive text and binary payload in WebSocket
  • Encoders/decoders for custom text payload

In addition, it also shows how images can be captured and drawn using HTML5 Canvas in a JSP.

How could this be turned in to an online game ? Imagine drawing a Tic-tac-toe board on the canvas with two players playing and others watching. Then you can build access rights and controls within the application itself. Instead of sending a snapshot of the canvas on demand, a new peer joining the game could be automatically transferred the current state as well. Do you want to build this game ?

I built a similar game a few years ago. Do somebody want to rewrite the game using WebSocket APIs ? :-)

Many thanks to Jitu and Akshay for helping through the WebSocket internals!

Here are some references for you:
Subsequent blogs will discuss the following topics (not necessary in that order) ...
  • Error handling
  • Interface-driven WebSocket endpoint
  • Java client API
  • Client and Server configuration
  • Security
  • Subprotocols
  • Extensions
  • Other topics from the API
Comments:

Nice work Mr. Gupta, I really appreciate it, is simple and amazing.
I am a jWebSocket developer, we have a big structure but with not much documentation, some long ago I made an engine for jWebSocket to wrap grizzly websockets interface that GlassFish uses, maybe you can be interested on it. It allows us keep all the approach from jWebSocket inside GlassFish, probably you would also like to take a look it is already included in the jWebSocket packet, with just changing the configuration which engine to start you get it running, http://jwebsocket.org. We have a lot of functionalities already implemented such as listeners, plugins, very easy to use in server and client side, allowing you to focus only in your application without taking into account many other functionalities already implemented in the framework.
Best regards,
Victor Antonio

Posted by vbarzana on November 30, 2012 at 01:46 AM PST #

Awesome work, Arun!

Posted by Jeff on November 30, 2012 at 04:49 PM PST #

Hi Arun,
thanks for the excellent blog! Very comprehensive and easy to follow! Just as your YouTube video recording for the same example at
http://www.youtube.com/watch?v=uoMKlSNUPsk
Thanks and best regards,
Peter

Posted by guest on December 02, 2012 at 12:50 PM PST #

Great stuff! Will it only work in Glassfish?

Posted by Melvin Vivas on December 02, 2012 at 05:54 PM PST #

@Victor, please share your feedback with the JSR 356 EG at http://java.net/projects/websocket-spec/lists/users/archive

This sample should work on any JSR 356 compliant implementation, for now this is only implemented in GlassFish 4. But any Java EE 7 compliant application server will support it.

Posted by Arun Gupta on December 02, 2012 at 06:02 PM PST #

Hello, sir i would like to ask that what is the scope of java training, what all topics should be covered and it is kinda bothering me … and has anyone studies from this course http://www.wiziq.com/course/1779-core-and-advance-java-concepts of core and advance java online ?? or tell me any other guidance...
would really appreciate help… and Also i would like to thank for all the information you are providing on java concepts.

Posted by christi parks on December 03, 2012 at 07:04 AM PST #

Hello all, this example does not run in glassfish 4.0-b66

Posted by guest on December 11, 2012 at 03:32 AM PST #

APIs have changed from 64 -> 66. I'll upload an updated version later today.

Posted by Arun Gupta on December 11, 2012 at 06:17 AM PST #

New sample code download is now available and is tested on GlassFish build 66.

Posted by Arun Gupta on December 11, 2012 at 09:41 AM PST #

Arun, I posted to GitHub a simple tool that I built to do regression tests against a WebSocket server in development. It's an HTML5/JS single page app that makes it nice/easy to do replay tests.

http://jw-gadgetworks.github.com/websocket-tester/

Posted by Jeff Williams on January 08, 2013 at 02:20 PM PST #

Thanks a lot Jeff for sharing the link!

Looking forward to your sample being ported to GlassFish 4/Tyrus. Happy to help in any manner.

Posted by Arun Gupta on January 09, 2013 at 09:10 AM PST #

Are there some advantages of one language C and Java over the other? Which one is optimal? Which one is more "future proof"? Would it be optimal to know both? If so, which order? I am a little confused on the subject. Just got across this course http://www.wiziq.com/course/1779-core-and-advance-java-concepts, will it be helpful also. A little enlightenment could help. Thanks

Posted by christi parks on January 23, 2013 at 02:19 AM PST #

Hi Arun,

I observed that the problems I am facing in my program are there in the whiteboard app given in your blog too. (At least when I try to run locally .
Problem is the java code throws exception when you refresh one of the browser white board page. And after that the program does not work.

The reason is that the session object is null on close. http://java.net/jira/browse/TYRUS-73

I have raised a couple of JIRA bugs.

To avoid this situation I have put the code "peer.getRemote().sendObject(figure);" in try block so that if it fails , it can gracefully remove that peer instead of ending up in exception.

Also the isOpen API does not work ( http://java.net/jira/browse/TYRUS-76 ) for me so that I could check if the socket is still open before trying to send any message.

Posted by guest on January 28, 2013 at 09:08 PM PST #

Hey , Has the specs changed again ? The whiteboard seems to be not running in build 73

Error:

[13:13:36.097] Firefox can't establish a connection to the server at ws://localhost:8080/whiteboard/websocket. @ http://localhost:8080/whiteboard/websocket.js:42
--
[13:13:44.190] sending text: {"shape":"square","color":"#FF0000","coords":{"x":106,"y":79.41667175292969}}

Posted by Neil on February 02, 2013 at 11:47 PM PST #

Neil,

I'll try 73 later today and update the sample code if required.

Arun

Posted by Arun Gupta on February 03, 2013 at 11:41 PM PST #

Neil,

I do not see b73 http://download.java.net/glassfish/4.0/promoted. Where did you download it from ?

Arun

Posted by Arun Gupta on February 04, 2013 at 02:49 PM PST #

Hi Arun, i also have issues with b73. It is not able to connect.
What i saw is changes in the EndpointFactory and i am not able to create the DummyEndpointFactory

Posted by Johnny Bekkestad on February 07, 2013 at 04:30 AM PST #

73 is not promoted yet. Can we please stick with 72 only ?

http://dlc.sun.com.edgesuite.net/glassfish/4.0/promoted/glassfish-4.0-b72.zip

I'll update the sample when 73 is available :-)

Posted by Arun Gupta on February 07, 2013 at 01:16 PM PST #

THe sample is now working with GlassFish b74 and the source code is available at:

https://blogs.oracle.com/arungupta/resource/totd189-whiteboard-glassfish74.zip

Posted by Arun Gupta on February 11, 2013 at 05:17 PM PST #

GF4 b74 occasionally drops websocket return messages under high load. Is this a known issue?

To reproduce, write a simple echo server like TOTD #183 and send it a bunch of 1000 string messages each 10bk of size. Some of the messages won't return to the browser (though all of them reach the server).

Posted by guest on February 16, 2013 at 01:43 AM PST #

Just checked with the engineering team and this seems to be a known issue. Filed http://java.net/jira/browse/TYRUS-98 for tracking.

Posted by Arun Gupta on February 18, 2013 at 09:44 AM PST #

I tried b76 today and used the code Posted by Arun Gupta on February 11, 2013 at 05:17 PM PST # , facing the same problem

[12:41:22.599] Firefox can't establish a connection to the server at ws://localhost:8080/whiteboard/websocket. @ http://localhost:8080/whiteboard/websocket.js:42

Somehow it is not able to reach the endpoint. JSP renders just fine. The libraries also got downloaded via POM when I rebuild.

Posted by Neil on February 19, 2013 at 11:14 PM PST #

The sample is now updated to work with GlassFish b76 and the source code is available at:

https://blogs.oracle.com/arungupta/resource/totd189-whiteboard-glassfish76.zip

Posted by Arun Gupta on February 20, 2013 at 04:32 PM PST #

Neil,

Does this work with b74 and only causing issue with b76 ?

Arun

Posted by Arun Gupta on February 20, 2013 at 04:57 PM PST #

Hi Arun,

Could you advise how I can get the sample project to work.
I get the error 'unexpected response code 404'.
The error is displayed in the chrome javascript console

These are the environment details

Source code
~~~~~~~~~~~
totd189-whiteboard-glassfish78

Glassfish
~~~~~~~~~
glassfish-4.0-b78

Netbeans
~~~~~~~~
Product Version: NetBeans IDE Dev (Build 201305072300)
Updates: Updates available

Java SDK
~~~~~~~~
Java: 1.7.0_21; Java HotSpot(TM) 64-Bit Server VM 23.21-b01
Runtime: Java(TM) SE Runtime Environment 1.7.0_21-b11

Linux
~~~~~
Debian Squeeze 6.0.6
System: Linux version 3.2.0-0.bpo.2-amd64 running on amd64; UTF-8; en_US (nb)

Posted by guest on May 08, 2013 at 04:32 AM PDT #

Post a Comment:
Comments are closed for this entry.
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