Java EE 7: Building Web Applications with WebSocket, JavaScript and HTML5



Overview

Purpose

This tutorial shows you how to create an application that uses the WebSocket API for real-time communication between a client and a server. You learn how to:

  • Create a Java Platform, Enterprise Edition 7 (Java EE 7) application that uses the WebSocket API
  • Use the OnOpen and OnMessage WebSocket lifecycle events to perform different actions on the Java EE 7 application.
  • Define a client-side WebSocket endpoint by using JavaScript
  • Operate on Plain Old Java Objects (POJOs), in real-time, with actions invoked from a web browser client
Time to Complete

Approximately 1 hour

Introduction

Modern web applications require more interactivity than ever before for client/server communications. HTTP, however, wasn't built to deliver the kind of interactivity needed today. "Push" or Comet techniques, such as long-polling, emerged as a way to allow a server to push data to a browser. Because these techniques usually rely on HTTP, they present some disadvantages for client/server communications, such as HTTP overhead. These disadvantages result in less efficient communication between the server and the web browser, especially for real-time applications.

WebSocket provides an alternative to this limitation by providing bi-directional, full-duplex, real-time, client/server communications. The server can send data to the client at any time. Because WebSocket runs over TCP, it also reduces the overhead of each message. WebSocket also provides greater scalability for message-intensive applications because only one connection per client is used (whereas HTTP creates one request per message). Finally, WebSocket is part of Java EE 7, so you can use other technologies in the Java EE 7 stack.

Scenario

In this tutorial, you create Java WebSocket Home, a smart home control web application based on Java EE 7. Java WebSocket Home has a user interface for connecting and controlling fictitious devices from a web browser to a Java application. This application provides real-time updates to all clients that are connected to the Java WebSocket Home server.

Software Requirements

The following is a list of software requirements needed for this tutorial:

Prerequisites

Before starting this tutorial, you should have:

  • Knowledge of the Java programming language
  • Basic knowledge of Java EE 7
  • Basic knowledge of HTML 5, JavaScript, and cascading style sheets (CSS)
 

Introduction to the WebSocket API in Java EE 7

Introduced as part of the HTML 5 initiative, the WebSocket protocol is a standard web technology that simplifies communication and connection management between clients and a server. By maintaining a constant connection, WebSocket provides full-duplex client/server communication. It also provides a low-latency, low-level communication that works on the underlying TCP/IP connection.

The Java API for WebSocket (JSR-356) simplifies the integration of WebSocket into Java EE 7 applications.

Here are some of the features of the Java API for WebSocket:

  • Annotation-driven programming that allows developers to use POJOs to interact with WebSocket lifecycle events
  • Interface-driven programming that allows developers to implement interfaces and methods to interact with WebSocket lifecycle events
  • Integration with other Java EE technologies (You can inject objects and Enterprise JavaBeans by using components such as Contexts and Dependency Injection.)
 

Creating a Java EE 7 Project

In this section, you create a Java EE 7 web application.

  1. Open the NetBeans IDE.

  2. From the File menu, select New Project.

    tooltip text
  3. In the New Project dialog box, perform the following steps:

    1. Select Java Web from Categories.
    2. Select Web Application from Projects.
    3. Click Next.
    tooltip text
  4. Enter WebsocketHome as the project name and click Next.

    tooltip text
  5. In the New Web Application dialog box, perform the following steps:

    1. Select GlassFish Server from the Server list.
    2. Enter WebsocketHome as the context path.
    3. Click Finish.
    tooltip text

    The WebsocketHome project has been created.

    tooltip text
  6. Right-click the WebsocketHome project and select Run to test your application.

    tooltip text

    A browser window displays a TODO write content message.

    tooltip text

You successfully created a Java EE 7 web application by using NetBeans.

 

Creating the Device Model

