HTML5 Zone is brought to you in partnership with:

Andy Moncsek is a software engineer and passionate Java developer since 2004. He enjoys programming with all kinds of JVM languages, frameworks, and operating systems. He is currently working for Trivadis AG Switzerland and in his spare time he is the project-owner and commiter of JacpFX (http://jacpfx.org); a framework to create Rich Clients in MVC style with JavaFX 8 and an Actor like component approach. Andy has posted 6 posts at DZone. You can read more from them at their website. View Full User Profile

Creating a WebSocket-Chat-Application with Jetty and Glassfish

07.01.2011
| 101401 views |
  • submit to reddit

This article describes how to create a simple HTML5 chat application using WebSockets to connect to a Java back-end. I decided to include a Jetty 8 and a GlassFish 3.1 example to demonstrate the current approaches for server side WebSocket implementations. While a Servlet can easily be migrated from one Servlet container to an other, a WebSocket implementation can not (at the moment). Even with one server provider I had trouble to run an application with different server versions. I started my tests with Jetty 7.2. Later I updated my Maven dependencies to Jetty 7.4 and some interfaces already changed! To avoid trouble with this article I decided to use the latest available Jetty implementation which is currently Jetty 8 M3. For my GlassFish tests I found a useful resource to dive into GlassFish WebSocket implementation. I attached the source code of both examples, so you can easily test it on your own and use it for further explorations of Java WebSockets.

What this chat application will do:

The chat application we create is as simple as possible. We have one single window with two input fields (1 & 3) and one message output field (2). In the first input field on top (1) you can register your chat name. The output field in the middle (2) is used to display the incoming messages posted by all connected users. The input field at the bottom (3) is your message input field where you can type your message. So this is basically the whole application on client side.

To establish a WebSocket connection we simply create a WebSocket instance on client side that is pointing to our Java WebSocket server location.

 	var ws;
 	$(document).ready(
			function() {
				ws = new WebSocket("ws://localhost:8080/../WebSocketChat");
				ws.onopen = function(event) {					
				}
				ws.onmessage = function(event) {
					var $textarea = $('#messages');
					$textarea.val($textarea.val() + event.data + "\n");
				}
				ws.onclose = function(event) {					
				}
			});
										Listing 1

The URL prefix “ws” defines a default WebSocket connection. To open a secure one, the prefix “wss” is used. The WebSocket API defines mainly three callback methods: onopen, onmessage and onclose. The event argument passed by the methods contains a data field with the payload of the server.

To pass a message to the server we define a small function like this:

	function sendMessage() {
		var message = $('#username').val() + ":" + $('#message').val();
		ws.send(message);
		$('#message').val('');
	}
										Listing 2

Here we simply concatenate the user name and the message and call the WebSocket “send” method to transfer the message to server which is then pushing it to all connected users.

The back-end

So, what do we need on server side to create a running application? First of all we need a servlet to connect your client and secondly one socket per client connected to your chat. The WebSocket protocol defines a HTTP-like handshake which allows the server to interpret the handshake as HTTP and to switch to the WebSocket protocol. Once the servlet associates you to a WebSocket all your chat traffic can be handled through this full duplex connection.

The Jetty Implementation

As mentioned before, the server API differs currently on every server. For the Jetty server you create a WebSocketServlet to have a specified entry point where to associate your WebSocket to the client.

 public class WebSocketChatServlet extends WebSocketServlet {
 	public final Set users = new CopyOnWriteArraySet();

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
	}

	@Override
	public WebSocket doWebSocketConnect(HttpServletRequest arg0, String arg1) {
		return new ChatWebSocket(users);
	}
 }
											    Listing 3

As you can see in Listing 3 the doGet method is not implementing anything. Instead we are using the “doWebSocket” to return a “ChatWebSocket” instance referencing a set with all current users in the chat.

Now one single WebSocket is associated with your client. Once the “doWebSocket” method is called, all following operations in our chat will be performed on our newly created “ChatWebSocket” object.

The users set we passed to the “ChatWebSocket” contains all connected users in our simple chat application. When we receive a client message we will iterate this set and notify every single registered user.

So, let’s have a look at the ChatWebSocket:

 public class ChatWebSocket implements OnTextMessage {
	private Connection connection;
	private Set users;

	public ChatWebSocket(Set users ) {
		this.users = users;
	}
	public void onMessage(String data) {
		for (ChatWebSocket user : users) {
			try {
				user.connection.sendMessage(data);
			} catch (Exception e) {
			}
		}

	}
	@Override
	public void onOpen(Connection connection) {
		this.connection = connection;
		users.add(this);
	}
	@Override
	public void onClose(int closeCode, String message) {
		users.remove(this);
       }
 }	
							Listing 4

First of all you may see that the “ChatWebSocket” is implementing an “OnTextMessage” interface. This interface extends the WebSocket interface and is one of those changes I had to deal with when jumped from Jetty 7.2 to Jetty 8. Instead of implementing different kinds of “onMessage” methods you decide whether you want to handle text or binary messages. Use “OnBinaryMessage” when you have to deal with binary data and “OnTextMessage” when you create applications like this chat application.

