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

WebSockets with Embedding Jetty [step3]


In [step2] we have implemented our chat application on client side with JavasScript WebSocket component. In this article we will create chat WebSockets on server side with Jetty-WebSocket and start the Jetty Server with Embedding Jetty. We will create a Java main which start a Jetty Server and configure it to manage WebSockets Chat Application on server side.

We will use Eclipse IDE to create a Java Project org.samples.websockets.embeddingjetty (but you can do with another IDE if you wish). At the end of this article our workspace will looks like this:

Download

You can download several Eclipse projects explained in this article:

Download Jetty-WebSockets JARs

You can download org.samples.websockets.embeddingjetty_step3_2.zip which contains Eclipse project explained in this section.

Before starting read this section, create an Eclipse Java Project org.samples.websockets.embeddingjetty.

Here we will use Jetty-WebSockets 7.4.4.v20110707 which is the last stable Jetty release. This version is very important because WebSockets API has changed since the article Jetty WebSocket Server. I would like say too that WTP Jetty Websocket_Wizard doesn’t generate well WebSockets code for the Jetty 7.4.

At first we need to download required Jetty-WebSockets JARs :

  • jetty-continuation-7.4.4.v20110707.jar
  • jetty-http-7.4.4.v20110707.jar
  • jetty-io-7.4.4.v20110707.jar
  • jetty-server-7.4.4.v20110707.jar
  • jetty-util-7.4.4.v20110707.jar
  • jetty-websocket-7.4.4.v20110707.jar
  • servlet-api-2.5.jar

If you wish download thoses JARs you can :

Jetty-WebSockets POM (maven)

If you don’t know maven or you don’t know how to use maven to download JARs and you can read French, please read Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step14] where I explain how download JARs by using POM maven.

In your POM, you must declare the Maven repository of the Jetty Eclipse Repository like this :

<repository>
  <id>org.eclipse.jetty</id>
  <name>Jetty Eclipse Repository</name>
  <url>http://mvnrepository.com/artifact/org.eclipse.jetty</url>
</repository>

After you must declare the jetty-websocket dependency like this :

<dependency>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-websocket</artifactId>
  <version>7.4.4.v20110707</version>
</dependency>

Here the full POM that I have used to download Jetty-WebSocket JARs :

<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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.samples.websockets</groupId>
	<artifactId>org.samples.websockets.embeddingjetty</artifactId>
	<packaging>pom</packaging>
	<version>0.0.1-SNAPSHOT</version>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<version>2.1</version>
				<executions>
					<execution>
						<id>copy-dependencies</id>
						<phase>process-resources</phase>
						<goals>
							<goal>copy-dependencies</goal>
						</goals>
						<configuration>
							<outputDirectory>lib</outputDirectory>
							<overWriteReleases>true</overWriteReleases>
							<overWriteSnapshots>true</overWriteSnapshots>
							<overWriteIfNewer>true</overWriteIfNewer>
							<excludeTypes>libd</excludeTypes>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

	<repositories>

		<!-- Jetty Eclipse Repository -->
		<repository>
			<id>org.eclipse.jetty</id>
			<name>Jetty Eclipse Repository</name>
			<url>http://mvnrepository.com/artifact/org.eclipse.jetty</url>
		</repository>

	</repositories>

	<dependencies>

		<!-- Jetty WebSockets -->
		<dependency>
			<groupId>org.eclipse.jetty</groupId>
			<artifactId>jetty-websocket</artifactId>
			<version>7.4.4.v20110707</version>
		</dependency>

	</dependencies>

</project>

You can do :

mvn process-resources

Or use the launch org.samples.websockets.embeddingjetty\launch-mvn\Process-resources Jetty-Websockets.launch from the org.samples.websockets.embeddingjetty_step3_2.zip :

After refreshing the Eclipse project, lib folder must appears with Jetty-WebSockets JARs :