In this section, you create the class that contains a device's attributes.

  1. Select File > New File.

    tooltip text
  2. In the New File dialog box, perform the following steps:

    1. Select Java from Categories.
    2. Select Java Class from File Types.
    3. Click Next.
    tooltip text
  3. In the New Java Class dialog box, perform the following steps:

    1. Enter Device as the class name.
    2. Enter org.example.model as the package.
    3. Click Finish.
    tooltip text

    The Device class is added to the project.

    tooltip text
  4. Add the following code to the Device.java class to define the class constructor, and its getter and setter methods:

    
    package org.example.model;
    
    public class Device {
    
        private int id;
        private String name;
        private String status;
        private String type;
        private String description;
    
        public Device() {
        }
        
        public int getId() {
            return id;
        }
        
        public String getName() {
            return name;
        }
    
        public String getStatus() {
            return status;
        }
    
        public String getType() {
            return type;
        }
        
        public String getDescription() {
            return description;
        }
    
        public void setId(int id) {
            this.id = id;
        }
        
        public void setName(String name) {
            this.name = name;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public void setType(String type) {
            this.type = type;
        }
        
        public void setDescription(String description) {
            this.description = description;
        }
    }
    
                                    
  5. Select File > Save to save the file.

    tooltip text

You successfully created the Device class.

 

Creating the WebSocket Server Endpoint

In this section, you create a WebSocket endpoint.

  1. Select File > New File.

    tooltip text
  2. In the New File dialog box, perform the following steps:

    1. Select Java from Categories.
    2. Select Java Class from File Types.
    3. Click Next.
    tooltip text
  3. In the New Java Class dialog box, perform the following steps:

    1. Enter DeviceWebSocketServer as the class name.
    2. Enter org.example.websocket as the package.
    3. Click Finish.
    tooltip text

    The DeviceWebSocketServer class is added to the project.

    tooltip text
  4. Define the WebSocket server endpoint path by adding the following code:

    
    package org.example.websocket;
    
    import javax.websocket.server.ServerEndpoint;
    
    @ServerEndpoint("/actions")
    public class DeviceWebSocketServer {
        
    }
    
                                    
  5. Define the WebSocket lifecycle annotations by adding the following methods and imports to the DeviceWebSocketServer class:

    
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
        
    @ServerEndpoint("/actions")
    public class DeviceWebSocketServer {
    
        @OnOpen
            public void open(Session session) {
        }
    
        @OnClose
            public void close(Session session) {
        }
    
        @OnError
            public void onError(Throwable error) {
        }
    
        @OnMessage
            public void handleMessage(String message, Session session) {
        }
    }    
    
                                    

    The WebSocket lifecycle annotations are mapped to Java methods. In this example, the @OnOpen annotation is mapped to the open() method; the @OnMessage annotation is mapped to the handleMessage()method; the @OnClose annotation to the close() method; and the @OnError annotation to the onError() method.

  6. Specify that the class is application-scoped by adding the @ApplicationScoped annotation and importing its package.

    
    ...
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.enterprise.context.ApplicationScoped;
    
    @ApplicationScoped
    @ServerEndpoint("/actions")
    public class DeviceWebSocketServer {
    ...
    }
    
                                    
  7. Save the file.

You successfully created the WebSocket server endpoint.

 

Creating the Session Handler

In this section, you create a class for handling the sessions that are connected to the server.

  1. Select File > New File.

    tooltip text
  2. In the New File dialog box, perform the following steps:

    1. Select Java from Categories.
    2. Select Java Class from File Types.
    3. Click Next.
    tooltip text
  3. In the New Java Class dialog box, perform the following steps:

    1. Enter DeviceSessionHandler as the class name.
    2. Enter org.example.websocket as the package.
    3. Click Finish.
    tooltip text

    The DeviceSessionHandler class is added to the project.

    tooltip text
  4. Specify that the class is application-scoped by adding the @ApplicationScoped annotation and importing its corresponding package.

    
    package org.example.websocket;
    
    import javax.enterprise.context.ApplicationScoped;
    
    @ApplicationScoped
    public class DeviceSessionHandler {
        
    }
                         
                                    
  5. Declare a HashSet for storing the list of devices added to the application and the active sessions in the application, and import their packages.

    
    package org.example.websocket;
    
    import javax.enterprise.context.ApplicationScoped;
    import java.util.HashSet;
    import java.util.Set;
    import javax.websocket.Session;
    import org.example.model.Device;
    
    @ApplicationScoped
    public class DeviceSessionHandler {
        private final Set<Session> sessions = new HashSet<>();
        private final Set<Device> devices = new HashSet<>();
    }
    
                                    

    Note: Each client connected to the application has its own session.

  6. Define the following methods for adding and removing sessions to the server.

    
    package org.example.websocket;
    
    ...
    
    @ApplicationScoped
    public class DeviceSessionHandler {
        
        ...
            
        public void addSession(Session session) {
            sessions.add(session);
        }
    
        public void removeSession(Session session) {
            sessions.remove(session);
        }
    }
    
                                    
  7. Define the methods that operate on the Device object.

    These methods are:

    • addDevice() - Add a device to the application.
    • removeDevice() - Remove a device from the application.
    • toggleDevice() - Toggle the device status.
    • getDevices() - Retrieve the list of devices and their attributes.
    • getDeviceById() - Retrieve a device with a specific identifier.
    • createAddMessage() - Build a JSON message for adding a device to the application.
    • sendToSession() - Send an event message to a client.
    • sendToAllConnectedSessions() - Send an event message to all connected clients.
    
    package org.example.websocket;
        
    ...    
    public class DeviceSessionHandler {    
        
        ...
        
        public List<Device> getDevices() {
            return new ArrayList<>(devices);
        }
    
        public void addDevice(Device device) {
        }
    
        public void removeDevice(int id) {
        }
    
        public void toggleDevice(int id) {
        }
    
        private Device getDeviceById(int id) {
            return null;
        }
    
        private JsonObject createAddMessage(Device device) {
            return null;
        }
    
        private void sendToAllConnectedSessions(JsonObject message) {
        }
    
        private void sendToSession(Session session, JsonObject message) {
        }
    }    
    
                                    
  8. Save the file.

You successfully created the session handler.

 

Rendering the User Interface

In this section, you create the Java WebSocket Home user interface (UI) by using HTML5 and CSS.

  1. Open the index.html file.

  2. Enter the following code to add the proper elements for adding and displaying devices on the web browser client.

    
    <!DOCTYPE html>
    <html>
        <head>
            <title></title>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <script src="websocket.js"></script>
            <link rel="stylesheet" type="text/css" href="style.css">
        </head>
        <body>
    
            <div id="wrapper">
                <h1>Java Websocket Home</h1>
                <p>Welcome to the Java WebSocket Home. Click the Add a device button to start adding devices.</p>
                <br />
                <div id="addDevice">
                    <div class="button"> <a href="#" OnClick="showForm()">Add a device</a> </div>
                    <form id="addDeviceForm">
                        <h3>Add a new device</h3>
                        <span>Name: <input type="text" name="device_name" id="device_name"></span>
                        <span>Type: 
                            <select id="device_type">
                                <option name="type" value="Appliance">Appliance</option>
                                <option name="type" value="Electronics">Electronics</option>
                                <option name="type" value="Lights">Lights</option>
                                <option name="type" value="Other">Other</option>
                            </select></span>
    
                        <span>Description:<br />
                            <textarea name="description" id="device_description" rows="2" cols="50"></textarea>
                        </span>
    
                        <input type="button" class="button" value="Add" onclick=formSubmit()>
                        <input type="reset" class="button" value="Cancel" onclick=hideForm()>
                    </form>
                </div>
                <br />
                <h3>Currently connected devices:</h3>
                <div id="content">
                </div>
            </div>
    
        </body>
    </html>
    
                                    
  3. Save the file.

  4. Select File > New File.

    tooltip text
  5. In the New File dialog dialog box, perform the following steps:

    1. Select Web from Categories.
    2. Select Cascading Style Sheet from File Types.
    3. Click Next.
    tooltip text
  6. In the New Cascading Style Sheet dialog box, perform the following steps:

    1. Enter style as the file name.
    2. Select Web Pages as the location.
    3. Click Finish.
    tooltip text

    The style.css file is added to the project.

    tooltip text
  7. Copy the following code into the style.css file.

    
    body {
        font-family: Arial, Helvetica, sans-serif;
        font-size: 80%;
        background-color: #1f1f1f;
    }
    
    #wrapper {
        width: 960px;
        margin: auto;
        text-align: left;
        color: #d9d9d9;
    }
    
    p {
        text-align: left;
    }
    
    .button {
        display: inline;
        color: #fff;
        background-color: #f2791d;
        padding: 8px;
        margin: auto;
        border-radius: 8px;
        -moz-border-radius: 8px;
        -webkit-border-radius: 8px;
        box-shadow: none;
        border: none;
    }
    
    .button:hover {
        background-color: #ffb15e;
    }
    .button a, a:visited, a:hover, a:active {
        color: #fff;
        text-decoration: none;
    }
    
    #addDevice {
        text-align: center;
        width: 960px;
        margin: auto;
        margin-bottom: 10px;
    }
    
    #addDeviceForm {
        text-align: left;
        width: 400px;
        margin: auto;
        padding: 10px;
    }
    
    #addDeviceForm span {
        display: block;
    }
    
    #content {
        margin: auto;
        width: 960px;
    }
    
    .device {
        width: 180px;
        height: 110px;
        margin: 10px;
        padding: 16px;
        color: #fff;
        vertical-align: top;
        border-radius: 8px;
        -moz-border-radius: 8px;
        -webkit-border-radius: 8px;
        display: inline-block;
    }
    
    .device.off {
        background-color: #c8cccf;
    }
    
    .device span {
        display: block;
    }
    
    .deviceName {
        text-align: center;
        font-weight: bold;
        margin-bottom: 12px;
    }
    
    .removeDevice {
        margin-top: 12px;
        text-align: center;
    }
    
    .device.Appliance {
        background-color: #5eb85e;
    }
    
    .device.Appliance a:hover {
        color: #a1ed82;
    }
    
    .device.Electronics {   
        background-color: #0f90d1;
    }
    
    .device.Electronics a:hover {
        color: #4badd1;
    }
    
    .device.Lights {
        background-color: #c2a00c;
    }
    
    .device.Lights a:hover {
        color: #fad232;
    }
    
    .device.Other {
        background-color: #db524d;
    }
    
    .device.Other a:hover {
        color: #ff907d;
    }
    
    .device a {
        text-decoration: none;
    }
    
    .device a:visited, a:active, a:hover {
        color: #fff;
    }
    
    .device a:hover {
        text-decoration: underline;
    }
    
                                    
  8. Save the file.

