jetty

Jetty WebSocket Example

1.Introduction

This article is about WebSockets with Jetty. In this example we will give brief information on WebSockets and show how to implement WebSocket Servers and Clients using Jetty WebSocket APIs. In addition to these, an example HTML+JavaScript client interacting with the Server through WebSockets will be provided.

2.WebSockets

WebSocket is a standard protocol facilitating full-duplex communication over a single TCP socket. The protocol is designed primarily for Web Browsers and Web Servers however it can be applied to other server-client integration cases.

The concept of WebSockets has been emerged due to the limitations of HTTP. HTTP is a pull based (request-response) protocol; which means that server cannot directly push messages to the client. The alternatives aiming to tackle this problem (TCP sockets, Comet, Long-polling etc.) provide workaround solutions causing side effects of their own.

WebSocket protocol is based on existing web application technologies. A WebSocket connection utilizes the same port with the application server(80 or 8080 for instance), hence it is less likely to be blocked by firewalls. WebSocket connection between client and server is established by a handshake through an HTTP upgrade request. After successful handshake, the protocol is switched from HTTP to WebSocket.

 
WebSocket defines two prefixes for the server URI.

  • ws: for unsecure connections
    • Example: ws://example.codegeeks.com/websocketExample
  • wss: for secure connections
    • Example: wss://example.codegeeks.com/websocketExample

For the browser based applications, WebSocket protocol support has to be provided by the browser. Currently WebSockets are supported by almost all modern browsers including Chrome, Firefox, Internet Explorer and Safari.

Today WebSockets are widely used in the cases that servers need to push data to the web clients (online gaming, chat).

3.Jetty WebSocket APIs

WebSocket technology needs to be supported not only on the browser but also on the server side. Different platforms do have their own implementation for WebSockets both for server and client roles. Jetty is one of the platforms providing WebSocket API for both server and clients sides.

Jetty implements two alternative APIs for WebSocket development:

First of them is the JSR-356 compliant one. JSR-356 is the Java API for WebSocket specification which is included in Java EE 7. The spec defines an annotation based API as well as an listener interface based one.

The other alternative is Jetty’s own implementation: Jetty WebSocket API. This API had emerged before JSR-356 was released. The programming model of Jetty WebSocket API is very similar to JSR-356 based one. It also provides similar annotations and listener interfaces.

In the following subsections we expand on both alternatives.

3.1 Alternative 1: Jetty JSR-356 Implementation

JSR-356 has two different approaches for WebSocket implementation: One is Annotations based whereas the other is Interface based. In the Annotations approach, you have to decorate your POJOs with relevant annotations of the API. In the Interface approach, you need to implement the WebSocket Endpoint interface.

Both approaches are similar. The annotations in the first approach match to the methods to be implemented in the Interface approach. Here we explain only the annotations.

@ServerEndpoint:

ServerEndpoint is used to annotate a POJO classes as server side WebSockets. The value of the annotation determines the URL path of the WebSocket(similar to the servlet mappings in Java web applications):

An example can be seen below:

 @ServerEndpoint(value = "/example")
public class MySocket{
…}

@ClientEndpoint:

ServerEndpoint is used to annotate a POJO classes as client side WebSockets.

@ClientEndpoint
public class MySocket{
…}

@OnOpen:

OnOpen annotates the method that handles the event when a connection is established. JSR-356 does not mandate anything on naming of annotated methods, so we can name our methods as we like.

@OnOpen
public void onSessionOpened(Session session){
}

Session is the class that encapsulates the Socket connection session.

@OnMessage:

OnMessage is used to annotate the method that handles the incoming messages.

An example is below:

@OnMesssage
public String onMessageReceived(String message, Session session){
}

@OnClose:

We can mark the method that handles the event fired when the socket connection is closed with OnClose annotation. An example usage is below:

@OnClose
public void onClose(Session session, CloseReason closeReason){
}

CloseReason is a class that encapsulates the termination reason along with a code.

@OnError:

OnError annotation defines the method that handles exceptions. An example usage is as follows:

@OnError 
public void onErrorReceived(Throwable t) {
}

ServerEndpoint is applicable on server side whereas ClientEndpoint is applicable only on client side. Other annotations are applicable for both sides.

