Skip to content

Instantly share code, notes, and snippets.

@teocci
Last active July 6, 2016 07:29
Show Gist options
  • Save teocci/f02621f320736fa76490bccaea07f915 to your computer and use it in GitHub Desktop.
Save teocci/f02621f320736fa76490bccaea07f915 to your computer and use it in GitHub Desktop.

How to build a JAVA web app with WebSocket, JavaScript and HTML5

This tutorial shows how to create a JAVA application that uses the WebSocket API for real-time communication between a client and a server. In this tutorial, we will explore how to:

  • Create a JAVA application that uses the WebSocket API
  • Use the OnOpen and OnMessage WebSocket lifecycle events to perform different actions on the application.
  • Define a client-side WebSocket endpoint by using JavaScript
  • Operate on Java Objects, in real-time, with actions invoked from a web browser client

1. Introduction

Modern web applications require more interactivity than ever before for client/server communications. However, HTTP was not built to deliver the kind of interactivity needed today. "Push" 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. Finally,WebSocket also provides greater scalability for message-intensive applications because only one connection per client is used, whereas HTTP creates one request per message.

Scenario

In this tutorial, we are going to create a smart device control web application based on Java EE 7. This 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 Controller server.

Requirements

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

2. WebSocket API in Java

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 applications.

Creating a Java Project

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

  1. Let's open our favorite IDE.
  2. From the File menu, select New Project.
  3. In the New Project dialog box, perform the following steps:
    • Select Java Web from Categories.
    • Select Web Application from Projects.
    • Click Next.
  4. Enter ControllerServer as the project name and click Next.
  5. In the New Web Application dialog box, perform the following steps:
    • Select GlassFish Server from the Server list.
    • Enter ControllerServer as the context path.
    • Click Finish.
  6. Right-click the ControllerServer project and select Run to test your application.
    • A browser window displays an empty content page.

We successfully created a Java web application.

Creating the Device Model

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

  1. Select File > New File.

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

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

    • Enter Device as the class name.
    • Enter net.teocci.model as the package.
    • Click Finish.
  4. Add the following code to the Device.java class to define the class constructor, and its getter and setter methods:

    package net.teocci.model;
    
    import java.util.Random;
    
    /**
     * Created by teocci on 7/5/16.
     */
    public class Device {
    
        private int id;
        private String eventID;
        private String eventTitle;
        private String userID;
        private String status;
        private String color;
        private int assignedPort;
    
        public Device() {
            this.color = generateColor();
            this.assignedPort = generatePort();
        }
    
        public int getId() {
            return id;
        }
    
        public String getEventID() {
            return eventID;
        }
    
        public String getEventTitle() {
            return eventTitle;
        }
    
        public String getUserID() {
            return userID;
        }
    
        public String getStatus() {
            return status;
        }
    
        public String getColor() {
            return color;
        }
    
        public int getAssignedPort() {
            return assignedPort;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public void setEventID(String eventID) {
            this.eventID = eventID;
        }
    
        public void setEventTitle(String eventTitle) {
            this.eventTitle = eventTitle;
        }
    
        public void setUserID(String userID) {
            this.userID = userID;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        public void setAssignedPort(int assignedPort) {
            this.assignedPort = assignedPort;
        }
    
        private int generatePort() {
            Random rn = new Random();
            return rn.nextInt(9999 - 8100 + 1) + 8100;
        }
    
        private String generateColor() {
            Random rn = new Random();
            return "color0" + (rn.nextInt(9) + 1);
        }
    }
    
  5. Select File > Save to save the file.

We successfully created the Device class.

Creating the WebSocket Server Endpoint

In this section, you create a WebSocket endpoint.

  1. Select File > New File.

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

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

    • Enter DeviceWebSocketServer as the class name.
    • Enter net.teocci.websocket as the package.
    • Click Finish.
  4. Define the WebSocket server endpoint path by adding the following code:

    package net.teocci.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.

We 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.

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

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

    • Enter DeviceSessionHandler as the class name.
    • Enter net.teocci.websocket as the package.
    • Click Finish.
  4. Specify that the class is application-scoped by adding the @ApplicationScoped annotation and importing its corresponding package.

    package net.teocci.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 net.teocci.websocket;
    
    import javax.enterprise.context.ApplicationScoped;
    import java.util.HashSet;
    import java.util.Set;
    import javax.websocket.Session;
    import net.teocci.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 net.teocci.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 net.teocci.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.

We 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>Controller</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> Event:
                    <select id="device_event">
                        <option name="event" value="ev_apps_001">Applications Event</option>
                        <option name="event" value="ev_elec_001">Electronics Event</option>
                        <option name="event" value="ev_lgth_001">Lights Event</option>
                        <option name="event" value="ev_other_001">Other Event</option>
                    </select>
                </span>
                <span>UserID: <input type="text" name="device_userID" id="device_userID"></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.

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

    • Select Web from Categories.
    • Select Cascading Style Sheet from File Types.
    • Click Next.
  6. In the New Cascading Style Sheet dialog box, perform the following steps:

    • Enter style as the file name.
    • Select Web Pages as the location.
    • Click Finish.
  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;
    }
    
    .deviceEventTitle {
        text-align: center;
        font-weight: bold;
        margin-bottom: 12px;
    }
    
    .removeDevice {
        margin-top: 12px;
        text-align: center;
    }
    
    .device.Appliance a:hover {
        color: #a1ed82;
    }
    
    .device.Electronics a:hover {
        color: #4badd1;
    }
    
    .device.Lights a:hover {
        color: #fad232;
    }
    
    .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;
    }
    
    /* Background definitions*/
    
    .device.color01 {
        background-color: #5eb85e;
    }
    
    .device.color02 {
        background-color: #0f90d1;
    }
    
    .device.color03 {
        background-color: #c2a00c;
    }
    
    .device.color04 {
        background-color: #db524d;
    }
    
    .device.color05 {
        background-color: #007aff;
    }
    
    .device.color06 {
        background-color: #ff7000;
    }
    
    .device.color07 {
        background-color: #15e25f;
    }
    
    .device.color08 {
        background-color: #cfc700;
    }
    
    .device.color09 {
        background-color: #cf00be;
    }
    
  8. Save the file.