You successfully created the UI elements for the Java WebSocket Home application.

 

Creating the WebSocket Client Endpoint

In this section, you specify the client endpoint by using JavaScript.

  1. Select File > New File.

    tooltip text
  2. In the New File dialog box, perform the following steps:

    1. Select Web from Categories.
    2. Select JavaScript File from File Types.
    3. Click Next.
    tooltip text
  3. Enter websocket as the file name and click Finish.

    tooltip text

    The websocket.js file is added to the project.

    tooltip text

    The file performs the following actions:

    • Maps the WebSocket server endpoint to the URI defined in "Creating the WebSocket Server Endpoint".
    • Captures the JavaScript events for adding, removing, and changing a device's status and pushes those events to the WebSocket server. These methods are addDevice()removeDevice(), andtoggleDevice(). The actions are sent in JSON messages to the WebSocket server.
    • Defines a callback method for the WebSocket onmessage event. The onmessage event captures the events sent from the WebSocket server (in JSON) and processes those actions. In this application, these actions are usually rendering changes in the client UI.
    • Toggles the visibility of an HTML form for adding a new device.
  4. Add the following code to the websocket.js file.

    
    window.onload = init;
    var socket = new WebSocket("ws://localhost:8080/WebsocketHome/actions");
    socket.onmessage = onMessage;
    
    function onMessage(event) {
        var device = JSON.parse(event.data);
        if (device.action === "add") {
            printDeviceElement(device);
        }
        if (device.action === "remove") {
            document.getElementById(device.id).remove();
            //device.parentNode.removeChild(device);
        }
        if (device.action === "toggle") {
            var node = document.getElementById(device.id);
            var statusText = node.children[2];
            if (device.status === "On") {
                statusText.innerHTML = "Status: " + device.status + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn off</a>)";
            } else if (device.status === "Off") {
                statusText.innerHTML = "Status: " + device.status + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn on</a>)";
            }
        }
    }
    
    function addDevice(name, type, description) {
        var DeviceAction = {
            action: "add",
            name: name,
            type: type,
            description: description
        };
        socket.send(JSON.stringify(DeviceAction));
    }
    
    function removeDevice(element) {
        var id = element;
        var DeviceAction = {
            action: "remove",
            id: id
        };
        socket.send(JSON.stringify(DeviceAction));
    }
    
    function toggleDevice(element) {
        var id = element;
        var DeviceAction = {
            action: "toggle",
            id: id
        };
        socket.send(JSON.stringify(DeviceAction));
    }
    
    function printDeviceElement(device) {
        var content = document.getElementById("content");
        
        var deviceDiv = document.createElement("div");
        deviceDiv.setAttribute("id", device.id);
        deviceDiv.setAttribute("class", "device " + device.type);
        content.appendChild(deviceDiv);
    
        var deviceName = document.createElement("span");
        deviceName.setAttribute("class", "deviceName");
        deviceName.innerHTML = device.name;
        deviceDiv.appendChild(deviceName);
    
        var deviceType = document.createElement("span");
        deviceType.innerHTML = "<b>Type:</b> " + device.type;
        deviceDiv.appendChild(deviceType);
    
        var deviceStatus = document.createElement("span");
        if (device.status === "On") {
            deviceStatus.innerHTML = "<b>Status:</b> " + device.status + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn off</a>)";
        } else if (device.status === "Off") {
            deviceStatus.innerHTML = "<b>Status:</b> " + device.status + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn on</a>)";
            //deviceDiv.setAttribute("class", "device off");
        }
        deviceDiv.appendChild(deviceStatus);
    
        var deviceDescription = document.createElement("span");
        deviceDescription.innerHTML = "<b>Comments:</b> " + device.description;
        deviceDiv.appendChild(deviceDescription);
    
        var removeDevice = document.createElement("span");
        removeDevice.setAttribute("class", "removeDevice");
        removeDevice.innerHTML = "<a href=\"#\" OnClick=removeDevice(" + device.id + ")>Remove device</a>";
        deviceDiv.appendChild(removeDevice);
    }
    
    function showForm() {
        document.getElementById("addDeviceForm").style.display = '';
    }
    
    function hideForm() {
        document.getElementById("addDeviceForm").style.display = "none";
    }
    
    function formSubmit() {
        var form = document.getElementById("addDeviceForm");
        var name = form.elements["device_name"].value;
        var type = form.elements["device_type"].value;
        var description = form.elements["device_description"].value;
        hideForm();
        document.getElementById("addDeviceForm").reset();
        addDevice(name, type, description);
    }
    
    function init() {
        hideForm();
    }
    
                                    
  5. Save the file.

You successfully created the WebSocket client endpoint and the defined actions for handling WebSocket events in the client.

 

Processing WebSocket Events in the Server