A last remark is that, the signatures (parameter and return types) of the annotated methods must be one of the signatures allowed by JSR-356. The examples presented above are legal according to the spec. JavaDoc presents the allowed method signatures for each annotation.

3.2 Alternative 2: Jetty WebSocket API Implementation

In addition to JSR-356 support, Jetty also provides its own API. The reason that Jetty has two APIs on WebSockets is that JSR-356 was released after Jetty had released its own. JSR-356 API is heavily inspired by Jetty’s.

From the developer’s point of view Jetty API is very similar to the JSR-356’s, with minor differences.

Jetty WebSocket API needs initialization of a Servlet which extends org.eclipse.jetty.websocket.servlet.WebSocketServlet class. In JSR-356 implementation this is not needed. In this servlet, we configure the servlet socket class, which resembles the annotated Server Socket class implementation of JSR-356.

In the Example section, we will show how we can configure this server socket at the Servlet implementation.

Jetty API provides three alternative approaches for WebSocket development:

  • Annotation Based: Similar to JSR-356 annotations
  • Listener Based: Similar to JSR-356 listeners
  • Adapter Based: A convenience approach which eases Listener based implementation.

The easiest way is the Annotation approach. The annotation names (and classes) are different than JSR-356’s however they are almost of the same use.

@WebSocket

This annotation defines that the class is a WebSocket server. It is similar to the @ServletEndpoint of JSR-356 but we do not give the endpoint URL here. In addition to this, this annotation is not specific to the server side. Socket clients are also marked with this annotation.

@WebSocket
public class ExampleWebSocket{
}

@OnWebSocketConnect

This annotation defines that the method to be invoked when a connection is opened. It is similar to @OnOpen of JSR-356.

@OnWebSocketConnect
public void onConnect(Session session){
}

@OnWebSocketMessage

This annotation defines that the method to be invoked when a message is received. It is similar to @OnMessage of JSR-356.

@OnWebSocketMessage
public void onText(Session session, String message){
}

@OnWebSocketClose

This annotation defines that the method to be invoked when a connection is closed. It is similar to @OnClose of JSR-356.

@OnWebSocketClose
public void onClose(Session session, int status, String reason){
}

@OnWebSocketError

This annotation defines that the method to be invoked when a connection related error is thrown. It is similar to @OnError of JSR-356.

@OnWebSocketError
public void onError(Throwable error){
}

Similar to the JSR-356, the method names are not mandated by Jetty in this approach. The full list of allowed parameter types can be viewed in Jetty documentation.

3.3 Which API to Choose?

Both APIs offer similar features and programming approach. However there are subtle differences.

JSR-356 is specification based and standard. You can port easily your server sockets from Jetty to another Servlet container or Application server as long as the server supports JSR-356. In addition to this, programming with this API is a bit simpler. You do not need to configure a servlet with JSR-356 API. The downside of this alternative is, the documentation on Jetty side is missing and it seems less mature than the second alternative (but that might be just an impression).

Jetty WebSocket API is not standard, so you have to change your code when you change your Servlet Container. In addition to this, you have to code the servlet (some piece of boilerplate code) yourself. But Jetty API is more flexible allowing to easier control timeouts, paths, SSL etc. Another advantage is that, Jetty documentation of its own API is better than Jetty documentation on JSR-356.

At this point, I would humbly recommend using JSR-356 API if simpler configuration matters to you or if portability is a major concern. If you need to configure WebSocket parameters in detail and you do not need to port your WebSocket to another container, I would recommend Jetty WebSocket API.

Of course, this portability issue is only about porting WebSocket code from one container to another (from Jetty to Tomcat for example). WebSocket is a standardized protocol, so any kind of WebSocket client connect to any  implementation (JSR compliant or not) server without a problem.

In the following section we will provide examples of both APIs for server and client sides.

4.Example

In the example, we will first start with a simple WebSocket Server implementation that gets a text message from the client and echoes back converting the message to uppercase. We will provide both JSR-356 and Jetty API versions of these server side WebSocket implementations.

Later we will move on to the client part. We will implement a Java WebSocket clients interacting with this WebSocket server (again both JSR-356 based and Jetty API versions). Thereafter, we will provide another client example this time with HTML + JavaScript.

4.1 Environment

