Accueil > Jetty, WebSockets > WebSockets with Embedding Jetty [step2]

WebSockets with Embedding Jetty [step2]


In [step1] we have seen how WebSockets could be used in a chat application and introduce WebSockets component on client/server side.

In this article we will create chat application on client side with JavaScript WebSocket component. Google Chrome implements WebSocket so our chat application will be tested with this browser.

Jetty distribution provides a sample with chat.html. This article will explain step by step how to create chat application on client side with chat.html. In the next article [step3], we will create a Jetty server which will start to the 8081 port. The Chat WebSocket server will be available at ws://localhost:8081. You can notice
that URL is ws://localhost:8081/ and not http://localhost:8081/. ws:// is WebSocket protocol. For secured protocol (like https://), wss:// must be used.

Once WebSockets will be finished on client side (explained in this article) and server side (explained in [step3]), chat application will look like this :

Download

You can display (and download) chat.html explained in this article.

WebSocket Chat – Overview

Before explaining step by step how to create our chat application with chat.html, it’s interesting to see in action the chat features provided by Jetty distribution.

Check WebSocket support

The Jetty chat.html test if Web Browser supports JavaScript WebSocket component. For instance if you will open chat.html with FireFox you will see this alert :

Chat connection

When you will open chat.html with Google Chrome, you will see that the chat application will display Username field and « Join » button :

To start a chat, you must fill Username field with an User and click on « Join » button or press Enter :

This action creates a JavaScript WebSocket which connect to the server. « Username » label is changed with « Chat » label, « Join » button label is changed with « Send » and Chat area display the new User who has joined the chat :

If you open a new chat application in new tab and you join the chat, you can see that :

If you go the first chat application you will see that the second user has joined the chat :

Chat communication

If the first user type « hello » and press Enter or click on « Send » button, message is sent :

You can check that the first user has received « hello » message from him :

You can check that the second user has received « hello » message from the first user :

UI Chat Application

At this step we create a chat.html file to create the UI chat application.

UI Chat Application – HTML

UI chat application is composed with 3 areas :

  • an HTML div which contains messages. This div is identified with « chat » id.
  • an HTML div which displays Username field and « Join » button. This div is identified with « join » id. This div is displayed at first time. Once User is connected after joining the chat, this div is hidden.
  • an HTML div which displays Chat field and « Send » button. This div is identified with « joined » id. This div is hidden at first time. Once User is connected after joining the chat, this div is displayed.

Create a chat.html file with this content :

<html>
<head>
<title>WebSocket Chat</title>
</head>
<body>
	<div id="chat"></div>
	<div id="input">
		<div id="join">
			Username:&nbsp;
			<input id="username" type="text" /><input id="joinB"
				class="button" type="submit" name="join" value="Join" />
		</div>
		<div id="joined" class="hidden">
			Chat:&nbsp;<input id="phrase" type="text" /> <input id="sendB"
				class="button" type="submit" name="join" value="Send" />
		</div>
	</div>
</body>
</html>

CSS hidden class will manage the hidden of the div when CSS styles will be added. If you open chat.html with Google Chrome, you will see that :

UI Chat Application – CSS Styles

Now you can style chat application by modifying chat.html like this :

<html>
<head>
<title>WebSocket Chat</title>
<style type='text/css'>
div {
	border: 0px solid black;
}

div#chat {
	clear: both;
	width: 40em;
	height: 20ex;
	overflow: auto;
	background-color: #f0f0f0;
	padding: 4px;
	border: 1px solid black;
}

div#input {
	clear: both;
	width: 40em;
	padding: 4px;
	background-color: #e0e0e0;
	border: 1px solid black;
	border-top: 0px
}

input#phrase {
	width: 30em;
	background-color: #e0f0f0;
}

input#username {
	width: 14em;
	background-color: #e0f0f0;
}

div.hidden {
	display: none;
}

span.from {
	font-weight: bold;
}
</style>
</head>
<body>
	<div id="chat"></div>
	<div id="input">
		<div id="join">
			Username:&nbsp;
			<input id="username" type="text" /><input id="joinB"
				class="button" type="submit" name="join" value="Join" />
		</div>
		<div id="joined" class="hidden">
			Chat:&nbsp;<input id="phrase" type="text" /> <input id="sendB"
				class="button" type="submit" name="join" value="Send" />
		</div>
	</div>