In this section, you process WebSocket lifecycle events in the DeviceWebSocketServer class.

  1. Open the DeviceWebSocketServer class.

  2. Inject a DeviceSessionHandler object to process the WebSocket lifecycle events in each session and import its corresponding package.

    
    package org.example.websocket;
    
    ...
    import javax.websocket.server.ServerEndpoint;
    import javax.inject.Inject;
    
    @ApplicationScoped
    @ServerEndpoint("/actions")
    public class DeviceWebSocketServer {
        
        @Inject
        private DeviceSessionHandler sessionHandler;
        
        @OnOpen
        public void open(Session session) {
        }
        
        ...
    }    
    
                                    
  3. Process the OnMessage WebSocket lifecycle event by adding the following code to the open method.

    
        @OnOpen
        public void open(Session session) {
            sessionHandler.addSession(session);
        }
    
                                    

    The OnMessage method performs the following actions:

    • Reads device actions and attributes sent from the client.
    • Invokes the session handler to perform the proper operations on the specified Device object. In this application, the add action sent from the client invokes the addDevice method, the remove action invokes the removeDevice method, and the toggle action invokes the toggleDevice method.
  4. Process the OnOpen WebSocket event and add the missing imports.

    
    package org.example.websocket;
    
    ...
    import java.io.StringReader;
    import javax.json.Json;
    import javax.json.JsonObject;
    import javax.json.JsonReader;
    import org.example.model.Device;    
    
    ...
    
        @OnMessage
        public void handleMessage(String message, Session session) {
    
            try (JsonReader reader = Json.createReader(new StringReader(message))) {
                JsonObject jsonMessage = reader.readObject();
    
                if ("add".equals(jsonMessage.getString("action"))) {
                    Device device = new Device();
                    device.setName(jsonMessage.getString("name"));
                    device.setDescription(jsonMessage.getString("description"));
                    device.setType(jsonMessage.getString("type"));
                    device.setStatus("Off");
                    sessionHandler.addDevice(device);
                }
    
                if ("remove".equals(jsonMessage.getString("action"))) {
                    int id = (int) jsonMessage.getInt("id");
                    sessionHandler.removeDevice(id);
                }
    
                if ("toggle".equals(jsonMessage.getString("action"))) {
                    int id = (int) jsonMessage.getInt("id");
                    sessionHandler.toggleDevice(id);
                }
            }
        }
    
                                    

    The OnOpen event reads the attributes sent from the client in JSON and creates a new Device object with the specified parameters.

  5. Implement the OnClose and OnError actions and add the missing imports.

    
    package org.example.websocket;
    
    ...
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    ...
        
        @OnClose
        public void close(Session session) {
            sessionHandler.removeSession(session);
        }
    
        @OnError
        public void onError(Throwable error) {
            Logger.getLogger(DeviceWebSocketServer.class.getName()).log(Level.SEVERE, null, error);
        }
    
                                    
  6. Save the file.

You successfully processed WebSocket lifecycle events in the DeviceWebSocketServer class.

 

Implementing the WebSocket Actions in the Session Handler