For the example, following programming environment is used:

  • For the Jetty WebSocket Server:
    • Java 7
    • Maven 3.x.y
    • Maven Jetty Plugin
    • Eclipse Luna as the IDE
  • For the the Jetty WebSocket Client:
    • Java 7
    • Maven 3.x.y
    • Eclipse Luna as the IDE
  • For the HTML WebSocket Client
    • A browser supporting WebSockets (Firefox 38.0.5 in this example)
    • Eclipse Luna as the IDE (used as HTML editor)

In this example, we will create a single Maven project in Eclipse and run it as a web application for the servers. The Java clients will be run as console applications and the HTML client will be deployed on the web application.

4.2 Creating the Maven Project

In order to create the Maven project on Eclipse, you can follow the steps below:

  1. Go to File -> New ->Other -> Maven Project
  2. Tick “Create a simple project” and press “Next”.
  3. Enter groupId as : com.javacodegeeks.snippets.enterprise
  4. Enter artifactId as : jetty-websocket-example
  5. Press “Finish”.

Now our Maven project is created.

4.3 Configuring the Maven Dependencies and Jetty Plugin

In order to configure the project, we need following dependencies:

First we need Jetty dependencies:

  • org.eclipse.jetty.websocket:jetty-server
  • org.eclipse.jetty.websocket:jetty-servlet

If you are implementing a WebSocket server with JSR-356 API we need:

  • org.eclipse.jetty.websocket:javax-websocket-server-impl

If we choose, Jetty API for WebSocket server we need:

  • org.eclipse.jetty.websocket:websocket-server

For the client side implementation we choose one of the following depending on we choose JSR-356 or the Jetty alternative:

  • org.eclipse.jetty.websocket:javax-websocket-client-impl
  • org.eclipse.jetty.websocket:websocket-client

The version we have used in this example is 9.2.11.v20150529.

In this example, we have added all of these dependencies to our pom.xml. Depending on the approach you choose, you can remove the unnecessary ones based on your your need.

 <dependencies>
		<!--Jetty dependencies start here -->
		<dependency>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-server</artifactId>
			<version>${jetty.version}</version>
		</dependency>

		<dependency>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-servlet</artifactId>
			<version>${jetty.version}</version>
		</dependency>
		<!--Jetty dependencies end here -->

		<!--Jetty Websocket server side dependencies start here -->



		<!--Jetty JSR-356 Websocket server side dependency -->
		<dependency>
			<groupId>org.eclipse.jetty.websocket</groupId>
			<artifactId>javax-websocket-server-impl</artifactId>
			<version>${jetty.version}</version>
		</dependency>

		<!--Jetty Websocket API server side dependency -->
		<dependency>
			<groupId>org.eclipse.jetty.websocket</groupId>
			<artifactId>websocket-server</artifactId>
			<version>${jetty.version}</version>
		</dependency>


		<!--Jetty Websocket server dependencies end here -->
		
		<!--Jetty Websocket client side dependencies start here -->

		

		<!--JSR-356 Websocket client side depencency  -->
		<dependency>
			<groupId>org.eclipse.jetty.websocket</groupId>
			<artifactId>javax-websocket-client-impl</artifactId>
			<version>${jetty.version}</version>
		</dependency>
		
		<!--Jetty Websocket API client side dependency -->
		<dependency>
			<groupId>org.eclipse.jetty.websocket</groupId>
			<artifactId>websocket-client</artifactId>
			<version>${jetty.version}</version>
		</dependency>
		<!--Jetty Websocket client side dependencies end here -->

	</dependencies>

In order to test our WebSocket server, we use the Maven Jetty plugin. This plugin also has to be added to our pom.xml.

<build>
		<plugins>
			<plugin>
				<groupId>org.eclipse.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>${jetty.version}</version>
			</plugin>
		</plugins>


	</build>

Finally our pom.xml looks like:

pom.xml