Update your ClassPath with Jetty-WebSockets JARs :

Simple Server with Embedding Jetty

In this section we will create a simple Embedding Jetty Server which is started in the 8081 port. To do that create the Java class org.samples.websockets.embeddingjetty.ChatWebSocketServer like this :

package org.samples.websockets.embeddingjetty;

import org.eclipse.jetty.server.Server;

public class ChatWebSocketServer {

	public static void main(String[] args) {
		try {
			// 1) Create a Jetty server with the 8091 port.
			Server server = new Server(8081);
			// 2) Start the Jetty server.
			server.start();
			// Jetty  server is stopped when the Thread is interruped.
			server.join();
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}
}

We have created a simple Jetty Server with Java main started on 8081 port. You can notice that the code is very simple! To test our code, start the Java main ChatWebSocketServer. The Eclipse console must display some Jetty logs which shows you Jetty Server is started in the 8081 port :

Open a Google Chrome (or another WEB Browser) and go at http://localhost:8081 :

You will see 404 error because the Jetty server has not defined HTML resources files, Servlet, Filter… If you are interested to do that, you can find several samples with Embedding Jetty at SVN example-jetty-embedded.

Chat Application with Jetty-WebSockets

In this section we create a Jetty Handler to manage WebSocket with ChatWebSocketHandler and register it to the Jetty Server instance.

ChatWebSocketHandler

In this section we create a Jetty Handler ChatWebSocketHandler to manage WebSocket.

Implements WebSocketHandler

Create org.samples.websockets.embeddingjetty.ChatWebSocketHandler like this :

package org.samples.websockets.embeddingjetty;

import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.websocket.WebSocketHandler;

public class ChatWebSocketHandler extends WebSocketHandler {

	public WebSocket doWebSocketConnect(HttpServletRequest request,
			String protocol) {
		return null;
	}
}

When this handler will be registered in the Jetty Server instance and user of chat application on client side will click on « Join » button, which call the JavaScript code :

var location = "ws://localhost:8081/"
this._ws = new WebSocket(location);

The Jetty server will call :

ChatWebSocketHandler#doWebSocketConnect(HttpServletRequest request, String protocol)

This method creates an instance of org.eclipse.jetty.websocket.WebSocket. The server side will store an instance of org.eclipse.jetty.websocket.WebSocket per JavasScript WebSocket component (client side).

Implements WebSocket.OnTextMessage

At this step we must implement org.eclipse.jetty.websocket.WebSocket to return an instance of this class when

ChatWebSocketHandler#doWebSocketConnect(HttpServletRequest request, String protocol)

is called.

WebSocket interface before 7.4 version

Before Jetty 7.4 version that we are using, Jetty WebSocket interface was defined like this:

package org.eclipse.jetty.websocket;

import java.io.IOException;

public interface WebSocket
{
    @Deprecated
    public final byte LENGTH_FRAME=(byte)0x80;
    @Deprecated
    public final byte SENTINEL_FRAME=(byte)0x00;

    public final static byte   OP_CONTINUATION = 0x00;
    public final static byte   OP_CLOSE = 0x01;
    public final static byte   OP_PING = 0x02;
    public final static byte   OP_PONG = 0x03;
    public final static byte   OP_TEXT = 0x04;
    public final static byte   OP_BINARY = 0x05;
    
    public final static int CLOSE_NORMAL=1000;
    public final static int CLOSE_SHUTDOWN=1001;
    public final static int CLOSE_PROTOCOL=1002;
    public final static int CLOSE_DATA=1003;
    public final static int CLOSE_LARGE=1004;
    
    void onConnect(Outbound outbound);
    void onMessage(byte opcode,String data);
    void onFragment(boolean more,byte opcode,byte[] data, int offset, int length);
    void onMessage(byte opcode,byte[] data, int offset, int length);
    void onDisconnect(); // TODO add code 
    
