Joget DX 8 Stable Released
The stable release for Joget DX 8 is now available, with a focus on UX and Governance.
Table of Contents |
---|
...
This article demonstrate the implementation of the is a guide for implementing web socket support as function into a pluginJoget application.
...
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) public void onMessage(String message, Session session) public void onClose(Session session) |
Code Block | ||
---|---|---|
| ||
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"; |
Pay attention to the onMessage method (public void onMessage(String message, Session session)
Code Block | ||
---|---|---|
| ||
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 FormRowSetString formatDatagetVersion(FormData formData) { FormRowSet rowSet = null; return "5.0.0"; } @Override public // get valueString getClassName() { String id = getPropertyString(FormUtil.PROPERTY_IDreturn getClass().getName(); } if (id != null) {@Override public String getLabel() { String value = FormUtil.getElementPropertyValue(this, formData); //support i18n if (value != null) {return AppPluginUtil.getMessage("org.joget.WebSocketPlugin.pluginLabel", getClassName(), MESSAGE_PATH); } @Override public String // set value into Properties and FormRowSet objectgetDescription() { //support i18n FormRow result = new FormRow(return AppPluginUtil.getMessage("org.joget.WebSocketPlugin.pluginDesc", getClassName(), MESSAGE_PATH); } @Override public String result.setProperty(id, value);getPropertyOptions() { return rowSet = new FormRowSet(); AppUtil.readPluginResource(getClass().getName(), "/properties/form/" + getName() + ".json", null, true, MESSAGE_PATH); } @Override public FormRowSet formatData(FormData rowSet.add(result);formData) { FormRowSet rowSet = }null; } // get value return rowSet; String id } = getPropertyString(FormUtil.PROPERTY_ID); @Override public Stringif renderTemplate(FormDataid formData, Map dataModel!= null) { String templatevalue = "webSocketPlugin.ftl"FormUtil.getElementPropertyValue(this, formData); WorkflowUserManager wum = (WorkflowUserManager) AppUtil.getApplicationContext().getBean("workflowUserManager"); if (value != null) { String username = wum.getCurrentUsername(); // set value into Properties dataModel.put("username", username); and FormRowSet object String html = FormUtil.generateElementHtml(this, formData, template, dataModel FormRow result = new FormRow(); return html; } @Overrideresult.setProperty(id, value); public String getFormBuilderTemplate() { return "<labelrowSet class='label'>" +new getLabelFormRowSet(); + "</label>"; } @Override public String getFormBuilderCategoryrowSet.add(result) {; return FormBuilderPalette.CATEGORY_CUSTOM; } @Override public} void onOpen(Session session) { return rowSet; try {} @Override public String renderTemplate(FormData formData, Map session.getBasicRemote().sendText("Connection established");dataModel) { }String catchtemplate (IOException e) {= "webSocketPlugin.ftl"; WorkflowUserManager wum = (WorkflowUserManager) eAppUtil.printStackTracegetApplicationContext().getBean("workflowUserManager"); } String username = }wum.getCurrentUsername(); dataModel.put("username", username); @Override String publichtml void= onMessage(String message, Session session) {FormUtil.generateElementHtml(this, formData, template, dataModel); tryreturn {html; } @Override public String session.getBasicRemotegetFormBuilderTemplate().sendText("Server received: " + message); { return "<label } catch (IOException e) {class='label'>" + getLabel() + "</label>"; } @Override e.printStackTracepublic String getFormBuilderCategory(); { }return FormBuilderPalette.CATEGORY_CUSTOM; } @Override public void onCloseonOpen(Session session) { try { LogUtil.info(getClassName(), "Webscoket connection closed session.getBasicRemote().sendText("Connection established"); } } catch (IOException e) @Override{ public void onError(Session session, Throwable throwable) { e.printStackTrace(); LogUtil.error(getClassName(), throwable, ""); } } @Override public booleanvoid isEnabled(onMessage(String message, Session session) { try if ("true".equalsIgnoreCase(getPropertyString("enableWebsocket"))) { { session.getBasicRemote().sendText("Server received: " return+ truemessage); } else catch (IOException e) { return falsee.printStackTrace(); } } } |
Figure 1:
Code Block | ||
---|---|---|
| ||
<div class="form-cell" ${elementMetaData!}> <label class="label"> @Override public void onClose(Session session) { LogUtil.info(getClassName(), "Webscoket connection closed"); } ${element.properties.label} <span class="form-cell-validator">${decoration}</span> @Override public void onError(Session <#if error??>session, Throwable throwable) { LogUtil.error(getClassName(), throwable, ""); <span class="form-error-message">${error}</span>} public boolean </#if>isEnabled() { </label> <#ifif (element.properties.enableWebsocket! == 'true')> "true".equalsIgnoreCase(getPropertyString("enableWebsocket"))) { <input id="isEnabled" name="isEnabled" type="hidden" /> return true; </#if> <#if (element.properties.readonly! == 'true' && element.properties.readonlyLabel! == 'true') > } else { return false; <div class="form-cell-value"><span>${value!?html}</span></div> } <input id="${elementParamName!}" } } |
A 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:
The following 4 functions are event handlers that handles different WebSocket Events : ws.onopen = function(event) |
Code Block | ||
---|---|---|
| ||
<div class="form-cell" ${elementMetaData!}> <label class="label"> ${element.properties.label} <span class="form-cell-validator">${decoration}</span> 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) { <#if error??> const<span ws = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "${request.contextPath}/web/socket/plugin/org.joget.WebSocketPlugin"); ws.onopen = function(event) {class="form-error-message">${error}</span> </#if> </label> <#if (element.properties.enableWebsocket! == 'true')> <input id="isEnabled" name="isEnabled" type="hidden" /> console.log(event);</#if> <#if (element.properties.readonly! == 'true' && element.properties.readonlyLabel! == 'true') > $("#output").append('Connection opened with timeStamp: ' + event.timeStamp + '<br/>');<div class="form-cell-value"><span>${value!?html}</span></div> <input $("#sendButton").on("click", function(){ id="${elementParamName!}" name="${elementParamName!}" type="hidden" value="${value!?html}" /> <#else> <input type="text" id="messageInput" placeholder="Enter message"> const message = $("#messageInput").val(); <button id="sendButton">Send</button> <button id="closeButton">Close</button><br> //send message to endpoint<div id='output'></div> </#if> <script> ws.send(message); $(document).ready(function() { $("#sendButton").off("click"); $("#output#closeButton").appendoff("${username} send " + message + '<br/>'); click"); if ($("#messageInput#isEnabled").val(""); length > 0) { const ws = new return false; WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "${request.contextPath}/web/socket/plugin/org.joget.WebSocketPlugin"); ws.onopen = }); function(event) { $("#closeButton").on("click", function(){console.log(event); $("#output").append('Connection opened with timeStamp: if (ws.readyState === WebSocket.OPEN) {' + event.timeStamp + '<br/>'); //close the endpoint connection$("#sendButton").on("click", function(){ const message ws.close= $("#messageInput").val(); } //send message to endpoint $("#sendButton").hide(ws.send(message); $("#closeButton#output").hide();append("${username} send " + message + '<br/>'); return false;$("#messageInput").val(""); return false; }); }); ws.onmessage = function$(event) "#closeButton").on("click", function(){ $("#output").append(event.data + '<br/>'); if (ws.readyState === WebSocket.OPEN) { }; ws.onclose = function(event) {//close the endpoint connection $("#output").append('Connection closed with timeStamp: ' + event.timeStamp + '<br/>' ws.close(); $("#output").append('WebSocket closed<br/>'); } }; $("#sendButton").hide(); ws.onError = function(event) { $("#output#closeButton").append("Error: " + event.data + '<br/>'hide(); }; return false; } else { $("#output").html('WebSocket is not enable.<br>'); }); }; } ws.onmessage = }); function(event) { </script> <div style="clear:both;"></div> </div> $("#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> |
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.