<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>com.javacodegeeks.snippets.enterprise</groupId>
	<artifactId>jetty-websocket-example</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<properties>
		<jetty.version>9.2.11.v20150529</jetty.version>
	</properties>

	<build>
		<plugins>
			<plugin>
				<groupId>org.eclipse.jetty</groupId>
				<artifactId>jetty-maven-plugin</artifactId>
				<version>${jetty.version}</version>
			</plugin>
		</plugins>


	</build>

	<dependencies>
		<!--Jetty dependencies start here -->
		<dependency>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-server</artifactId>
			<version>${jetty.version}</version>
		</dependency>

		<dependency>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-servlet</artifactId>
			<version>${jetty.version}</version>
		</dependency>
		<!--Jetty dependencies end here -->

		<!--Jetty Websocket server side dependencies start here -->



		<!--Jetty JSR-356 Websocket server side dependency -->
		<dependency>
			<groupId>org.eclipse.jetty.websocket</groupId>
			<artifactId>javax-websocket-server-impl</artifactId>
			<version>${jetty.version}</version>
		</dependency>

		<!--Jetty Websocket API server side dependency -->
		<dependency>
			<groupId>org.eclipse.jetty.websocket</groupId>
			<artifactId>websocket-server</artifactId>
			<version>${jetty.version}</version>
		</dependency>


		<!--Jetty Websocket server dependencies end here -->
		
		<!--Jetty Websocket client side dependencies start here -->

		

		<!--JSR-356 Websocket client side depencency  -->
		<dependency>
			<groupId>org.eclipse.jetty.websocket</groupId>
			<artifactId>javax-websocket-client-impl</artifactId>
			<version>${jetty.version}</version>
		</dependency>
		
		<!--Jetty Websocket API client side dependency -->
		<dependency>
			<groupId>org.eclipse.jetty.websocket</groupId>
			<artifactId>websocket-client</artifactId>
			<version>${jetty.version}</version>
		</dependency>
		<!--Jetty Websocket client side  dependencies end here -->

	</dependencies>
</project>

4.4 JSR-356 based WebSocket Server Example

After configuring the pom.xml, you can follow the steps below

  1. Create a POJO class.
  2. Annotate this class with @ServerEndpoint annotation and specify the endpoint value
  3. Implement the methods that handle the OnOpen, OnClose, OnMessage and OnError events.
  4. Annotate these methods with relevant annotations

In the example, we will implement a simple WebSocket server that receives a text message , converts it to uppercase and sends it back to the client.

Here we create the class ToUpper356Socket and annotate:

@ServerEndpoint("/jsr356toUpper")
public class ToUpper356Socket {
…
}

Then we implement OnOpen, OnClose, OnMessage methods. We do not have to implement methods for every annotation; so OnError is omitted here.
Our WebSocket server class looks like:

ToUpper356Socket.java

 
package com.javacodegeeks.snippets.enterprise.jettywebsocket.jsr356.server;

import java.io.IOException;

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

@ServerEndpoint("/jsr356toUpper")
public class ToUpper356Socket {

	@OnOpen
	public void onOpen(Session session) {
		System.out.println("WebSocket opened: " + session.getId());
	}
	@OnMessage
	public void onMessage(String txt, Session session) throws IOException {
		System.out.println("Message received: " + txt);
		session.getBasicRemote().sendText(txt.toUpperCase());
	}

	@OnClose
	public void onClose(CloseReason reason, Session session) {
		System.out.println("Closing a WebSocket due to " + reason.getReasonPhrase());

	}
}

javax.websocket.Session is an important class that encapsulates WebSocket connection information. We can access client information, connection status etc. through the instances of this Session class.

In order to send messages to the remote party, we use the session object again.

 session.getBasicRemote().sendText(“echo”);

sendText() is a method to send simple text messages to the client. There are also other methods to pass byte arrays or JSON objects to the remote party.

When we are done with coding the WebSocket server, we can run it via Jetty maven plugin with the following command:

 mvn jetty:run

Once Jetty is up, our websocket starts to listen connections on:

ws://localhost:8080/jsr356toUpper

4.5 JSR-356 based WebSocket Client Example

Now we are going to implement the WebSocket client code. Creating a client WebSocket with JSR-356 API is similar to the creating the server side socket:

  1. Create a POJO class.
  2. Annotate this class with @ClientEndpoint annotation.
  3. Implement the methods that handle the OnOpen, OnClose, OnMessage and OnError events.
  4. Annotate these methods with relevant annotations.

The Session object and message sending is the same as the Server side socket. You can see an example code below:

ToUpper356ClientSocket.java

 
package com.javacodegeeks.snippets.enterprise.jettywebsocket.jsr356.client;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;

@ClientEndpoint
public class ToUpper356ClientSocket {

	CountDownLatch latch = new CountDownLatch(1);
	private Session session;

	@OnOpen
	public void onOpen(Session session) {
		System.out.println("Connected to server");
		this.session = session;
		latch.countDown();
	}

	@OnMessage
	public void onText(String message, Session session) {
		System.out.println("Message received from server:" + message);
	}

	@OnClose
	public void onClose(CloseReason reason, Session session) {
		System.out.println("Closing a WebSocket due to " + reason.getReasonPhrase());
	}

	public CountDownLatch getLatch() {
		return latch;
	}

	public void sendMessage(String str) {
		try {
			session.getBasicRemote().sendText(str);
		} catch (IOException e) {

			e.printStackTrace();
		}
	}
}

You might have noticed that there is a CountDownLatch that is counted down when the connection is open. This latch is only for convenience in order to block other part of the client code before the connection is open. In addition to this, we added a public void sendMessage() method to send messages to the server.

Until now, we have created the WebSocket; the missing part is the code driving the client and initiating the contact. The snippet to initiate the connection can be seen below:

 
...
String dest = "ws://localhost:8080/jsr356toUpper";
ToUpper356ClientSocket socket = new ToUpper356ClientSocket();
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(socket, new URI(dest));
...

In order to keep things simple in scope of this example, we created a simple main class to drive this client WebSocket.

WebSocket356ClientMain.java

package com.javacodegeeks.snippets.enterprise.jettywebsocket.jsr356.client;

import java.net.URI;

import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;

import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;

public class WebSocket356ClientMain {

	public static void main(String[] args) {
	
		try {

			String dest = "ws://localhost:8080/jsr356toUpper";
			ToUpper356ClientSocket socket = new ToUpper356ClientSocket();
			WebSocketContainer container = ContainerProvider.getWebSocketContainer();
			container.connectToServer(socket, new URI(dest));

			socket.getLatch().await();
			socket.sendMessage("echo356");
			socket.sendMessage("test356");
			Thread.sleep(10000l);

		} catch (Throwable t) {
			t.printStackTrace();
		}
	}
}

Here we have established the connection and waited until the connection is set up. Then we have sent two messages and terminated.

The console output is below:

Connected to server
Message received from server:ECHO356
Message received from server:TEST356
Closing a WebSocket due to Shutdown

On the server side, we observe the following output:

WebSocket opened: websocket-1
Message received: echo356
Message received: test356
Closing a WebSocket due to Shutdown

 

4.6 Jetty WebSocket Server Example

Programming a WebSocket server with the Jetty API is not much different:

  1. Create a POJO class.
  2. Annotate this class with @WebSocket annotation.
  3. Implement the methods that handle the OnWebSocketConnect, OnWebSocketClose, OnWebSocketMessage and OnWebSocketError events.
  4. Annotate these methods with relevant annotations

Now we will implement the same WebSocket that converts client messages to upper case.

Our POJO initially looks like:

@WebSocket
public class ToUpperWebSocket {
}

After the methods and annotations are added, it becomes:

ToUpperWebSocket.java

package com.javacodegeeks.snippets.enterprise.jettywebsocket.jetty.server;

import java.io.IOException;

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;

@WebSocket
public class ToUpperWebSocket {

	@OnWebSocketMessage
	public void onText(Session session, String message) throws IOException {
		System.out.println("Message received:" + message);
		if (session.isOpen()) {
			String response = message.toUpperCase();
			session.getRemote().sendString(response);
		}
	}

	@OnWebSocketConnect
	public void onConnect(Session session) throws IOException {
		System.out.println(session.getRemoteAddress().getHostString() + " connected!");
	}

	@OnWebSocketClose
	public void onClose(Session session, int status, String reason) {
		System.out.println(session.getRemoteAddress().getHostString() + " closed!");
	}

}

Here the session object is of type org.eclipse.jetty.websocket.api.Session and sending messages to the client is performed with following method call:

session.getRemote().sendString("echo!");

As you see, there is not much difference between JSR-356 API and Jetty’s up-to here except we did not configure the URL path in the WebSocket class. Now we will configure it via a Servlet.

The main difference of Jetty WebSocket API is that we have to implement a Servlet extending WebSocketServlet class. In this servlet, we define the URL pattern to be matched and we configure WebSocket class handling the requests to this URL. A minimal servlet implementation is here:

ToUpperWebSocketServlet.java