    public interface Outbound
    {
        void sendMessage(String data) throws IOException;
        void sendMessage(byte opcode,String data) throws IOException;
        void sendMessage(byte opcode,byte[] data, int offset, int length) throws IOException;
        void sendFragment(boolean more,byte opcode,byte[] data, int offset, int length) throws IOException;
        void disconnect();
        void disconnect(int code,String message);
        boolean isOpen();
    }
}

When you wanted to implement Jetty-WebSocket you needed to implement several methods like WebSocket#sendMessage(byte opcode,byte[] data, int offset, int length) throws IOException which is the method to send binary data, although this method was never called. For instance in our chat application, only WebSocket#sendMessage(String data) throws IOException is used because chat application client send simple text.

WebSocket interface 7.4 version

Jetty 7.4 version has changed the WebSocket interface like this :

package org.eclipse.jetty.websocket;

import java.io.IOException;

/**
 * WebSocket Interface.
 * <p>
 * This interface provides the signature for a server-side end point of a websocket connection.
 * The Interface has several nested interfaces, for each type of message that may be received.
 */
public interface WebSocket
{   
    /**
     * Called when a new websocket connection is accepted.
     * @param connection The Connection object to use to send messages.
     */
    void onOpen(Connection connection);
    
    /**
     * Called when an established websocket connection closes
     * @param closeCode
     * @param message
     */
    void onClose(int closeCode, String message);

    /**
     * A nested WebSocket interface for receiving text messages
     */
    interface OnTextMessage extends WebSocket
    {
        /**
         * Called with a complete text message when all fragments have been received.
         * The maximum size of text message that may be aggregated from multiple frames is set with {@link Connection#setMaxTextMessageSize(int)}.
         * @param data The message
         */
        void onMessage(String data);
    }

    /**
     * A nested WebSocket interface for receiving binary messages
     */
    interface OnBinaryMessage extends WebSocket
    {
        /**
         * Called with a complete binary message when all fragments have been received.
         * The maximum size of binary message that may be aggregated from multiple frames is set with {@link Connection#setMaxBinaryMessageSize(int)}.
         * @param data
         * @param offset
         * @param length
         */
        void onMessage(byte[] data, int offset, int length);
    }
    
    /**
     * A nested WebSocket interface for receiving control messages
     */
    interface OnControl extends WebSocket
    {
        /** 
         * Called when a control message has been received.
         * @param controlCode
         * @param data
         * @param offset
         * @param length
         * @return true if this call has completely handled the control message and no further processing is needed.
         */
        boolean onControl(byte controlCode,byte[] data, int offset, int length);
    }
    
    /**
     * A nested WebSocket interface for receiving any websocket frame
     */
    interface OnFrame extends WebSocket
    {
        /**
         * Called when any websocket frame is received.
         * @param flags
         * @param opcode
         * @param data
         * @param offset
         * @param length
         * @return true if this call has completely handled the frame and no further processing is needed (including aggregation and/or message delivery)
         */
        boolean onFrame(byte flags,byte opcode,byte[] data, int offset, int length);
        
        void onHandshake(FrameConnection connection);
    }
    
    /**
     * A  Connection interface is passed to a WebSocket instance via the {@link WebSocket#onOpen(Connection)} to 
     * give the application access to the specifics of the current connection.   This includes methods 
     * for sending frames and messages as well as methods for interpreting the flags and opcodes of the connection.
     */
    public interface Connection
    {
        String getProtocol();
        void sendMessage(String data) throws IOException;
        void sendMessage(byte[] data, int offset, int length) throws IOException;
        void disconnect();
        boolean isOpen();

        /**
         * @param size size<0 No aggregation of frames to messages, >=0 max size of text frame aggregation buffer in characters
         */
        void setMaxTextMessageSize(int size);
        
        /**
         * @param size size<0 no aggregation of binary frames, >=0 size of binary frame aggregation buffer
         */
        void setMaxBinaryMessageSize(int size);
        
