Java WebSockets Tutorial with Glassfish and Jetty

In this tutorial I will show you how to make a simple chat application running on Glassfish 4. I will use two APIs to accomplish this: Jetty and JSON API.

We will first start with a web based client. Look at the image below. It has one field for your username, one field where you can type in your chat message and on text area which holds the conversation so far.

WebSocket chat client

Web-Client and Javascript

We can use WebSocket in JavaScript to create a full duplex connection between the client and the server. The JavaScript Websocket has 3 methods

onopen – this is called when the client and the server create a connection

onmessage – this method is executed when the server sends a message to the client

onclose – this method is called when the connection between the client and the server is destroyed




After the user hits the send button the username and the message itself are converted into JSON format and sent to the server.

You will find the complete code of the client below:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html">

<h:head>
	<title>WebSockets Chat</title>
	<meta name="author" content="javatutorial.net" />
	<script type="text/javascript" charset="utf-8" src="js/jquery-1.3.2.js"></script>
	<link type="text/css" rel="stylesheet" href="css/style.css" />
	<script type="text/javascript">
	var ws;

	$(document).ready(
			function() {
				ws = new WebSocket("ws://localhost:8080/chat");
				ws.onopen = function(event) {
					
				}
				ws.onmessage = function(event) {
					var $textarea = $('#messages');
					var json = JSON.parse(event.data);
					$textarea.val($textarea.val() + json.username + ": " + json.message + "\n");
					$textarea.animate({
						scrollTop : $textarea.height()
					}, 1000);
				}
				ws.onclose = function(event) {
					
				}

			});

	function sendMessage() {
		var message = {
			    "username": $('#username').val(),
			    "message": $('#message').val() 
			}
		ws.send(JSON.stringify(message));
		$('#message').val('');
	}
	</script>
</h:head>
<h:body>
	<div id="body">
		<div id="menu">
			<p class="welcome">
				User: <input id="username" value="anonymous" />
			</p>
			<div style="clear: both"></div>
		</div>

		<div id="chatbox">
			<textarea id="messages" rows="16" cols="50" readonly="readonly"></textarea>
		</div>

		<form name="message" action="">
			<input name="usermsg" type="text" id="message" size="63" /> <input
				type="button" name="submitmsg" value="Send..."
				onclick="sendMessage();" />
		</form>
	</div>
</h:body>
</html>

Implementing the Websocket Server

I will use Glassfish 4.1 to deploy the WebSocket server. We will use Jetty 9.x as Websocket server and  Glassfish has already built in Jetty.  Another API we will use is the JSON API which is also part of Glassfish 4. Look at the Maven pom file below for needed dependencies

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>net.javatutorial</groupId>
	<artifactId>ChatServer</artifactId>
	<version>0.0.1</version>
	<packaging>war</packaging>

	<name>ChatServer</name>
	<url>http://javatutorial.net</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-api</artifactId>
			<version>6.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-server</artifactId>
			<version>9.2.7.v20150116</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.eclipse.jetty.websocket</groupId>
			<artifactId>javax-websocket-server-impl</artifactId>
			<version>9.2.7.v20150116</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-annotations</artifactId>
			<version>9.2.7.v20150116</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-webapp</artifactId>
			<version>9.2.7.v20150116</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
    		<groupId>javax.json</groupId>
    		<artifactId>javax.json-api</artifactId>
    		<version>1.0</version>
    		<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<inherited>true</inherited>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
			<plugin>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.3</version>
				<configuration>
					<webXml>src/main/webapp/WEB-INF/web.xml</webXml>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

The ChatMessage object holds the username and the message strings provides a Decoder and Encoder to convert the structure into JSON format.

package net.javatutorial.chatserver.pojos;

import java.io.StringReader;
import java.util.Collections;

import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonReaderFactory;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

public class ChatMessage {

	public static class MessageEncoder implements Encoder.Text<ChatMessage> {
		@Override
		public void init(EndpointConfig config) {
		}

		@Override
		public String encode(ChatMessage message) throws EncodeException {
			return Json.createObjectBuilder()
					.add("username", message.getUsername())
					.add("message", message.getMessage()).build().toString();
		}

		@Override
		public void destroy() {
		}
	}

	public static class MessageDecoder implements Decoder.Text<ChatMessage> {
		private JsonReaderFactory factory = Json
				.createReaderFactory(Collections.<String, Object> emptyMap());

		@Override
		public void init(EndpointConfig config) {
		}

		@Override
		public ChatMessage decode(String str) throws DecodeException {
			ChatMessage message = new ChatMessage();

			JsonReader reader = factory.createReader(new StringReader(str));
			JsonObject json = reader.readObject();
			message.setUsername(json.getString("username"));
			message.setMessage(json.getString("message"));

			return message;
		}

		@Override
		public boolean willDecode(String str) {
			return true;
		}

		@Override
		public void destroy() {
		}
	}

	private String username;
	private String message;

	public ChatMessage() {
	}

	public ChatMessage(String username, String message) {
		super();
		this.username = username;
		this.message = message;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

}

Finally we will need the Websocket server endpoint.

package net.javatutorial.chatserver.sockets;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import net.javatutorial.chatserver.pojos.ChatMessage;
import net.javatutorial.chatserver.pojos.ChatMessage.MessageDecoder;
import net.javatutorial.chatserver.pojos.ChatMessage.MessageEncoder;

@ServerEndpoint(value = "/chat", encoders = { MessageEncoder.class }, decoders = { MessageDecoder.class })
public class ChatServerEndpoint {
	private static final Set<Session> sessions = Collections
			.synchronizedSet(new HashSet<Session>());

	@OnOpen
	public void onOpen(Session session) {
		sessions.add(session);
	}

	@OnClose
	public void onClose(Session session) {
		sessions.remove(session);
	}

	@OnMessage
	public void onMessage(ChatMessage message, Session client)
			throws IOException, EncodeException {
		for (Session session : sessions) {
			session.getBasicRemote().sendObject(message);
		}
	}
}

You can download the complete source code and eclipse project here.

Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Digg thisPin on PinterestShare on RedditShare on StumbleUponPrint this pageEmail this to someone

Leave a Reply