package com.javacodegeeks.snippets.enterprise.jettywebsocket.jetty.server;

import javax.servlet.annotation.WebServlet;

import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;

@WebServlet(urlPatterns="/toUpper")
public class ToUpperWebSocketServlet  extends WebSocketServlet{

	@Override
	public void configure(WebSocketServletFactory factory) {
		
	      factory.register(ToUpperWebSocket.class);
		
	}

}

In this servlet, we define the URL path for the server and register our ToUpperWebSocket for this Servlet.

Once we implement the servlet, we can run mvn jetty:run again.

Now we can access our socket on:

ws://localhost:8080/toUpper

4.7 Jetty WebSocket Client Example

Here we are going to implement the WebSocket client code with the Jetty API. Steps are below:

  1. Create a POJO class.
  2. Annotate this class with @WebSocket annotation.
  3. Implement the methods that handle the OnWebSocketConnect, OnWebSocketClose, OnWebSocketMessage and OnWebSocketError events.
  4. Annotate these methods with relevant annotations

The Session object and message sending is the same as the Server side socket. You can see an example client side WebSocket code below:
ToUpperClientSocket.java

package com.javacodegeeks.snippets.enterprise.jettywebsocket.jetty.client;

import java.io.IOException;
import java.util.concurrent.CountDownLatch;

import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;

@WebSocket
public class ToUpperClientSocket {

	private Session session;
	
	CountDownLatch latch= new CountDownLatch(1);

	@OnWebSocketMessage
	public void onText(Session session, String message) throws IOException {
		System.out.println("Message received from server:" + message);
	}

	@OnWebSocketConnect
	public void onConnect(Session session) {
		System.out.println("Connected to server");
		this.session=session;
		latch.countDown();
	}
	