In this section, you perform operations in the Device object by using the DeviceSessionHandler class.

  1. Open the DeviceSessionHandler.java class.

  2. Define a variable for storing the device identifiers in the server.

    
    ...
        
    public class DeviceSessionHandler {
    
        private int deviceId = 0;
        private final Set<Session> sessions = new HashSet<>();
        private final Set<Device> devices = new HashSet<>();
        
        ...
    
    }
    
                                    
  3. Add the for loop to the addSession method to send the list of devices to the connected client.

    
        public void addSession(Session session) {
            sessions.add(session);
            for (Device device : devices) {
                JsonObject addMessage = createAddMessage(device);
                sendToSession(session, addMessage);
            }
    
        }
    
                                    
  4. Implement the addDevice method by adding the following code.

    
        public void addDevice(Device device) {
            device.setId(deviceId);
            devices.add(device);
            deviceId++;
            JsonObject addMessage = createAddMessage(device);
            sendToAllConnectedSessions(addMessage);
        }
    
                                    

    The addDevice method performs the following actions:

    • Creates a new Device object with the current value of the deviceID variable and the parameters specified by the user in the client.
    • Sends a message, in JSON, to all sessions or active clients in the WebSocket server.
  5. Implement the removeDevice method.

    
        public void removeDevice(int id) {
            Device device = getDeviceById(id);
            if (device != null) {
                devices.remove(device);
                JsonProvider provider = JsonProvider.provider();
                JsonObject removeMessage = provider.createObjectBuilder()
                        .add("action", "remove")
                        .add("id", id)
                        .build();
                sendToAllConnectedSessions(removeMessage);
            }
        }
    
                                    

    The removeDevice method removes the device object specified by the user and sends a message, in JSON, to all sessions that are active in the WebSocket server.

  6. Implement the toggleDevice method.

    
        public void toggleDevice(int id) {
            JsonProvider provider = JsonProvider.provider();
            Device device = getDeviceById(id);
            if (device != null) {
                if ("On".equals(device.getStatus())) {
                    device.setStatus("Off");
                } else {
                    device.setStatus("On");
                }
                JsonObject updateDevMessage = provider.createObjectBuilder()
                        .add("action", "toggle")
                        .add("id", device.getId())
                        .add("status", device.getStatus())
                        .build();
                sendToAllConnectedSessions(updateDevMessage);
            }
        }
    
                                    

    The toggleDevice method toggles the device status and sends the event to all sessions that are still active in the WebSocket server.

  7. Implement missing methods.

    
        private Device getDeviceById(int id) {
            for (Device device : devices) {
                if (device.getId() == id) {
                    return device;
                }
            }
            return null;
        }
    
        private JsonObject createAddMessage(Device device) {
            JsonProvider provider = JsonProvider.provider();
            JsonObject addMessage = provider.createObjectBuilder()
                    .add("action", "add")
                    .add("id", device.getId())
                    .add("name", device.getName())
                    .add("type", device.getType())
                    .add("status", device.getStatus())
                    .add("description", device.getDescription())
                    .build();
            return addMessage;
        }
    
        private void sendToAllConnectedSessions(JsonObject message) {
            for (Session session : sessions) {
                sendToSession(session, message);
            }
        }
    
        private void sendToSession(Session session, JsonObject message) {
            try {
                session.getBasicRemote().sendText(message.toString());
            } catch (IOException ex) {
                sessions.remove(session);
                Logger.getLogger(DeviceSessionHandler.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    
                                    
  8. Save the file.

You successfully implemented the WebSocket actions in the session handler.

 

Testing the Java WebSocket Home Application

In this section, you test the Java WebSocket Home application.

  1. Right-click the WebsocketHome project and click Run to build and deploy the project.

    tooltip text

    A Web browser displays the Java WebSocketHome index page.

  2. Open another web browser and place it next to the first one.

    tooltip text
  3. In either window, click Add a device to display the "Add a new device" form.

    tooltip text
  4. In the "Add a new device form," perform the following steps:

    1. Enter Microwave as the name.
    2. Select Appliance as the type.
    3. Enter Kitchen as the description.
    4. Click Add.
    tooltip text

    A device is added to the Java WebSocket Home server and it is rendered in both web browsers.

    tooltip text
  5. Optional: Add more devices of different types.

    tooltip text
  6. On any device, click Turn on.

    tooltip text

    The device status changes to Off in the server and all clients.

    tooltip text
  7. On any device, click Remove device.

    tooltip text

    The device is removed from the server and all clients.

    tooltip text

 

Summary

Congratulations! You created a smart home control web application by using Java EE 7 and the WebSocket API.

You also learned how to:

  • Define a WebSocket server endpoint in a Java class by using the WebSocket API annotations
  • Send messages to and from the client to the WebSocket server in JSON
  • Use the WebSocket API lifecycle annotations to handle WebSocket events
  • Process WebSocket lifecycle events in the client by using HTML5 and JavaScript
Resources

For more information about the topics in this tutorial, see:

Credits
  • Lead Curriculum Developer: Miguel Salazar
  • Other Contributors: Eduardo Moranchel

Overview

Purpose

This tutorial shows you how to create an application that uses the WebSocket API for real-time communication between a client and a server. You learn how to:

  • Create a Java Platform, Enterprise Edition 7 (Java EE 7) application that uses the WebSocket API
  • Use the OnOpen and OnMessage WebSocket lifecycle events to perform different actions on the Java EE 7 application.
  • Define a client-side WebSocket endpoint by using JavaScript
  • Operate on Plain Old Java Objects (POJOs), in real-time, with actions invoked from a web browser client
Time to Complete

Approximately 1 hour

Introduction

Modern web applications require more interactivity than ever before for client/server communications. HTTP, however, wasn't built to deliver the kind of interactivity needed today. "Push" or Comet techniques, such as long-polling, emerged as a way to allow a server to push data to a browser. Because these techniques usually rely on HTTP, they present some disadvantages for client/server communications, such as HTTP overhead. These disadvantages result in less efficient communication between the server and the web browser, especially for real-time applications.

WebSocket provides an alternative to this limitation by providing bi-directional, full-duplex, real-time, client/server communications. The server can send data to the client at any time. Because WebSocket runs over TCP, it also reduces the overhead of each message. WebSocket also provides greater scalability for message-intensive applications because only one connection per client is used (whereas HTTP creates one request per message). Finally, WebSocket is part of Java EE 7, so you can use other technologies in the Java EE 7 stack.

Scenario

In this tutorial, you create Java WebSocket Home, a smart home control web application based on Java EE 7. Java WebSocket Home has a user interface for connecting and controlling fictitious devices from a web browser to a Java application. This application provides real-time updates to all clients that are connected to the Java WebSocket Home server.

Software Requirements

The following is a list of software requirements needed for this tutorial:

Prerequisites

Before starting this tutorial, you should have:

  • Knowledge of the Java programming language
  • Basic knowledge of Java EE 7
  • Basic knowledge of HTML 5, JavaScript, and cascading style sheets (CSS)
 

Introduction to the WebSocket API in Java EE 7

Introduced as part of the HTML 5 initiative, the WebSocket protocol is a standard web technology that simplifies communication and connection management between clients and a server. By maintaining a constant connection, WebSocket provides full-duplex client/server communication. It also provides a low-latency, low-level communication that works on the underlying TCP/IP connection.

The Java API for WebSocket (JSR-356) simplifies the integration of WebSocket into Java EE 7 applications.

Here are some of the features of the Java API for WebSocket:

  • Annotation-driven programming that allows developers to use POJOs to interact with WebSocket lifecycle events
  • Interface-driven programming that allows developers to implement interfaces and methods to interact with WebSocket lifecycle events
  • Integration with other Java EE technologies (You can inject objects and Enterprise JavaBeans by using components such as Contexts and Dependency Injection.)
 

Creating a Java EE 7 Project

In this section, you create a Java EE 7 web application.

  1. Open the NetBeans IDE.

  2. From the File menu, select New Project.

    tooltip text
  3. In the New Project dialog box, perform the following steps:

    1. Select Java Web from Categories.
    2. Select Web Application from Projects.
    3. Click Next.
    tooltip text
  4. Enter WebsocketHome as the project name and click Next.

    tooltip text
  5. In the New Web Application dialog box, perform the following steps:

    1. Select GlassFish Server from the Server list.
    2. Enter WebsocketHome as the context path.
    3. Click Finish.
    tooltip text

    The WebsocketHome project has been created.

    tooltip text
  6. Right-click the WebsocketHome project and select Run to test your application.

    tooltip text

    A browser window displays a TODO write content message.

    tooltip text

You successfully created a Java EE 7 web application by using NetBeans.

 

Creating the Device Model

In this section, you create the class that contains a device's attributes.

  1. Select File > New File.

    tooltip text
  2. In the New File dialog box, perform the following steps:

    1. Select Java from Categories.
    2. Select Java Class from File Types.
    3. Click Next.
    tooltip text
  3. In the New Java Class dialog box, perform the following steps:

    1. Enter Device as the class name.
    2. Enter org.example.model as the package.
    3. Click Finish.
    tooltip text

    The Device class is added to the project.

    tooltip text
  4. Add the following code to the Device.java class to define the class constructor, and its getter and setter methods:

    
    package org.example.model;
    
    public class Device {
    
        private int id;
        private String name;
        private String status;
        private String type;
        private String description;
    
        public Device() {
        }
        
        public int getId() {
            return id;
        }
        
        public String getName() {
            return name;
        }
    
        public String getStatus() {
            return status;
        }
    
        public String getType() {
            return type;
        }
        
        public String getDescription() {
            return description;
        }
    
        public void setId(int id) {
            this.id = id;
        }
        
        public void setName(String name) {
            this.name = name;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public void setType(String type) {
            this.type = type;
        }
        
        public void setDescription(String description) {
            this.description = description;
        }
    }
    
                                    
  5. Select File > Save to save the file.

    tooltip text

You successfully created the Device class.

 

Creating the WebSocket Server Endpoint

In this section, you create a WebSocket endpoint.

  1. Select File > New File.

    tooltip text
  2. In the New File dialog box, perform the following steps:

    1. Select Java from Categories.
    2. Select Java Class from File Types.
    3. Click Next.
    tooltip text
  3. In the New Java Class dialog box, perform the following steps:

    1. Enter DeviceWebSocketServer as the class name.
    2. Enter org.example.websocket as the package.
    3. Click Finish.
    tooltip text

    The DeviceWebSocketServer class is added to the project.

    tooltip text
  4. Define the WebSocket server endpoint path by adding the following code:

    
    package org.example.websocket;
    
    import javax.websocket.server.ServerEndpoint;
    
    @ServerEndpoint("/actions")
    public class DeviceWebSocketServer {
        
    }
    
                                    
  5. Define the WebSocket lifecycle annotations by adding the following methods and imports to the DeviceWebSocketServer class:

    
    import javax.websocket.OnClose;
    import javax.websocket.OnError;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.server.ServerEndpoint;
        
    @ServerEndpoint("/actions")
    public class DeviceWebSocketServer {
    
        @OnOpen
            public void open(Session session) {
        }
    
        @OnClose
            public void close(Session session) {
        }
    
        @OnError
            public void onError(Throwable error) {
        }
    
        @OnMessage
            public void handleMessage(String message, Session session) {
        }
    }    
    
                                    

    The WebSocket lifecycle annotations are mapped to Java methods. In this example, the @OnOpen annotation is mapped to the open() method; the @OnMessage annotation is mapped to the handleMessage()method; the @OnClose annotation to the close() method; and the @OnError annotation to the onError() method.

  6. Specify that the class is application-scoped by adding the @ApplicationScoped annotation and importing its package.

    
    ...
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.enterprise.context.ApplicationScoped;
    
    @ApplicationScoped
    @ServerEndpoint("/actions")
    public class DeviceWebSocketServer {
    ...
    }
    
                                    
  7. Save the file.

You successfully created the WebSocket server endpoint.

 

Creating the Session Handler

In this section, you create a class for handling the sessions that are connected to the server.

  1. Select File > New File.

    tooltip text
  2. In the New File dialog box, perform the following steps:

    1. Select Java from Categories.
    2. Select Java Class from File Types.
    3. Click Next.
    tooltip text
  3. In the New Java Class dialog box, perform the following steps:

    1. Enter DeviceSessionHandler as the class name.
    2. Enter org.example.websocket as the package.
    3. Click Finish.
    tooltip text

    The DeviceSessionHandler class is added to the project.

    tooltip text
  4. Specify that the class is application-scoped by adding the @ApplicationScoped annotation and importing its corresponding package.

    
    package org.example.websocket;
    
    import javax.enterprise.context.ApplicationScoped;
    
    @ApplicationScoped
    public class DeviceSessionHandler {
        
    }
                         
                                    
  5. Declare a HashSet for storing the list of devices added to the application and the active sessions in the application, and import their packages.

    
    package org.example.websocket;
    
    import javax.enterprise.context.ApplicationScoped;
    import java.util.HashSet;
    import java.util.Set;
    import javax.websocket.Session;
    import org.example.model.Device;
    
    @ApplicationScoped
    public class DeviceSessionHandler {
        private final Set<Session> sessions = new HashSet<>();
        private final Set<Device> devices = new HashSet<>();
    }
    
                                    

    Note: Each client connected to the application has its own session.

  6. Define the following methods for adding and removing sessions to the server.

    
    package org.example.websocket;
    
    ...
    
    @ApplicationScoped
    public class DeviceSessionHandler {
        
        ...
            
        public void addSession(Session session) {
            sessions.add(session);
        }
    
        public void removeSession(Session session) {
            sessions.remove(session);
        }
    }
    
                                    
  7. Define the methods that operate on the Device object.

    These methods are:

    • addDevice() - Add a device to the application.
    • removeDevice() - Remove a device from the application.
    • toggleDevice() - Toggle the device status.
    • getDevices() - Retrieve the list of devices and their attributes.
    • getDeviceById() - Retrieve a device with a specific identifier.
    • createAddMessage() - Build a JSON message for adding a device to the application.
    • sendToSession() - Send an event message to a client.
    • sendToAllConnectedSessions() - Send an event message to all connected clients.
    
    package org.example.websocket;
        
    ...    
    public class DeviceSessionHandler {    
        
        ...
        
        public List<Device> getDevices() {
            return new ArrayList<>(devices);
        }
    
        public void addDevice(Device device) {
        }
    
        public void removeDevice(int id) {
        }
    
        public void toggleDevice(int id) {
        }
    
        private Device getDeviceById(int id) {
            return null;
        }
    
        private JsonObject createAddMessage(Device device) {
            return null;
        }
    
        private void sendToAllConnectedSessions(JsonObject message) {
        }
    
        private void sendToSession(Session session, JsonObject message) {
        }
    }    
    
                                    
  8. Save the file.

You successfully created the session handler.

 

Rendering the User Interface

In this section, you create the Java WebSocket Home user interface (UI) by using HTML5 and CSS.

  1. Open the index.html file.

  2. Enter the following code to add the proper elements for adding and displaying devices on the web browser client.

    
    <!DOCTYPE html>
    <html>
        <head>
            <title></title>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <script src="websocket.js"></script>
            <link rel="stylesheet" type="text/css" href="style.css">
        </head>
        <body>
    
            <div id="wrapper">
                <h1>Java Websocket Home</h1>
                <p>Welcome to the Java WebSocket Home. Click the Add a device button to start adding devices.</p>
                <br />
                <div id="addDevice">
                    <div class="button"> <a href="#" OnClick="showForm()">Add a device</a> </div>
                    <form id="addDeviceForm">
                        <h3>Add a new device</h3>
                        <span>Name: <input type="text" name="device_name" id="device_name"></span>
                        <span>Type: 
                            <select id="device_type">
                                <option name="type" value="Appliance">Appliance</option>
                                <option name="type" value="Electronics">Electronics</option>
                                <option name="type" value="Lights">Lights</option>
                                <option name="type" value="Other">Other</option>
                            </select></span>
    
                        <span>Description:<br />
                            <textarea name="description" id="device_description" rows="2" cols="50"></textarea>
                        </span>
    
                        <input type="button" class="button" value="Add" onclick=formSubmit()>
                        <input type="reset" class="button" value="Cancel" onclick=hideForm()>
                    </form>
                </div>
                <br />
                <h3>Currently connected devices:</h3>
                <div id="content">
                </div>
            </div>
    
        </body>
    </html>
    
                                    
  3. Save the file.

  4. Select File > New File.

    tooltip text
  5. In the New File dialog dialog box, perform the following steps:

    1. Select Web from Categories.
    2. Select Cascading Style Sheet from File Types.
    3. Click Next.
    tooltip text
  6. In the New Cascading Style Sheet dialog box, perform the following steps:

    1. Enter style as the file name.
    2. Select Web Pages as the location.
    3. Click Finish.
    tooltip text

    The style.css file is added to the project.

    tooltip text
  7. Copy the following code into the style.css file.

    
    body {
        font-family: Arial, Helvetica, sans-serif;
        font-size: 80%;
        background-color: #1f1f1f;
    }
    
    #wrapper {
        width: 960px;
        margin: auto;
        text-align: left;
        color: #d9d9d9;
    }
    
    p {
        text-align: left;
    }
    
    .button {
        display: inline;
        color: #fff;
        background-color: #f2791d;
        padding: 8px;
        margin: auto;
        border-radius: 8px;
        -moz-border-radius: 8px;
        -webkit-border-radius: 8px;
        box-shadow: none;
        border: none;
    }
    
    .button:hover {
        background-color: #ffb15e;
    }
    .button a, a:visited, a:hover, a:active {
        color: #fff;
        text-decoration: none;
    }
    
    #addDevice {
        text-align: center;
        width: 960px;
        margin: auto;
        margin-bottom: 10px;
    }
    
    #addDeviceForm {
        text-align: left;
        width: 400px;
        margin: auto;
        padding: 10px;
    }
    
    #addDeviceForm span {
        display: block;
    }
    
    #content {
        margin: auto;
        width: 960px;
    }
    
    .device {
        width: 180px;
        height: 110px;
        margin: 10px;
        padding: 16px;
        color: #fff;
        vertical-align: top;
        border-radius: 8px;
        -moz-border-radius: 8px;
        -webkit-border-radius: 8px;
        display: inline-block;
    }
    
    .device.off {
        background-color: #c8cccf;
    }
    
    .device span {
        display: block;
    }
    
    .deviceName {
        text-align: center;
        font-weight: bold;
        margin-bottom: 12px;
    }
    
    .removeDevice {
        margin-top: 12px;
        text-align: center;
    }
    
    .device.Appliance {
        background-color: #5eb85e;
    }
    
    .device.Appliance a:hover {
        color: #a1ed82;
    }
    
    .device.Electronics {   
        background-color: #0f90d1;
    }
    
    .device.Electronics a:hover {
        color: #4badd1;
    }
    
    .device.Lights {
        background-color: #c2a00c;
    }
    
    .device.Lights a:hover {
        color: #fad232;
    }
    
    .device.Other {
        background-color: #db524d;
    }
    
    .device.Other a:hover {
        color: #ff907d;
    }
    
    .device a {
        text-decoration: none;
    }
    
    .device a:visited, a:active, a:hover {
        color: #fff;
    }
    
    .device a:hover {
        text-decoration: underline;
    }
    
                                    
  8. Save the file.