        /**
         * Size in characters of the maximum text message to be received
         * @return size <0 No aggregation of frames to messages, >=0 max size of text frame aggregation buffer in characters
         */
        int getMaxTextMessageSize();
        
        /**
         * Size in bytes of the maximum binary message to be received
         * @return size <0 no aggregation of binary frames, >=0 size of binary frame aggregation buffer
         */
        int getMaxBinaryMessageSize();
    }
    
    /**
     * Frame Level Connection
     * <p>The Connection interface at the level of sending/receiving frames rather than messages.
     *
     */
    public interface FrameConnection extends Connection
    {
        boolean isMessageComplete(byte flags);
        void close(int closeCode,String message);
        byte binaryOpcode();
        byte textOpcode();
        byte continuationOpcode();
        byte finMask();
        
        boolean isControl(byte opcode);
        boolean isText(byte opcode);
        boolean isBinary(byte opcode);
        boolean isContinuation(byte opcode);
        boolean isClose(byte opcode);
        boolean isPing(byte opcode);
        boolean isPong(byte opcode);
        
        void sendControl(byte control,byte[] data, int offset, int length) throws IOException;
        void sendFrame(byte flags,byte opcode,byte[] data, int offset, int length) throws IOException;
    }
    
}

So now when you wish implement Jetty WebSocket you must choose the internal WebSocket interface that you wish implement. In our case text message is used for our chat application, so we will implement WebSocket.OnTextMessage :

/**
 * A nested WebSocket interface for receiving text messages
*/
interface OnTextMessage extends WebSocket
{
    /**
     * Called with a complete text message when all fragments have been received.
     * The maximum size of text message that may be aggregated from multiple frames is set with {@link Connection#setMaxTextMessageSize(int)}.
     * @param data The message
     */
    void onMessage(String data);
}

Here hyerarchy of the Jetty 7.4 WebSocket interface :

Modify org.samples.websockets.embeddingjetty.ChatWebSocketHandler like this :

package org.samples.websockets.embeddingjetty;

import javax.servlet.http.HttpServletRequest;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketHandler;

public class ChatWebSocketHandler extends WebSocketHandler {

	public WebSocket doWebSocketConnect(HttpServletRequest request,
			String protocol) {
		return new ChatWebSocket();
	}

	private class ChatWebSocket implements WebSocket.OnTextMessage {

		public void onOpen(Connection connection) {

		}

		public void onMessage(String data) {

		}

		public void onClose(int closeCode, String message) {

		}
	}
}

Implements ChatWebSocket

At this step we must implement each methods of ChatWebSocket.To do that modify org.samples.websockets.embeddingjetty.ChatWebSocketHandler like this :

package org.samples.websockets.embeddingjetty;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.servlet.http.HttpServletRequest;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketHandler;

public class ChatWebSocketHandler extends WebSocketHandler {

	private final Set<ChatWebSocket> webSockets = new CopyOnWriteArraySet<ChatWebSocket>();

	public WebSocket doWebSocketConnect(HttpServletRequest request,
			String protocol) {
		return new ChatWebSocket();
	}

	private class ChatWebSocket implements WebSocket.OnTextMessage {

		private Connection connection;

		public void onOpen(Connection connection) {
			// Client (Browser) WebSockets has opened a connection.
			// 1) Store the opened connection
			this.connection = connection;
			// 2) Add ChatWebSocket in the global list of ChatWebSocket
			// instances
			// instance.
			webSockets.add(this);
		}

		public void onMessage(String data) {
			// Loop for each instance of ChatWebSocket to send message server to
			// each client WebSockets.
			try {
				for (ChatWebSocket webSocket : webSockets) {
					// send a message to the current client WebSocket.
					webSocket.connection.sendMessage(data);
				}
			} catch (IOException x) {
				// Error was detected, close the ChatWebSocket client side
				this.connection.disconnect();
			}

		}