</body>
</html>

If you open chat.html with Google Chrome, you will see that :

Chat logic with JavaScript WebSocket

At this step we can manage the logic of the chat application with JavaScript by using JavaScript WebSocket component. The WebSocket interface defines several methods :

[Constructor(in DOMString url, in optional DOMString protocols),
 Constructor(in DOMString url, in optional DOMString[] protocols)]
interface WebSocket {
  readonly attribute DOMString url;

  // ready state
  const unsigned short CONNECTING = 0;
  const unsigned short OPEN = 1;
  const unsigned short CLOSING = 2;
  const unsigned short CLOSED = 3;
  readonly attribute unsigned short readyState;
  readonly attribute unsigned long bufferedAmount;

  // networking
           attribute Function? onopen;
           attribute Function? onerror;
           attribute Function? onclose;
  readonly attribute DOMString extensions;
  readonly attribute DOMString protocol;
  void close([Clamp] in optional unsigned short code, in optional DOMString reason);

  // messaging
           attribute Function? onmessage;
           attribute DOMString binaryType;
  void send(in DOMString data);
  void send(in ArrayBuffer data);
  void send(in Blob data);
};
WebSocket implements EventTarget;

Scripts before loading page.

At this step, we define JavaScript functions which manages chat application with JavaScript WebSocket. The Chat WebSocket server will be available at ws://localhost:8081. After the HTML content :

<html>
<head>
<title>WebSocket Chat</title>

Add this script :

<script type="text/javascript">
	if (!window.WebSocket)
		alert("WebSocket not supported by this browser");

	function $() {
		return document.getElementById(arguments[0]);
	}
	function $F() {
		return document.getElementById(arguments[0]).value;
	}

	function getKeyCode(ev) {
		if (window.event)
			return window.event.keyCode;
		return ev.keyCode;
	}
	
	var room = {
		join : function(name) {
			this._username = name;
			//var location = document.location.toString().replace('http://',
			//		'ws://').replace('https://', 'wss://');
			var location = "ws://localhost:8081/"
			this._ws = new WebSocket(location);
			this._ws.onopen = this._onopen;
			this._ws.onmessage = this._onmessage;
			this._ws.onclose = this._onclose;
			this._ws.onerror = this._onerror;
		},

		chat : function(text) {
			if (text != null && text.length > 0)
				room._send(room._username, text);
		},

		_onopen : function() {			
			$('join').className = 'hidden';
			$('joined').className = '';
			$('phrase').focus();
			room._send(room._username, 'has joined!');
		},

		_onmessage : function(m) {
			if (m.data) {
				var c = m.data.indexOf(':');
				var from = m.data.substring(0, c).replace('<', '&lt;').replace(
						'>', '&gt;');
				var text = m.data.substring(c + 1).replace('<', '&lt;')
						.replace('>', '&gt;');

				var chat = $('chat');
				var spanFrom = document.createElement('span');
				spanFrom.className = 'from';
				spanFrom.innerHTML = from + ':&nbsp;';
				var spanText = document.createElement('span');
				spanText.className = 'text';
				spanText.innerHTML = text;
				var lineBreak = document.createElement('br');
				chat.appendChild(spanFrom);
				chat.appendChild(spanText);
				chat.appendChild(lineBreak);
				chat.scrollTop = chat.scrollHeight - chat.clientHeight;
			}
		},

		_onclose : function(m) {
			this._ws = null;
			$('join').className = '';
			$('joined').className = 'hidden';
			$('username').focus();
			$('chat').innerHTML = '';
		},

		_onerror : function(e) {
			alert(e);
		},
		
		_send : function(user, message) {
			user = user.replace(':', '_');
			if (this._ws)
				this._ws.send(user + ':' + message);
		}
	};
</script>