You successfully created the UI elements for the Java WebSocket Home application.

 

Creating the WebSocket Client Endpoint

In this section, you specify the client endpoint by using JavaScript.

  1. Select File > New File.

    tooltip text
  2. In the New File dialog box, perform the following steps:

    1. Select Web from Categories.
    2. Select JavaScript File from File Types.
    3. Click Next.
    tooltip text
  3. Enter websocket as the file name and click Finish.

    tooltip text

    The websocket.js file is added to the project.

    tooltip text

    The file performs the following actions:

    • Maps the WebSocket server endpoint to the URI defined in "Creating the WebSocket Server Endpoint".
    • Captures the JavaScript events for adding, removing, and changing a device's status and pushes those events to the WebSocket server. These methods are addDevice()removeDevice(), andtoggleDevice(). The actions are sent in JSON messages to the WebSocket server.
    • Defines a callback method for the WebSocket onmessage event. The onmessage event captures the events sent from the WebSocket server (in JSON) and processes those actions. In this application, these actions are usually rendering changes in the client UI.
    • Toggles the visibility of an HTML form for adding a new device.
  4. Add the following code to the websocket.js file.

    
    window.onload = init;
    var socket = new WebSocket("ws://localhost:8080/WebsocketHome/actions");
    socket.onmessage = onMessage;
    
    function onMessage(event) {
        var device = JSON.parse(event.data);
        if (device.action === "add") {
            printDeviceElement(device);
        }
        if (device.action === "remove") {
            document.getElementById(device.id).remove();
            //device.parentNode.removeChild(device);
        }
        if (device.action === "toggle") {
            var node = document.getElementById(device.id);
            var statusText = node.children[2];
            if (device.status === "On") {
                statusText.innerHTML = "Status: " + device.status + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn off</a>)";
            } else if (device.status === "Off") {
                statusText.innerHTML = "Status: " + device.status + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn on</a>)";
            }
        }
    }
    
    function addDevice(name, type, description) {
        var DeviceAction = {
            action: "add",
            name: name,
            type: type,
            description: description
        };
        socket.send(JSON.stringify(DeviceAction));
    }
    
    function removeDevice(element) {
        var id = element;
        var DeviceAction = {
            action: "remove",
            id: id
        };
        socket.send(JSON.stringify(DeviceAction));
    }
    
    function toggleDevice(element) {
        var id = element;
        var DeviceAction = {
            action: "toggle",
            id: id
        };
        socket.send(JSON.stringify(DeviceAction));
    }
    
    function printDeviceElement(device) {
        var content = document.getElementById("content");
        
        var deviceDiv = document.createElement("div");
        deviceDiv.setAttribute("id", device.id);
        deviceDiv.setAttribute("class", "device " + device.type);
        content.appendChild(deviceDiv);
    
        var deviceName = document.createElement("span");
        deviceName.setAttribute("class", "deviceName");
        deviceName.innerHTML = device.name;
        deviceDiv.appendChild(deviceName);
    
        var deviceType = document.createElement("span");
        deviceType.innerHTML = "<b>Type:</b> " + device.type;
        deviceDiv.appendChild(deviceType);
    
        var deviceStatus = document.createElement("span");
        if (device.status === "On") {
            deviceStatus.innerHTML = "<b>Status:</b> " + device.status + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn off</a>)";
        } else if (device.status === "Off") {
            deviceStatus.innerHTML = "<b>Status:</b> " + device.status + " (<a href=\"#\" OnClick=toggleDevice(" + device.id + ")>Turn on</a>)";
            //deviceDiv.setAttribute("class", "device off");
        }
        deviceDiv.appendChild(deviceStatus);
    
        var deviceDescription = document.createElement("span");
        deviceDescription.innerHTML = "<b>Comments:</b> " + device.description;
        deviceDiv.appendChild(deviceDescription);
    
        var removeDevice = document.createElement("span");
        removeDevice.setAttribute("class", "removeDevice");
        removeDevice.innerHTML = "<a href=\"#\" OnClick=removeDevice(" + device.id + ")>Remove device</a>";
        deviceDiv.appendChild(removeDevice);
    }
    
    function showForm() {
        document.getElementById("addDeviceForm").style.display = '';
    }
    
    function hideForm() {
        document.getElementById("addDeviceForm").style.display = "none";
    }
    
    function formSubmit() {
        var form = document.getElementById("addDeviceForm");
        var name = form.elements["device_name"].value;
        var type = form.elements["device_type"].value;
        var description = form.elements["device_description"].value;
        hideForm();
        document.getElementById("addDeviceForm").reset();
        addDevice(name, type, description);
    }
    
    function init() {
        hideForm();
    }
    
                                    
  5. Save the file.