		public void onClose(int closeCode, String message) {
			// Remove ChatWebSocket in the global list of ChatWebSocket
			// instance.
			webSockets.remove(this);
		}
	}
}

Register ChatWebSocketHandler in the Jetty Server

At this step ChatWebSocketHandler is created and we must register it to the Jetty server instance like this:

ChatWebSocketHandler chatWebSocketHandler = new ChatWebSocketHandler();
			chatWebSocketHandler.setHandler(new DefaultHandler());
			server.setHandler(chatWebSocketHandler);

Modify org.samples.websockets.embeddingjetty.ChatWebSocketServer like this :

package org.samples.websockets.embeddingjetty;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;

public class ChatWebSocketServer {

	public static void main(String[] args) {
		try {
			// 1) Create a Jetty server with the 8091 port.
			Server server = new Server(8081);
			// 2) Register ChatWebSocketHandler in the Jetty server instance.
			ChatWebSocketHandler chatWebSocketHandler = new ChatWebSocketHandler();
			chatWebSocketHandler.setHandler(new DefaultHandler());
			server.setHandler(chatWebSocketHandler);
			// 2) Start the Jetty server.
			server.start();
			// Jetty server is stopped when the Thread is interruped.
			server.join();
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}
}

Play with Chat Application!

At this step you can play with the chat application. To do that start the Jetty Server and open chat.html in Google Chrome:

Conclusion

In this article we have seen how to develop Chat Application WebSockets on server side with Jetty-WebSocket. We are used an Embedding Jetty to start the Jetty server and configure it to support our Chat Application WebSockets with the ChatWebSocketHandler class. This class can be used in a Jetty server too. In the [step4] I see you how to use our Chat Jetty-WebSocket in another Java Server (Tomcat, WebShpere…) which doesn’t support WebSocket.

Catégories :Jetty, WebSockets
  1. james percy
    août 18, 2011 à 11:46

    thank you so much, this is an excellent tutorial, you rock

    • août 18, 2011 à 2:29

      Hi James,

      I’m happy that this article pleased you. Thank a lot for your post.

      Regards Angelo

  2. Mehmet
    novembre 19, 2011 à 8:35

    Hello,

    Thank you very much. The best tutorial is this.

    Best Regards,

    • novembre 20, 2011 à 10:37

      Hi Mehmet.

      Many thanks for your comment.

      Regards Angelo

  3. Tito
    avril 15, 2012 à 9:25

    Very good tutorial it’s help me a lot but i’m confused, when you say « You will see 404 error because the Jetty server has not defined HTML resources files »… i went to the link but i don’t understand what to do, i always have 404 error, and i don’t understand why?
    can you please help me, you can respond me in french if you want

    thanks you a lot

    • avril 16, 2012 à 12:32

      Hi Tito,

      Many thank’s. Sorry I cannot help you more. It was a long time that I have written those article. Good luck.

      Regards Angelo

  4. Francois
    Mai 15, 2012 à 7:54

    best tutorial ever read on the subject

  5. Hericson
    juin 15, 2012 à 8:31

    Best tutorial, thx,

    But I have an error on firefox an Chrome:
    WARN::Unsupported Websocket version: 13

    Someone have an issue about it ? thx

    • Phil
      juin 20, 2012 à 2:40

      upgrade to the last version of jetty 8 and servlet 3.0 API

      • roger
        août 14, 2012 à 8:54

        Thanks Phil for the same it worked. Very helpful guide a must read for someone trying to learn how websockets works with embedded jetty

  6. tuxtux
    janvier 11, 2013 à 11:07

    Hello,

    Really good tutorial,

    Thank u a lot,

    I did it with jetty-websocket et jetty-server 8.1.0.RC5 + chrome 24 (cause 7.4.4.v20110707 was out of date)

  7. marat
    Mai 10, 2013 à 6:24

