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.
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>https://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.