You successfully created the WebSocket client endpoint and the defined actions for handling WebSocket events in the client.

 

Processing WebSocket Events in the Server

In this section, you process WebSocket lifecycle events in the DeviceWebSocketServer class.

  1. Open the DeviceWebSocketServer class.

  2. Inject a DeviceSessionHandler object to process the WebSocket lifecycle events in each session and import its corresponding package.

    
    package org.example.websocket;
    
    ...
    import javax.websocket.server.ServerEndpoint;
    import javax.inject.Inject;
    
    @ApplicationScoped
    @ServerEndpoint("/actions")
    public class DeviceWebSocketServer {
        
        @Inject
        private DeviceSessionHandler sessionHandler;
        
        @OnOpen
        public void open(Session session) {
        }
        
        ...
    }    
    
                                    
  3. Process the OnMessage WebSocket lifecycle event by adding the following code to the open method.

    
        @OnOpen
        public void open(Session session) {
            sessionHandler.addSession(session);
        }
    
                                    

    The OnMessage method performs the following actions:

    • Reads device actions and attributes sent from the client.
    • Invokes the session handler to perform the proper operations on the specified Device object. In this application, the add action sent from the client invokes the addDevice method, the remove action invokes the removeDevice method, and the toggle action invokes the toggleDevice method.
  4. Process the OnOpen WebSocket event and add the missing imports.

    
    package org.example.websocket;
    
    ...
    import java.io.StringReader;
    import javax.json.Json;
    import javax.json.JsonObject;
    import javax.json.JsonReader;
    import org.example.model.Device;    
    
    ...
    
        @OnMessage
        public void handleMessage(String message, Session session) {
    
            try (JsonReader reader = Json.createReader(new StringReader(message))) {
                JsonObject jsonMessage = reader.readObject();
    
                if ("add".equals(jsonMessage.getString("action"))) {
                    Device device = new Device();
                    device.setName(jsonMessage.getString("name"));
                    device.setDescription(jsonMessage.getString("description"));
                    device.setType(jsonMessage.getString("type"));
                    device.setStatus("Off");
                    sessionHandler.addDevice(device);
                }
    
                if ("remove".equals(jsonMessage.getString("action"))) {
                    int id = (int) jsonMessage.getInt("id");
                    sessionHandler.removeDevice(id);
                }
    
                if ("toggle".equals(jsonMessage.getString("action"))) {
                    int id = (int) jsonMessage.getInt("id");
                    sessionHandler.toggleDevice(id);
                }
            }
        }
    
                                    

    The OnOpen event reads the attributes sent from the client in JSON and creates a new Device object with the specified parameters.

  5. Implement the OnClose and OnError actions and add the missing imports.

    
    package org.example.websocket;
    
    ...
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    ...
        
        @OnClose
        public void close(Session session) {
            sessionHandler.removeSession(session);
        }
    
        @OnError
        public void onError(Throwable error) {
            Logger.getLogger(DeviceWebSocketServer.class.getName()).log(Level.SEVERE, null, error);
        }
    
                                    
  6. Save the file.