	public void sendMessage(String str) {
		try {
			session.getRemote().sendString(str);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public CountDownLatch getLatch() {
		return latch;
	}

}

Again we have a CountDownLatch as in the JSR-356 client socket for sycnhronization purposes.

The code that starts connection for the client is below:

...
String dest = "ws://localhost:8080/jsr356toUpper";
WebSocketClient client = new WebSocketClient();
ToUpperClientSocket socket = new ToUpperClientSocket();
client.start();
...

A simple main class establishing a connection with this server socket is below:

WebSocketClientMain.java

package com.javacodegeeks.snippets.enterprise.jettywebsocket.jetty.client;

import java.net.URI;

import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;

public class WebSocketClientMain {

	public static void main(String[] args) {
		String dest = "ws://localhost:8080/jsr356toUpper";
		WebSocketClient client = new WebSocketClient();
		try {
			
			ToUpperClientSocket socket = new ToUpperClientSocket();
			client.start();
			URI echoUri = new URI(dest);
			ClientUpgradeRequest request = new ClientUpgradeRequest();
			client.connect(socket, echoUri, request);
			socket.getLatch().await();
			socket.sendMessage("echo");
			socket.sendMessage("test");
			Thread.sleep(10000l);

		} catch (Throwable t) {
			t.printStackTrace();
		} finally {
			try {
				client.stop();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

The console output is below:

Connected to server
Message received from server:ECHO
Message received from server:TEST

4.8 HTML Client Example

Java based client for a WebSocket is convenient for some cases but the core use of WebSockets is ability to push data from servers to browsers. In this part, we will create a simple HTML page connecting to the WebSocket server in order to demonstrate browser to server integration.

This example page has three functionalities:

  1. User shall connect to the server.
  2. User shall send messages to the server.
  3. User shall see the received messages on the server.

On this purpose, there are two buttons on the page. One is to CONNECT, one is to SEND messages. In addition to these, there is a text input area and an output div in the HTML page.

When the user clicks the CONNECT button, following JavaScript code executes connecting to the server.

The connection code is as follows:

function connect() {
		// open the connection if one does not exist
		if (webSocket !== undefined
				&& webSocket.readyState !== WebSocket.CLOSED) {
			return;
		}
		// Create a websocket
		webSocket = new WebSocket("ws://localhost:8080/toUpper");

		webSocket.onopen = function(event) {
			updateOutput("Connected!");
			connectBtn.disabled = true;
			sendBtn.disabled = false;

		};

		webSocket.onmessage = function(event) {
			updateOutput(event.data);
		};

		webSocket.onclose = function(event) {
			updateOutput("Connection Closed");
			connectBtn.disabled = false;
			sendBtn.disabled = true;
		};
	}

After successful connection, onopen function of the WebSocket is called. In the example, it is implemented to update the output panel.

Everytime a message is received, the output is updated with the message. This is defined implementing the onmessage function of the of the websocket object.

When the connection is terminated for any reason, the output is updated again. This behavior is realized implementing the onclose function of the websocket.

When the user types something in the input text and presses the SEND button, the input text is sent to the server via the WebSocket. The snippet on this purpose is as below:

function send() {
		var text = document.getElementById("input").value;
		webSocket.send(text);
	}

 

As you see, unlike HTTP the type of interaction with WebSockets is duplex; we can simultaneously send and receive data. In HTTP, we would have to initiate an HTTP request and wait for the response to receive data from the server.

The full code of the HTML page is below:

<html>
<body>
	<div>
		<input type="text" id="input" />
	</div>
	<div>
		<input type="button" id="connectBtn" value="CONNECT"
			onclick="connect()" /> <input type="button" id="sendBtn"
			value="SEND" onclick="send()" disabled="true" />
	</div>
	<div id="output">
		<p>Output</p>
	</div>
</body>

<script type="text/javascript">
	var webSocket;
	var output = document.getElementById("output");
	var connectBtn = document.getElementById("connectBtn");
	var sendBtn = document.getElementById("sendBtn");

	function connect() {
		// open the connection if one does not exist
		if (webSocket !== undefined
				&& webSocket.readyState !== WebSocket.CLOSED) {
			return;
		}
		// Create a websocket
		webSocket = new WebSocket("ws://localhost:8080/toUpper");

		webSocket.onopen = function(event) {
			updateOutput("Connected!");
			connectBtn.disabled = true;
			sendBtn.disabled = false;

		};

		webSocket.onmessage = function(event) {
			updateOutput(event.data);
		};

		webSocket.onclose = function(event) {
			updateOutput("Connection Closed");
			connectBtn.disabled = false;
			sendBtn.disabled = true;
		};
	}

	function send() {
		var text = document.getElementById("input").value;
		webSocket.send(text);
	}

	function closeSocket() {
		webSocket.close();
	}

	function updateOutput(text) {
		output.innerHTML += "<br/>" + text;
	}
</script>
</html>

You can see a screenshot from the web client here:

Web Client GUI
Web Client GUI

Please note that the HTML +JavaScript code are for simple demonstration purpose only. There are many better development practices and libraries for front-end development.

5.Conclusion

WebSocket is a technology tackling the shortcomings of the HTTP for bidirectional duplex communication. It is a trending topic that is becoming more widespread. In this post, we have strived to provide brief information on WebSockets and we have provided example Server and Client side implementations of WebSockets using WebSocket API of Jetty.

Download
You can download the full source code of this example here : JettyWebSocketExample

Ibrahim Tasyurt

Ibrahim is a Senior Software Engineer residing in Ankara,Turkey. He holds BSc and MS degrees in Computer Engineering from Middle East Technical University(METU). Throughout his professional carrier, he has worked in Enterprise Web Application projects for public sector and telecommunications domains. Java EE, Web Services and Enterprise Application Integration are the areas he is primarily involved with.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

7 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
saar
saar
7 years ago

i get : WebSocket connection to ‘ws://localhost:8443/jsr356toUpper’ failed: Error during WebSocket handshake: Unexpected response code: 404

Viktor
Viktor
6 years ago
Reply to  saar

Same issue. Anyone with solution for this problem?

Shakti Tomar
Shakti Tomar
6 years ago

Nice example but can be more useful if you could post the images of folder structures and the jetty configurations as well.

Phil
Phil
6 years ago

This is really cool. I like the way you explained it. I’m looking to integrate this with Angular 2 web chat.

Evan
Evan
5 years ago

“@ClientEndpoint:
ServerEndpoint is used to annotate a POJO classes as client side WebSockets”

That should be “ClientEndpoint is used…”

sneha
sneha
4 years ago

Has anyone run this project ? i m getting build issue saying web.xml is missing

Rudresh
Rudresh
3 years ago

is there any example to add security layer in it. please share for example with jetty websocket.

Back to top button