Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

...

What's this for?

This article demonstrate the implementation of the web socket support plugin. The plugin is available as a form element.

...

Step Examples

Step 1: Drag form element & enable websocket

Drag "Web Socket" form element into the form and tick on the "Enable Websocket" in the configuration. Feel free to make any changes to other properties.

Image Removed

Figure 1: 

Step 2: Testing the plugin

At Runtime, enter a message as seen in figure 2 then send it afterwards.
Image Removed

Figure 2:

Image Removed

Figure 3:

Step 3:

Image Removed

Figure 4: 

Image Removed

Figure 5:

...

is a guide for implementing web socket support function into a Joget application.

Prerequisites

  1. Identify the type of plugin you want to develop. Refer: Introduction to Plugin Architecture
  2. You are required to have a plugin project ready. Refer: Guideline for Developing a Plugin

Plugin Info

The following is an example of implementing the WebSocket feature inside a radio form element. It showcases one of the ways to create a WebSocket plugin and the code belongs to the plugin class. 

Info

There are 4 key web socket methods that you are required to pay attention to:

public void onOpen(Session session)
- This method will be invoked when a WebSocket connection is opened. It send a specified text message to client side.

public void onMessage(String message, Session session) 
- This method will be invoked when a message is received on the WebSocket. It will send a response back to the client to indicate that the server has received the message.

public void onClose(Session session) 
- This method will be invoked when WebSocket connection is closed. It will log an information message indicating that the connection is closed.

public void onError(Session session, Throwable throwable)
- This method will be invoked when an error occurs in the WebSocket communication. It will log an error message.

Code Block
languagejava
package org.joget;

import java.io.IOException;
import java.util.Map;

import javax.websocket.Session;

import org.joget.apps.app.service.AppPluginUtil;
import org.joget.apps.app.service.AppUtil;
import org.joget.apps.form.lib.Radio;
import org.joget.apps.form.model.FormBuilderPalette;
import org.joget.apps.form.model.FormData;
import org.joget.apps.form.model.FormRow;
import org.joget.apps.form.model.FormRowSet;
import org.joget.apps.form.service.FormUtil;
import org.joget.commons.util.LogUtil;
import org.joget.plugin.base.PluginWebSocket;
import org.joget.workflow.model.service.WorkflowUserManager;

public class WebSocketPlugin extends Radio implements PluginWebSocket {

    private final static String MESSAGE_PATH = "message/form/WebSocketPlugin";

    @Override
    public String getName() {
        return "WebSocketPlugin";
    }

    @Override
    public String getVersion() {
        return "5.0.0";
    }

    @Override
    public String getClassName() {
        return getClass().getName();
    }

    @Override
    public String getLabel() {
        //support i18n
        return AppPluginUtil.getMessage("org.joget.WebSocketPlugin.pluginLabel", getClassName(), MESSAGE_PATH);
    }

    @Override
    public String getDescription() {
        //support i18n
        return AppPluginUtil.getMessage("org.joget.WebSocketPlugin.pluginDesc", getClassName(), MESSAGE_PATH);
    }

    @Override
    public String getPropertyOptions() {
        return AppUtil.readPluginResource(getClass().getName(), "/properties/form/" + getName() + ".json", null, true, MESSAGE_PATH);

    }

    @Override
    public FormRowSet formatData(FormData formData) {

        FormRowSet rowSet = null;

        // get value
        String id = getPropertyString(FormUtil.PROPERTY_ID);
        if (id != null) {
            String value = FormUtil.getElementPropertyValue(this, formData);
            if (value != null) {
                // set value into Properties and FormRowSet object
                FormRow result = new FormRow();
                result.setProperty(id, value);
                rowSet = new FormRowSet();
                rowSet.add(result);
            }
        }
        return rowSet;
    }

    @Override
    public String renderTemplate(FormData formData, Map dataModel) {
        String template = "webSocketPlugin.ftl";

        WorkflowUserManager wum = (WorkflowUserManager) AppUtil.getApplicationContext().getBean("workflowUserManager");
        String username = wum.getCurrentUsername();
        dataModel.put("username", username);

        String html = FormUtil.generateElementHtml(this, formData, template, dataModel);
        return html;
    }