You successfully processed WebSocket lifecycle events in the DeviceWebSocketServer class.

 

Implementing the WebSocket Actions in the Session Handler

In this section, you perform operations in the Device object by using the DeviceSessionHandler class.

  1. Open the DeviceSessionHandler.java class.

  2. Define a variable for storing the device identifiers in the server.

    
    ...
        
    public class DeviceSessionHandler {
    
        private int deviceId = 0;
        private final Set<Session> sessions = new HashSet<>();
        private final Set<Device> devices = new HashSet<>();
        
        ...
    
    }
    
                                    
  3. Add the for loop to the addSession method to send the list of devices to the connected client.

    
        public void addSession(Session session) {
            sessions.add(session);
            for (Device device : devices) {
                JsonObject addMessage = createAddMessage(device);
                sendToSession(session, addMessage);
            }
    
        }
    
                                    
  4. Implement the addDevice method by adding the following code.

    
        public void addDevice(Device device) {
            device.setId(deviceId);
            devices.add(device);
            deviceId++;
            JsonObject addMessage = createAddMessage(device);
            sendToAllConnectedSessions(addMessage);
        }
    
                                    

    The addDevice method performs the following actions:

    • Creates a new Device object with the current value of the deviceID variable and the parameters specified by the user in the client.
    • Sends a message, in JSON, to all sessions or active clients in the WebSocket server.
  5. Implement the removeDevice method.

    
        public void removeDevice(int id) {
            Device device = getDeviceById(id);
            if (device != null) {
                devices.remove(device);
                JsonProvider provider = JsonProvider.provider();
                JsonObject removeMessage = provider.createObjectBuilder()
                        .add("action", "remove")
                        .add("id", id)
                        .build();
                sendToAllConnectedSessions(removeMessage);
            }
        }
    
                                    

    The removeDevice method removes the device object specified by the user and sends a message, in JSON, to all sessions that are active in the WebSocket server.

  6. Implement the toggleDevice method.

    
        public void toggleDevice(int id) {
            JsonProvider provider = JsonProvider.provider();
            Device device = getDeviceById(id);
            if (device != null) {
                if ("On".equals(device.getStatus())) {
                    device.setStatus("Off");
                } else {
                    device.setStatus("On");
                }
                JsonObject updateDevMessage = provider.createObjectBuilder()
                        .add("action", "toggle")
                        .add("id", device.getId())
                        .add("status", device.getStatus())
                        .build();
                sendToAllConnectedSessions(updateDevMessage);
            }
        }
    
                                    

    The toggleDevice method toggles the device status and sends the event to all sessions that are still active in the WebSocket server.

  7. Implement missing methods.

    
        private Device getDeviceById(int id) {
            for (Device device : devices) {
                if (device.getId() == id) {
                    return device;
                }
            }
            return null;
        }
    
        private JsonObject createAddMessage(Device device) {
            JsonProvider provider = JsonProvider.provider();
            JsonObject addMessage = provider.createObjectBuilder()
                    .add("action", "add")
                    .add("id", device.getId())
                    .add("name", device.getName())
                    .add("type", device.getType())
                    .add("status", device.getStatus())
                    .add("description", device.getDescription())
                    .build();
            return addMessage;
        }
    
        private void sendToAllConnectedSessions(JsonObject message) {
            for (Session session : sessions) {
                sendToSession(session, message);
            }
        }
    
        private void sendToSession(Session session, JsonObject message) {
            try {
                session.getBasicRemote().sendText(message.toString());
            } catch (IOException ex) {
                sessions.remove(session);
                Logger.getLogger(DeviceSessionHandler.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    
                                    
  8. Save the file.

You successfully implemented the WebSocket actions in the session handler.

 

Testing the Java WebSocket Home Application

In this section, you test the Java WebSocket Home application.

  1. Right-click the WebsocketHome project and click Run to build and deploy the project.

    tooltip text

    A Web browser displays the Java WebSocketHome index page.

  2. Open another web browser and place it next to the first one.

    tooltip text
  3. In either window, click Add a device to display the "Add a new device" form.

    tooltip text
  4. In the "Add a new device form," perform the following steps:

    1. Enter Microwave as the name.
    2. Select Appliance as the type.
    3. Enter Kitchen as the description.
    4. Click Add.
    tooltip text

    A device is added to the Java WebSocket Home server and it is rendered in both web browsers.

    tooltip text
  5. Optional: Add more devices of different types.

    tooltip text
  6. On any device, click Turn on.

    tooltip text

    The device status changes to Off in the server and all clients.

    tooltip text
  7. On any device, click Remove device.

    tooltip text

    The device is removed from the server and all clients.

    tooltip text

 

Summary

Congratulations! You created a smart home control web application by using Java EE 7 and the WebSocket API.

You also learned how to:

  • Define a WebSocket server endpoint in a Java class by using the WebSocket API annotations
  • Send messages to and from the client to the WebSocket server in JSON
  • Use the WebSocket API lifecycle annotations to handle WebSocket events
  • Process WebSocket lifecycle events in the client by using HTML5 and JavaScript
Resources

For more information about the topics in this tutorial, see:

Credits
  • Lead Curriculum Developer: Miguel Salazar
  • Other Contributors: Eduardo Moranchel
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值