Here some explanation about this script :

  1. Before loading the page, we must check if WebSocket is supported by the Web Browser :
    if (!window.WebSocket)
      alert("WebSocket not supported by this browser");
    
  2. room JavaScript variable (var room = {…}) is declared to define 2 methods :
    1. room.join(name) which is called when user clicks on « Join » button or types Enter in the username field. This method create a JavaScript WebSocket with the URL ws://localhost:8081/ :
      var location = "ws://localhost:8081/"
      this._ws = new WebSocket(location);

      You can notice that URL is ws://localhost:8081/ and not http://localhost:8081/. ws:// is WebSocket protocol. For secured protocol (like https://), wss:// must be used. After WebSocket creation, room.join(name) defines a private function for each methods of WebSocket :

      this._ws.onopen = this._onopen;
      this._ws.onmessage = this._onmessage;
      this._ws.onclose = this._onclose;
      this._ws.onerror = this._onerror;

      Here description of each methods :

      • WebSocket#onopen is called once User is connected. This function hide the ‘join’ div, display the ‘joined’ div and send a message to tell that user has joined :
        _onopen : function() {			
          $('join').className = 'hidden';
          $('joined').className = '';
          $('phrase').focus();
          room._send(room._username, 'has joined!');
        }

        send message is done by calling room._send which sends a message with WebSocket#send(in DOMString data) like this :

        _send : function(user, message) {
        	user = user.replace(':', '_');
        	if (this._ws)
        		this._ws.send(user + ':' + message);
        }
      • WebSocket#onmessage is called when message is received. This function add the received message in the ‘chat’ div. :
        _onmessage : function(m) {
        	if (m.data) {
        		var c = m.data.indexOf(':');
        		var from = m.data.substring(0, c).replace('<', '&lt;').replace('>', '&gt;');
        		var text = m.data.substring(c + 1).replace('<', '&lt;').replace('>', '&gt;');
        
        		var chat = $('chat');
        		var spanFrom = document.createElement('span');
        		spanFrom.className = 'from';
        		spanFrom.innerHTML = from + ':&nbsp;';
        		var spanText = document.createElement('span');
        		spanText.className = 'text';
        		spanText.innerHTML = text;
        		var lineBreak = document.createElement('br');
        		chat.appendChild(spanFrom);
        		chat.appendChild(spanText);
        		chat.appendChild(lineBreak);
        		chat.scrollTop = chat.scrollHeight - chat.clientHeight;
        	}
        }
        
      • WebSocket#onclose is called when WebSocket is closed. This function displays the ‘join’ div and hides the ‘joined’ div :
        _onclose : function(m) {
        	this._ws = null;
        	$('join').className = '';
        	$('joined').className = 'hidden';
        	$('username').focus();
        	$('chat').innerHTML = '';
        }
    2. room.chat(text) which is called when user clicks on « Send » button or type Enter in the phrase field (available when user has joined the chat). This method sends the content of the ‘phrase’ field with WebSocket#send(in DOMString data) like this :
      chat : function(text) {
      	if (text != null && text.length > 0)
      		room._send(room._username, text);
      }

Scripts after loading page.

At this step room variable which defines room.join(name) which create WebSocket and room.chat(text) which use WebSocket to send message is finished. Now we can call this room variable in click event button (« Join » and « Send ») and Enter event of the fields (« username » and « phrase »). To add events to the UI widgets button and input fields, add this script after the creation of the UI (after the last HTML div) :

<script type="text/javascript">	
		// 1) When user is NOT connected, 'join' div is displayed with Username input + Join button
		$('username').setAttribute('autocomplete', 'OFF');
		// Enter in the username field, opens Chat WebSockets
		$('username').onkeyup = function(ev) {
			var keyc = getKeyCode(ev);
			if (keyc == 13 || keyc == 10) {
				room.join($F('username'));
				return false;
			}
			return true;
		};
		// "Join" button click opens Chat WebSockets
		$('joinB').onclick = function(event) {
			room.join($F('username'));
			return false;
		};
		
		// 2) When user is connected, 'phrase' div is displayed with Chat input + Send button		
		$('phrase').setAttribute('autocomplete', 'OFF');
		// Enter in the 'phrase' field send the message
		$('phrase').onkeyup = function(ev) {
			var keyc = getKeyCode(ev);
			if (keyc == 13 || keyc == 10) {
				room.chat($F('phrase'));
				$('phrase').value = '';
				return false;
			}
			return true;
		};
		// "Send" button click send the message
		$('sendB').onclick = function(event) {
			room.chat($F('phrase'));
			$('phrase').value = '';
			return false;
		};
	</script>