    @Override
    public String getFormBuilderTemplate() {
        return "<label class='label'>" + getLabel() + "</label>";
    }

    @Override
    public String getFormBuilderCategory() {
        return FormBuilderPalette.CATEGORY_CUSTOM;
    }

    @Override
    public void onOpen(Session session) {
        try {
            session.getBasicRemote().sendText("Connection established");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onMessage(String message, Session session) {
        try {
            session.getBasicRemote().sendText("Server received: " + message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClose(Session session) {
        LogUtil.info(getClassName(), "Webscoket connection closed");
    }
    
    @Override
    public void onError(Session session, Throwable throwable) {
        LogUtil.error(getClassName(), throwable, "");
    }

    public boolean isEnabled() {
        if ("true".equalsIgnoreCase(getPropertyString("enableWebsocket"))) {
            return true;
        } else {
            return false;
        }
    }

}

FTL file is required for rendering the HTML content for the WebSocket plugin, you may copy the following code as a starting template.

Info

The endpoint is specified at this line:

Code Block
const ws = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "${request.contextPath}/web/socket/plugin/org.joget.WebSocketPlugin");

The following 4 functions are event handlers that handles different WebSocket Events :

ws.onopen = function(event)
ws.onmessage = function(event)
ws.onclose = function(event)
ws.onError = function(event)

You may make changes to any of the FTL code as it suits.


Code Block
languagejs
<div class="form-cell" ${elementMetaData!}>
    <label class="label">
        ${element.properties.label} <span class="form-cell-validator">${decoration}</span>
        <#if error??>
            <span class="form-error-message">${error}</span>
        </#if>
    </label>
    <#if (element.properties.enableWebsocket! == 'true')>
        <input id="isEnabled" name="isEnabled" type="hidden" />
    </#if>

    <#if (element.properties.readonly! == 'true' && element.properties.readonlyLabel! == 'true') >
        <div class="form-cell-value"><span>${value!?html}</span></div>
        <input id="${elementParamName!}" name="${elementParamName!}" type="hidden" value="${value!?html}" />
    <#else>    
        <input type="text" id="messageInput" placeholder="Enter message">
        <button id="sendButton">Send</button>
        <button id="closeButton">Close</button><br>
        <div id='output'></div>
    </#if>

    <script>
    $(document).ready(function() {
        $("#sendButton").off("click");
        $("#closeButton").off("click");

        if ($("#isEnabled").length > 0) {

            const ws = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "${request.contextPath}/web/socket/plugin/org.joget.WebSocketPlugin");

            ws.onopen = function(event) {
                console.log(event);
                $("#output").append('Connection opened with timeStamp: ' + event.timeStamp + '<br/>');
   
                $("#sendButton").on("click", function(){
                    const message = $("#messageInput").val();

                    //send message to endpoint
                    ws.send(message);  
                    $("#output").append("${username} send " + message + '<br/>'); 
                    $("#messageInput").val("");
                    return false;
                });

                $("#closeButton").on("click", function(){
                    if (ws.readyState === WebSocket.OPEN) {
                        //close the endpoint connection
                        ws.close();
                    }
                    $("#sendButton").hide();
                    $("#closeButton").hide();
                    return false;       
                });
            }; 

            ws.onmessage = function(event) {
                $("#output").append(event.data + '<br/>');
            }; 

            ws.onclose = function(event) {
                $("#output").append('Connection closed with timeStamp: ' + event.timeStamp + '<br/>');
                $("#output").append('WebSocket closed<br/>');
            }; 

            ws.onError = function(event) {
                $("#output").append("Error: " + event.data + '<br/>');
            };  
        
        } else {
            $("#output").html('WebSocket is not enable.<br>');
        }
    });
    </script>
    <div style="clear:both;"></div>
</div>

Sample 

This sample plugin code is available at https://github.com/jogetoss/sample-web-socket-plugin. JogetOSS is a community-led team for open source software related to the Joget no-code/low-code application platform. Projects under JogetOSS are community-driven and community-supported, and you are welcome to contribute to the projects.