    Hi Angelo,

    Do you know if there is a way to set a mime type for the messages being set from jetty? This is so we can query on the client if the message received was text or binary. In my case, Chrome issues a warning « Resource interpreted as Image but transferred with MIME type text/plain: … » when I send images.

    Regards,
    Marat

  8. seb
    novembre 29, 2013 à 2:21

    Very good tuto, very helpfull. Thank you.

  9. JettyUser
    août 22, 2014 à 9:08

    Can you please explain how to deal with secure websockets (wss) with jetty 8

    I have written server as follows:

    import java.net.*;
    import org.eclipse.jetty.http.*;
    import org.eclipse.jetty.http.HttpException;
    import org.eclipse.jetty.util.*;
    import org.eclipse.jetty.websocket.*;

    import org.eclipse.jetty.server.nio.SelectChannelConnector;

    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.server.NCSARequestLog;
    import org.eclipse.jetty.server.handler.DefaultHandler;
    import org.eclipse.jetty.server.handler.HandlerCollection;
    import org.eclipse.jetty.server.handler.RequestLogHandler;
    import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;

    import org.eclipse.jetty.util.thread.QueuedThreadPool;
    import org.eclipse.jetty.util.ssl.SslContextFactory;
    import org.eclipse.jetty.server.*;

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

    String jetty_home = System.getProperty(« jetty.home », »/opt/jetty »);
    System.setProperty(« jetty.home »,jetty_home);

    // === Create a Jetty Server ===
    Server server = new Server();

    // === jetty-https.xml ===
    // SSL Context Factory
    SslContextFactory sslContextFactory = new SslContextFactory();

    sslContextFactory.setKeyStorePath(jetty_home + « /etc/keystore.jks »);
    sslContextFactory.setKeyStorePassword(« jettydev »);
    //sslContextFactory.setKeyManagerPassword(« jettydev »);
    sslContextFactory.setTrustStore(jetty_home + « /etc/keystore.jks »);
    sslContextFactory.setTrustStorePassword(« jettydev »);
    sslContextFactory.setIncludeProtocols(new String[] { « TLSv1″, »SSLv2Hello » });

    // === SSL Connector (for https) ===
    SslSelectChannelConnector sslConnector = new SslSelectChannelConnector(sslContextFactory);

    sslConnector.setPort(443);

    // === Connector (for http) ===
    SelectChannelConnector connector = new SelectChannelConnector();
    connector.setPort(8086);

    server.setConnectors(new Connector[] {sslConnector, connector} );

    // === Register SocketHandler in the Jetty server instance. ===
    SocketHandler socketHandler = new SocketHandler();
    socketHandler.setHandler(new DefaultHandler());
    server.setHandler(socketHandler);

    // === Start the Jetty Server ===
    server.start();

    // === Jetty Server is stopped when the thread is interrupted. ===
    server.join();

    } catch(Exception e) {
    System.out.println(« Exception occured: « );
    e.printStackTrace();
    }
    }
    }

    So, here 2 connectors are used. One for http requests and other for https requests.

    At client side,
    for nonsecure websocket,
    ws = new Websocket(« ws://domain:8086/ »);
    works absolutely fine.

    Whereas,
    for secure websocket,
    ws = new Websocket(« wss://domain:443/ »);
    fails to form websocket connection.
    It does not reach server it seems.

    Can you please tell me why this happens and what else I need to do or what am I missing??
    I really hope you reply as early as possible. Its an urgent issue actually.

    Please help!

    • août 22, 2014 à 9:49

      Never done security with WebSocket. I cannot help you.

  1. juillet 26, 2011 à 9:47
  2. juillet 26, 2011 à 3:05
  3. juillet 27, 2011 à 3:31
  4. juillet 27, 2011 à 5:43
  5. août 22, 2011 à 8:01
  6. mars 8, 2012 à 3:09

Laisser un commentaire