This scripts is not very difficult to understand. It add events to the input fields and buttons to call the room.join(name) or room.chat(text).

chat.html

Here the full code of the chat.html :

<html>
<head>
<title>WebSocket Chat</title>
<script type="text/javascript">
	if (!window.WebSocket)
		alert("WebSocket not supported by this browser");

	function $() {
		return document.getElementById(arguments[0]);
	}
	function $F() {
		return document.getElementById(arguments[0]).value;
	}

	function getKeyCode(ev) {
		if (window.event)
			return window.event.keyCode;
		return ev.keyCode;
	}
	
	var room = {
		join : function(name) {
			this._username = name;
			//var location = document.location.toString().replace('http://',
			//		'ws://').replace('https://', 'wss://');
			var location = "ws://localhost:8081/"
			this._ws = new WebSocket(location);
			this._ws.onopen = this._onopen;
			this._ws.onmessage = this._onmessage;
			this._ws.onclose = this._onclose;
			this._ws.onerror = this._onerror;
		},

		chat : function(text) {
			if (text != null && text.length > 0)
				room._send(room._username, text);
		},

		_onopen : function() {			
			$('join').className = 'hidden';
			$('joined').className = '';
			$('phrase').focus();
			room._send(room._username, 'has joined!');
		},

		_onmessage : function(m) {
			if (m.data) {
				var c = m.data.indexOf(':');
				var from = m.data.substring(0, c).replace('<', '&lt;').replace(
						'>', '&gt;');
				var text = m.data.substring(c + 1).replace('<', '&lt;')
						.replace('>', '&gt;');

				var chat = $('chat');
				var spanFrom = document.createElement('span');
				spanFrom.className = 'from';
				spanFrom.innerHTML = from + ':&nbsp;';
				var spanText = document.createElement('span');
				spanText.className = 'text';
				spanText.innerHTML = text;
				var lineBreak = document.createElement('br');
				chat.appendChild(spanFrom);
				chat.appendChild(spanText);
				chat.appendChild(lineBreak);
				chat.scrollTop = chat.scrollHeight - chat.clientHeight;
			}
		},

		_onclose : function(m) {
			this._ws = null;
			$('join').className = '';
			$('joined').className = 'hidden';
			$('username').focus();
			$('chat').innerHTML = '';
		},

		_onerror : function(e) {
			alert(e);
		},
		
		_send : function(user, message) {
			user = user.replace(':', '_');
			if (this._ws)
				this._ws.send(user + ':' + message);
		}
	};
</script>
<style type='text/css'>
div {
	border: 0px solid black;
}

div#chat {
	clear: both;
	width: 40em;
	height: 20ex;
	overflow: auto;
	background-color: #f0f0f0;
	padding: 4px;
	border: 1px solid black;
}

div#input {
	clear: both;
	width: 40em;
	padding: 4px;
	background-color: #e0e0e0;
	border: 1px solid black;
	border-top: 0px
}

input#phrase {
	width: 30em;
	background-color: #e0f0f0;
}

input#username {
	width: 14em;
	background-color: #e0f0f0;
}

div.hidden {
	display: none;
}

span.from {
	font-weight: bold;
}
</style>
</head>
<body>
	<div id="chat"></div>
	<div id="input">
		<div id="join">
			Username:&nbsp;
			<input id="username" type="text" /><input id="joinB"
				class="button" type="submit" name="join" value="Join" />
		</div>
		<div id="joined" class="hidden">
			Chat:&nbsp;<input id="phrase" type="text" /> <input id="sendB"
				class="button" type="submit" name="join" value="Send" />
		</div>
	</div>
	
	<script type="text/javascript">	
		// 1) When user is NOT connected, 'join' div is displayed with Username input + Join button
		$('username').setAttribute('autocomplete', 'OFF');
		// Enter in the username field, opens Chat WebSockets
		$('username').onkeyup = function(ev) {
			var keyc = getKeyCode(ev);
			if (keyc == 13 || keyc == 10) {
				room.join($F('username'));
				return false;
			}
			return true;
		};
		// "Join" button click opens Chat WebSockets
		$('joinB').onclick = function(event) {
			room.join($F('username'));
			return false;
		};
		
		// 2) When user is connected, 'phrase' div is displayed with Chat input + Send button		
		$('phrase').setAttribute('autocomplete', 'OFF');
		// Enter in the 'phrase' field send the message
		$('phrase').onkeyup = function(ev) {
			var keyc = getKeyCode(ev);
			if (keyc == 13 || keyc == 10) {
				room.chat($F('phrase'));
				$('phrase').value = '';
				return false;
			}
			return true;
		};
		// "Send" button click send the message
		$('sendB').onclick = function(event) {
			room.chat($F('phrase'));
			$('phrase').value = '';
			return false;
		};
	</script>
		