We 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.

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

    • Select Web from Categories.
    • Select JavaScript File from File Types.
    • Click Next.
  3. Enter websocket as the file name and click Finish. 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(), and toggleDevice(). 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.

    /**
     * Created by teocci on 7/5/16.
     */
    window.onload = init;
    var socket = new WebSocket("ws://192.168.1.160:8080/ControllerServer/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[3];
            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(eventID, eventTitle, userID) {
        var DeviceAction = {
            action: "add",
            eventID: eventID,
            eventTitle: eventTitle,
            userID: userID
        };
        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.color);
        content.appendChild(deviceDiv);
    
        var eventTitle = document.createElement("span");
        eventTitle.setAttribute("class", "deviceEventTitle");
        eventTitle.innerHTML = device.eventTitle;
        deviceDiv.appendChild(eventTitle);
    
        var eventID = document.createElement("span");
        eventID.innerHTML = "<b>Event ID:</b> " + device.eventID;
        deviceDiv.appendChild(eventID);
    
        var userID = document.createElement("span");
        userID.innerHTML = "<b>User ID:</b> " + device.userID;
        deviceDiv.appendChild(userID);
    
        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 devicePort = document.createElement("span");
        devicePort.innerHTML = "<b>Assigned Port:</b> " + device.assignedPort;
        deviceDiv.appendChild(devicePort);
    
        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 eventID = form.elements["device_event"].value;
        var eventTitle = getEventTitle("device_event");
        console.log(eventTitle);
        var userID = form.elements["device_userID"].value;
        hideForm();
        document.getElementById("addDeviceForm").reset();
        addDevice(eventID, eventTitle, userID);
    }
    
    function getEventTitle(elementId) {
        var elt = document.getElementById(elementId);
    
        if (elt.selectedIndex == -1)
            return null;
    
        return elt.options[elt.selectedIndex].text;
    }
    
    function init() {
        hideForm();
    }
    
  5. Save the file.

We 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 net.teocci.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 net.kseek.websocket;
    
    import net.kseek.model.Device;
    ...
    import java.io.StringReader;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javax.json.Json;
    import javax.json.JsonObject;
    import javax.json.JsonReader;
    
    ...
    
        @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.setEventID(jsonMessage.getString("eventID"));
                    device.setEventTitle(jsonMessage.getString("eventTitle"));
                    device.setUserID(jsonMessage.getString("userID"));
                    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 net.kseek.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.

We 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("eventID", device.getEventID())
                    .add("eventTitle", device.getEventTitle())
                    .add("userID", device.getUserID())
                    .add("status", device.getStatus())
                    .add("color", device.getColor())
                    .add("assignedPort", device.getAssignedPort())
                    .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.

We successfully implemented the WebSocket actions in the session handler.

Testing our Controller Application

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

  1. Right-click the ControllerServer project and click Run to build and deploy the project. A Web browser displays the Java WebSocketHome index page.

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

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

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

    • Enter Microwave as the name.
    • Select Appliance as the type.
    • Enter Kitchen as the description.
    • Click Add.

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

  5. Optional: Add more devices of different types.

  6. On any device, click Turn on.

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

  7. On any device, click Remove device.

    The device is removed from the server and all clients.

Summary

Congratulations everyone! We created a control web application by using Java and the WebSocket API.

Finally, we 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment