Creating Binary WebSocket Connections with Java EE 7 and JavaFX
Overview
Purpose
This tutorial covers how to create binary WebSocket connections with Java Platform, Enterprise Edition 7 (Java EE 7) and JavaFX.
Time to Complete
Approximately 1 hour
Introduction
The WebSocket protocol, which was developed as part of HTML5, enables full-duplex and real-time communication to a Web server. The server and client can send and receive data at any time by using a single connection. WebSocket connections are started by using an HTTP request that enables the connection to change to the WebSocket protocol. These connections take advantage of all features of the HTTP protocol. Switching the connection allows for little overhead communication because only one HTTP handshake is performed for all exchanged messages.
The WebSocket API (JSR 356) is one of the many Java EE 7 technologies, and it is included in Java EE 7 application servers. It was also designed to run as part of a Java Platform, Standard Edition (Java SE) application. Project Tyrus, the reference implementation for WebSocket, includes the libraries required to create WebSocket connections outside a Java EE 7 application server. The required WebSocket libraries are provided in this zip file.
Using WebSocket connections, you can send and receive three kind of messages:
- Text-based messages are supported with HTML5 and JavaScript and are commonly used to exchange object data by using JavaScript Object Notation (JSON).
- Binary messages are used by stand-alone applications to exchange raw binary data, such as images.
- Ping Pong messages are special messages that the WebSocket protocol uses to keep the connection alive.
Binary WebSocket connections are used by applications to stream binary data from the server to multiple clients. They can be used to create:
- Streaming video
- Multimedia chats
- Voice over IP conversations
- Game communication
- Transport files or any kind of "free-form" data
Scenario
In this tutorial, you build an application to exchange images among multiple clients by using binary WebSockets that are connected to a server. You deploy the server in a Java EE7 server, and you create the client as a stand-alone JavaFX application.
Hardware and Software Requirements
The following is a list of hardware and software requirements:
- Download and install the latest version of Java SDK.
- Download and install NetBeans IDE 7.4 for Java EE, which includes GlassFish 4. During installation, be sure to select the check box to install GlassFish. JUnit is an optional installation and is not required for this tutorial.
Prerequisites
Before starting this tutorial, you should:
- Have installed the required software.
- Have basic experience with Java SE 7 desktop applications.
- Have basic experience with Java EE web-based applications.
Creating a Web Application
In this section, you create a Java EE 7 web application in the NetBeans IDE.
-
Select File > New Project.
View Image -
In the New Project dialog box, perform the following steps:
- Select Java Web from Categories.
- Select Web Application from Projects.
- Click Next.
View Image -
On the Name and Location page, enter BinaryWebSocketServer as the project name and click Next.
View Image -
On the Server and Settings page, perform the following steps:
- Select GlassFish Server 4.0 from the Server list.
- Select Java EE 7 Web from the Java EE Version list.
- Click Finish.
View Image
The BinaryWebSocketServer
project is created in
NetBeans.
Creating the Binary WebSocket Server Endpoint
In this section, you create the WebSocket server endpoint.
-
Right-click the
BinaryWebSocketServer
project and select New > Other.View Image -
On the New File page, perform the following steps:
- Select the Web category.
- Select the WebSocket Endpoint file type.
- Click Next.
View Image -
On the Create WebSocket Endpoint page, perform the following steps:
- Enter BinaryWebSocketServer for the class name.
- Enter com.demo.websocket for the project name.
- Enter /images for the WebSocket URI.
- Click Finish.
View Image -
Add a static
Set
ofSession
objects just below thepublic class BinaryWebSocketServer {
definition to store connected users.private
static final Set<Session> sessions =
Collections.synchronizedSet(new HashSet<Session>()); -
Add the following imports after the
package com.demo.websocket
declaration:
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.websocket.Session;Alternatively, you can import the classes by selecting the Fix Imports option.
- From the NetBeans menu, select Source > Fix
Imports.
View Image - In the Fix All Imports dialog box, select the classes
that you want to import and click OK.
View Image
- From the NetBeans menu, select Source > Fix
Imports.
-
Add the following two methods to add and remove sessions:
@OnOpen
public void onOpen(Session session) {
sessions.add(session);
}
@OnClose
public void onClose(Session session) {
sessions.remove(session);
} -
Add the following imports:
import javax.websocket.OnClose;
import javax.websocket.OnOpen; -
Modify the
onMessage
method.
@OnMessage
public void onMessage(ByteBuffer byteBuffer) {
for (Session session : sessions) {
try {
session.getBasicRemote().sendBinary(byteBuffer);
} catch (IOException ex) {
Logger.getLogger(BinaryWebSocketServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
} -
Add the following imports:
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger; - Use the following completed
BinaryWebSocketServer
code sample for reference:
package com.demo.websocket;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/images")
public class BinaryWebSocketServer {
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(ByteBuffer byteBuffer) {
for (Session session : sessions) {
try {
session.getBasicRemote().sendBinary(byteBuffer);
} catch (IOException ex) {
Logger.getLogger(BinaryWebSocketServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
Creating the JavaFX Project
In this section, you create the WebSocket client application.
-
Select File > New Project.
View Image -
In the New Project dialog box, perform the following steps:
- Select JavaFX from the Categories list.
- Select JavaFX Application from the Projects list.
- Click Next.
View Image -
On the Name and Location page, enter JavaFXBinaryWsClient for the project name and click Finish.
View Image To use WebSocket in Java SE/FX applications, you need additional JAR libraries. This example uses Project Tyrus, which is the reference implementation for Java WebSockets. The required JAR files are provided as part of this tutorial.
-
Right-click
JavaFXBinaryWsClient
and select New > Other.View Image -
On the Choose File page, perform the following steps:
- Select Other from the Categories list.
- Select Folder from the File Types list.
- Click Next.
View Image -
On the Name and Location page, enter lib for the folder name and click Finish.
View Image -
Add the WebSocket implementation JARs to the project.
- Download the WebSocket Client Jars from this zip file.
- Extract the files to the
lib
folder of theJavaFXBinaryWsClient
project.
View Image You can view the files in NetBeans on the Files tab.
-
Right-click the
JavaFXBinaryWsClient
project and select Properties.View Image -
Select the Libraries page and click Add JAR/Folder.
View Image -
In the Add JAR/Folder dialog box, add the unzipped JAR files.
- Browse to the lib folder in the
JavaFXBinaryWsClient
project. - Select all of the JAR files.
- Click Open.
View Image - Browse to the lib folder in the
-
In the Project Properties dialog box, click OK.
View Image If you see a reference warning about the
dist.jar
file, ignore it. The file is built with the project
Creating the JavaFX Client
In this section, you build the JavaFX binary WebSocket client.
You will:
-
Connect to the WebSocket server.
-
Create a simple JavaFX layout.
-
Send and receive binary WebSocket messages.
-
Open the
javafxbinarywsclient.JavaFxBinaryWsClient
Java class.View Image -
Annotate the class with the
@javax.websocket.ClientEndpoint
annotation.@ClientEndpoint
public class JavaFXBinaryWsClient extends Application {Add the following imports:
import javax.websocket.ClientEndpoint;
-
Add the following instance variables:
- A
private javax.websocket.Session session
to communicate messages to the server - A
private javafx.scene.image.ImageView imageView
to display images sent from the server - A
private static final Logger LOGGER = Logger.getLogger(JavaFXBinaryWsClient.class.getName());
to log messages and errors in the application
@ClientEndpoint
public class JavaFXBinaryWsClient extends Application {
private Session session;
private ImageView ImageView;
private static final Logger LOGGER = Logger.getLogger(JavaFXBinaryWsClient.class.getName());Add the following imports:
import java.util.logging.Logger;
import javafx.scene.image.ImageView;
import javax.websocket.Session; - A
-
Add a method to connect to the WebSocket server.
private void connectToWebSocket() {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
try {
URI uri = URI.create("ws://localhost:8080/BinaryWebSocketServer/images");
container.connectToServer(this, uri);
} catch (DeploymentException | IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
System.exit(-1);
}
}This method configures the running application as a WebSocket client.
-
Add the following imports:
import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.WebSocketContainer; -
At the beginning of the
start
method, invoke theconnectToWebSocket
method.@Override
public void start(final Stage primaryStage) {
connectToWebSocket(); -
Replace the contents of the
start
method after theconnectToWebSocket()
call to create the layout.At a minimum, your layout needs the following:
- A
javafx.scene.control.Button
to select an image to send - A
javafx.scene.image.ImageView
to display images received
The following code uses an
AnchorPane
to arrange the components:@Override
public void start(final Stage primaryStage) {
connectToWebSocket();
Button btn = new Button();
btn.setText("Send Image!");
btn.setPrefSize(400, 27);
imageView = new ImageView();
imageView.setFitHeight(400);
imageView.setFitWidth(400);
imageView.setPreserveRatio(true);
imageView.setSmooth(true);
AnchorPane root = new AnchorPane();
AnchorPane.setTopAnchor(btn, 0.0);
AnchorPane.setLeftAnchor(btn, 0.0);
AnchorPane.setRightAnchor(btn, 0.0);
AnchorPane.setTopAnchor(imageView, 27.0);
AnchorPane.setBottomAnchor(imageView, 0.0);
AnchorPane.setLeftAnchor(imageView, 0.0);
AnchorPane.setRightAnchor(imageView, 0.0);
root.getChildren().add(btn);
root.getChildren().add(imageView);
Scene scene = new Scene(root, 400, 427);
primaryStage.setTitle("Image push!");
primaryStage.setScene(scene);
primaryStage.show();
} - A
-
Add the following import:
javafx.scene.layout.AnchorPane;
-
Create a method to select and send an image.
private void selectAndSendImage(Stage stage) {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select Image to Send");
File file = fileChooser.showOpenDialog(stage);
try (InputStream input = new FileInputStream(file);
OutputStream output = session.getBasicRemote().getSendStream()) {
byte[] buffer = new byte[1024];
int read;
while ((read = input.read(buffer)) > 0) {
output.write(buffer, 0, read);
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
} -
Add the following imports:
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import javafx.stage.FileChooser; -
Create an event handler for the button and invoke the
selectAndSendImage
method. Make the stage method parameter in the start method to use it inside the event handler.btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
selectAndSendImage(primaryStage);
}
}); -
Add the following imports:
import javafx.event.ActionEvent;
import javafx.event.EventHandler; -
Create the WebSocket methods to handle connections and messages.
- A method annotated by
javax.websocket.OnOpen
that sets the session variable - A method annotated by
javax.websocket.OnClose
that attempts to reconnect if the connection is closed - A method annotated by
javax.websocket.OnMessage
that handles the messages and creates an image to be displayed in the imageView
@OnOpen
public void onOpen(Session session) {
this.session = session;
}
@OnMessage
public void onMessage(InputStream input) {
System.out.println("WebSocket message Received!");
Image image = new Image(input);
imageView.setImage(image);
}
@OnClose
public void onClose() {
connectToWebSocket();
} - A method annotated by
-
Add the following imports:
import javafx.scene.image.Image;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
Use the following completed JavaFXBinaryWsClient
source code for reference:
package javafxbinarywsclient;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javax.websocket.ClientEndpoint;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
@ClientEndpoint
public class JavaFXBinaryWsClient extends Application {
private static final Logger LOGGER = Logger.getLogger(JavaFXBinaryWsClient.class.getName());
private ImageView imageView;
private Session session;
@OnOpen
public void onOpen(Session session) {
this.session = session;
}
@OnMessage
public void onMessage(InputStream input) {
System.out.println("WebSocket message Received!");
Image image = new Image(input);
imageView.setImage(image);
}
@OnClose
public void onClose() {
connectToWebSocket();
}
@Override
public void start(final Stage primaryStage) {
connectToWebSocket();
Button btn = new Button();
btn.setText("Send Image!");
btn.setPrefSize(400, 27);
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
selectAndSendImage(primaryStage);
}
});
imageView = new ImageView();
imageView.setFitHeight(400);
imageView.setFitWidth(400);
imageView.setPreserveRatio(true);
imageView.setSmooth(true);
AnchorPane root = new AnchorPane();
AnchorPane.setTopAnchor(btn, 0.0);
AnchorPane.setLeftAnchor(btn, 0.0);
AnchorPane.setRightAnchor(btn, 0.0);
AnchorPane.setTopAnchor(imageView, 27.0);
AnchorPane.setBottomAnchor(imageView, 0.0);
AnchorPane.setLeftAnchor(imageView, 0.0);
AnchorPane.setRightAnchor(imageView, 0.0);
root.getChildren().add(btn);
root.getChildren().add(imageView);
Scene scene = new Scene(root, 400, 427);
primaryStage.setTitle("Image push!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private void selectAndSendImage(Stage stage) {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select Image to Send");
File file = fileChooser.showOpenDialog(stage);
try (InputStream input = new FileInputStream(file);
OutputStream output = session.getBasicRemote().getSendStream()) {
byte[] buffer = new byte[1024];
int read;
while ((read = input.read(buffer)) > 0) {
output.write(buffer, 0, read);
}
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
}
private void connectToWebSocket() {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
try {
URI uri = URI.create("ws://localhost:8080/BinaryWebSocketServer/images");
container.connectToServer(this, uri);
} catch (DeploymentException | IOException ex) {
LOGGER.log(Level.SEVERE, null, ex);
System.exit(-1);
}
}
}
Running and Testing the Application
-
Run the
BinaryWebSocketServer
project: Right-click theBinaryWebSocketServer
project and select Run.View Image View Image The http://localhost:8080/BinaryWebSocketServer URL opens in a web browser after GlassFish server starts.
-
Run the
JavaFXBinaryWsClient
project: Right-click theJavaFXBinaryWsClient
project and select Run.View Image View Image A new window with the JavaFX application opens.
-
Repeat step 2 to run another instance of the JavaFXBinaryWsClient project. (Do not close the window.)
View Image Two windows are running.
-
In one of the windows, press the Send Image! button, browse for an image, and open it.
View Image View Image The image is displayed in both windows.
-
Open more instances of the JavaFX applications and send another image. All instances will receive and display the images.
Summary
In this tutorial, you learned to:
- Create Binary WebSocket services with Java EE7
- Create Binary WebSocket clients with Java FX
Next Steps
This is a simple tutorial for binary WebSockets. You can try to implement more complex scenarios, such as the following, and use this project as the basis:
- Save the image on the server to send it to newly connected users.
- Send the latest three images to the clients.
- Combine text messages with images to create an image-based chat client.
- Stream videos as a sequence of images.
Resources
To learn more about WebSockets or Java EE7, see the following resources:
- JSR 356: JavaTM API for WebSocket
- WebSocket Java EE7 Tutorial
- Java EE Technical Documentation
- Java FX Documentation
- Project Tyrus, WebSocket Reference Implementation.
- Using WebSocket for Real-Time Communication in Java Platform, Enterprise Edition 7 OBE
- Java EE 7: New Features
Credits
- Lead Curriculum Developer: Eduardo Moranchel