The “onOpen” method on line 18 associates the current connection to the WebSocket object and adds this object to the global set of connected users. The “onClose” method simply removes the user from the set when disconnecting.

The key method in this WebSocket class is “onMessage”. We use it to notify all connected users by iterating on the users set and calling the “connection.send()” method. Remember the client side code where the massage was also sent to the server by calling this method. Now the server pushes the message to each client. Note that no client side polling is involved here, instead of this we have a real full duplex connection and a server push.

The GlassFish implementation

Like the Jetty implementation, GlassFish uses a Servlet to define an entry point, too. But instead of involving a new interface we use an standard HttpServlet and register a WebSocket adapter (ChatApplication) on first initialisation.

public class WebSocketChatServlet extends HttpServlet {
	private final ChatApplication app = new ChatApplication();
	 @Override
	public void init(ServletConfig config) throws ServletException {
 		   WebSocketEngine.getEngine().register(app);
 	 }
 }											Listing 5

 public class ChatApplication extends WebSocketApplication {
	@Override
	public WebSocket createSocket(WebSocketListener... listeners)
			throws IOException {
		return new ChatSocket(listeners);
	}

	@Override
        public void onMessage(WebSocket socket, DataFrame frame) throws IOException {
		final String data = frame.getTextPayload();
		for (final WebSocket webSocket : getWebSockets()) {
			try {
				webSocket.send(data);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}										Listing 6	

The “WebSocketApplication” class is an adapter, but hiding most of the WebSocket specific code. Extending this class means “overwrite the WebSocket creation and the message handling”. The ChatSocket itself is extending a “BaseServerWebSocket” and does not contain any specific code or methods to override. Instead, the “BaseServerWebSocket” is delegating all “onMessage, onOpen, onClose” calls to the WebSockat adapter (WebSocketApplication).

The “createSocket” method (line 3) in this example is simply associating a socket to a client.

The “onMessage” method in line 9 differs to the Jetty example. While we implemented the WebSocket methods in the previous example we now receive the socket as a parameter. Furthermore we do not define an “onTextMessage” or “onBinaryMessage” interface, we simply use the data frame with the payload.

In line 10 you can see that we call “getTextPaylod” to receive our chat text message. Alternatively you can use the “getBinaryPayload” method which gives you the binary payload similar to the “onBinaryMessage” interface in Jetty.

In listing 4, line 9 (Jetty WebSocket) we iterated on our user set. Here (listing 6) we do not have to handle a self created set because the WebSocketApplication provides already a set of all connected WebSockets (listing 6, line 11). Take this set and notify (webSocket.send(message); line 13) all connected clients.

Running the Jetty example

To run the Jetty example is quite simple. Go to the root folder of the project containing the pom.xml and type mvn clean install jetty:run. This will install all missing dependencies, deploy the web-project, and run the Jetty server. Now you can open the chat application on http://localhost:8080/html5-webapp-jetty/.

Running the GlassFish example

To run the GlassFish example, some additional steps are required. The provided pom.xml in the project root folder creates only a war file (mvn build). The created war file can be found in the target folder of your project. To run it you need an installed GlassFish 3.1 and you have to enable the WebSocket support. To do this, type:
asadmin set configs.config.server-config.network-config.protocols.protocol.http-listener-1.http.websockets-support-enabled=true
Now you can deploy your application and test it on http://localhost:8080/html5-webapp-glassfish/.

Conclusion:

WebSockets are a very promising approach for real-time web applications. The Client API will be standardized by W3C and the WebSockets Protocol by IETF, so you have an open and a potentially wide spread support on client and on server side. But currently the major problem is the missing uniform serverside API. You may risk that your WebSocket application will not run without changes on later server versions. Furthermore you cant switch from one server provider to an other without changing essential parts of your application.

Legacy
Published at DZone with permission of its author, Andy Moncsek.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Caspar MacRae replied on Sun, 2011/07/03 - 5:44am

Nice article, I've been meaning to look at Jetty's websocket support again for a while, thanks.

One trivial change; the form works much better with the onsubmit moved from <input> to <form> (with return false; to prevent submission) and autfocus on the <input>

		<form name="message" action="" onsubmit="sendMessage();return false;">
<input name="usermsg" type="text" id="message" size="63" autofocus="true" /> <input
type="button" name="submitmsg" value="Send..." />
</form>

 

darryl west replied on Sun, 2011/07/03 - 7:13pm

good article.  a small fix: you should change:

http://localhost:8080/html5-webapp-jetty/  --> http://localhost:8080/html5-jetty-webapp/

and a suggestion, changing "localhost:8080" to this...

  var url = 'ws://' + window.location.host + '/html5-jetty-webapp/WebSocketChat/anything';   
  ws = new WebSocket( url );

    ....

enables testing from multiple machines, which a bit more useful...

Jex Json replied on Mon, 2011/07/04 - 9:07am

hi,thanks for sharing,i have some doubt about your program,why don't you use pushlet framwork to build your program?    thk

Andy Moncsek replied on Mon, 2011/07/04 - 9:34am in response to: Jex Json

Hi, my intention was to write about WebSockets and a chat application is a good example for that. Thank you for reading

Martin Frys replied on Fri, 2011/07/08 - 7:51am

Hi,
nice article,
thanks
small comment: it didn't work in firefox 5.0, I had to enable Websockets:
type url about:config
and change the following properties:
network.websocket.enabled=true
network.websocket.override-security-block=true

Madhan Ksmy replied on Tue, 2011/07/26 - 3:08am

Hi ,

Gr8 article.. seen websocket in action by trying out in glassfish. But onething i didn get is the significance of  ChatApplication class , i mean, when tracing the flow i found that socket engine is initialized in the servlet and doesn't seems to override WS funstions (implemented in chatapplication class ).Not sure if i had missed something. (But the Chat app works like a Charm !)

A. Crest replied on Tue, 2011/11/15 - 7:35am

Hello, in Firefox 8, they renamed the "WebSocket" object into "MozWebSocket" ! Rgds

Jin-oh Jeong replied on Thu, 2011/12/08 - 12:27am

Thank you for your posting.

It is great, but the example didn't work as it was. I had to make some changes. First, the repository in the pom.xml is obsolete. It doesn't have the dependencies. So, I have to add an extra repository.

Second, the example doesn't work in the latest browsers.I'm using Firefox 8 and Chrome15 and they are supporting the websocket version 8 but The jetty server version you defined in your pom.xml doesn't support websocket version 8.

I had to change the jetty version into the latest one.(8.1.0.RC1) Sequentially, that required the version-up of jetty-maven-plugin and maven3.

Finally, I could run the example and now I am very happy :)

Thanks again.

 

Rk Murthy replied on Fri, 2012/01/27 - 8:32am

Hi, 

     The example given in this is not working for me on Glassfish server 3.1.

     I am getting the following error.

           java.io.IOException: Missing required headers for WebSocket negotiation.

 

      Can anybody help how to resolve this.  I also enabled websocket in glassfish server.

 

 

Mehmet Sahsu replied on Wed, 2012/02/22 - 9:05am

hello 

when i run jetty websocket application 

i took

WebSocket is not defined
... = new WebSocket("ws://localhost:8080/html5-jetty-webapp/WebSocketChat/anything"...
error and

ws is undefinedsendMessage()/html5...webapp/ (line 35)onclick()onclick (line 2)event = click clientX=868, clientY=416
ws.send(message);

can you clarify me i cant find the problem

thank you

 

Marco Falsitta replied on Fri, 2012/05/18 - 8:32am in response to: Jin-oh Jeong

Dear Jin-oh, does the pom.xml requires more settings beside what you wrote... I have changed to the ver last one (8.1.3.v20120416) but I still get a Unsupported Websocket version: 13 with Chrome and FireFox.. it only works with Safari. Thanks.

Carla Brian replied on Mon, 2012/05/21 - 6:31pm

This is a reliable application. I have to learn first about Jetty and Glassfish. - James Stuckey

Carla Brian replied on Sun, 2012/05/27 - 6:09am

This is useful for me. I need to practice more on this. -Steven C Wyer

Karthik Sundaram replied on Thu, 2012/06/28 - 3:04pm

Thanks! this got me started on websockets!  much easier to just start developing than jWebSocket or socket.io.

Ayaz Khatri replied on Thu, 2012/11/29 - 2:24am

 hello how can i  run this application on tomcat server.?

Thanks


Sridhar Sarnobat replied on Tue, 2012/12/25 - 11:56pm in response to: Marco Falsitta

Thanks for the info, I have this problem too. Can someone suggest what to do to get it to work in Chrome?

EDIT: This worked for me:

<jetty.version>8.1.0.RC5</jetty.version>

Sujit Keng replied on Wed, 2013/01/02 - 6:12am

Hey Andy Moncsek

I am New to the Web-socket...

will you tell me what changes I need to do to run this code in chrome 23.0.1271.97 version..

I am using glassfish server 3.1.1

Sujit Keng replied on Sat, 2013/01/05 - 3:38pm in response to: Mehmet Sahsu

Hey Friend...

Have you got the solution of this problem .... I need your help...

coz I also got the same problem ..

please reply ..

I am waiting ..


Sridhar Sarnobat replied on Sat, 2013/06/29 - 11:10pm

If you have groovy, try my example using Jetty:

https://github.com/sarnobat/jetty-websocket-groovy

Tanuja Phadke replied on Sun, 2013/12/01 - 2:51pm in response to: darryl west

 hello,

I copied the jar file and followed the exact steps as mentioned above . I see the webpage with username as "anonymous". But when I type some message and click send...  nothing shows up in area 2. What am I doing wrong. ?

Thanks ,


Tanuja Phadke replied on Sun, 2013/12/01 - 2:54pm

 

 hello,

I copied the jar file and followed the exact steps as mentioned above . I see the webpage with username as "anonymous". But when I type some message and click send...  nothing shows up in area 2.(the message output area. )  What am I doing wrong. ? Can someone help.

Thanks ,

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.