</body>
</html>

Conclusion

In this article we have implemented chat application on client side with JavaScript WebSocket. In the next step [step3] we will implement WebSockets on server side with Jetty-WebSocket and we will start the server with a Java main with Embedding Jetty.

Catégories :Jetty, WebSockets
  1. Victoria
    septembre 21, 2011 à 5:38

    This is a very nice tutorial.
    I just don’t understand why can the user join the chat when there is no server running yet, that is pure HTML code. These lines
    var location = « ws://localhost:8081/ »
    this._ws = new WebSocket(location);
    won’t do anything until there is a server running on localhost:8081

    • septembre 22, 2011 à 7:52

      Hi Victoria,

      Goal of my articles was to provide basic sample with WebSocket on client and server side but I know there is a lot of topics that I have not explained. For your server side not available, I have seen that in the WebSocket API http://www.w3.org/TR/websockets/#websocket this sentence :

      ————————————————————————————————————————————————-
      If the « establish a WebSocket connection » algorithm fails, it triggers the « fail the WebSocket connection » algorithm, which then invokes the « close the WebSocket connection » algorithm, which then establishes that the « WebSocket connection is closed », which fires the close event as described below.
      ————————————————————————————————————————————————-

      I have tried this explanation with this code :

      ————————————————————————————————————————————————-

      var ws =new WebSocket(‘ws://BAD_URL »);
      ws.onclose = function(closeEvent) {
      alert(closeEvent.wasClean);
      };

      ————————————————————————————————————————————————-

      and it displays false on the alert. I think you could improve the chat application with test of ‘wasClean’ to check if the close is done because connection cannot be etablished (wasClean = false) or if close os done because user quit the chat application.

      Regards Angelo

  2. Nenad S.
    décembre 2, 2011 à 3:58

    Hello Angelo,

    The example works perfectly. Thank you.
    Just a small comment – it can also work under Firefox with just a few small changes:
    ….
    if (!window.WebSocket && !window.MozWebSocket) {
    alert(« WebSocket not supported by this browser »);
    }

    …. and then later…..

    if (window.WebSocket) {
    this._ws = new WebSocket(location);
    } else if (window.MozWebSocket) {
    this._ws = new MozWebSocket(location);
    } else {
    alert(« WebSocket not supported by this browser »);
    //do some smart error handling 🙂
    }
    ……

    This will give impression that these are really different « users » since they are « talking » from the different browsers. Maybe somebody will find it useful.

    Best regards,

    Nenad S.

  3. kavya
    février 24, 2012 à 3:53

    It’s not working for me! 😦 I’m trying it in google chrome….It does not say « websocket not supported » but it shows only till 4th figure….user cannot join

    • février 24, 2012 à 8:08

      Hi kawa,

      Sorry I cannot help you. Check that URL server is well.
      Good luck.

      Rgards Angelo

  4. sat
    septembre 17, 2012 à 8:36

    Hi Angelo,

    Thanks for the great article about websocket.In works fine in the local machine.But when i tried to access the application from the another machine which is in the LAN through ip.Apache Server works fine.But i cant create the websocket using jetty server.When i click the join button it shows the alert as « Object Event ».

  1. juillet 25, 2011 à 10:45
  2. juillet 26, 2011 à 5:41
  3. juillet 26, 2011 à 9:46
  4. juillet 26, 2011 à 3:05

Laisser un commentaire