Search code examples
jsfjmsjsf-2.2java-ee-7java-websocket

Howto update a data table cell via mdb-> websocket in jee-7? (db pk, clientId mapping)


I have a jsf-2.2 primefaces datatable with paging. One column displays a status of a network component and on table loading I asynchronously query an external service over a resource adapter. When the data table is loaded the status cells are displayed with "Status unknown". Occasionally I will receive status packets from single network components as json in a Message Driven Bean. I then want to send this status via a websocket to the Browser to update the table cells. The json status packets contain the primary database keys of the network components, but on the javascript side in the browser, I need the clientIds of the data table cells. The clientIds have the form "switchTable:swths:2:switchActive" and only differ by the index in the middle.

My first idea was to write a facelet and overwrite the id with the primary key of the network component, but I think this is not the way to go.

Is there a recommended way to map the clientIds to the individual primary keys? This mapping would need to include the session, as there are multiple sessions with the same clientId. I want to update the table cell, that I found in DOM with document.getElementById immediately with the status text.


Solution

  • I came up with 2 solutions of this interesting problem.

    Solution 1

    • defining hidden table column with 'ID' text in it,
    • finding data table row and cell using javascript by iterating through all rows and matching 'value' of ID column cell

    Solution 2

    • utilizing JSF2 namespace xmlns:pt="http://xmlns.jcp.org/jsf/passthrough" to 'programatically' set html id attribute of column cell elements,
    • finding cell element with javascript's document.getElementById function

    Example

    xhtml page

    <html xmlns="http://www.w3.org/1999/xhtml"
        xmlns:h="http://java.sun.com/jsf/html"
        xmlns:f="http://java.sun.com/jsf/core"
        xmlns:p="http://primefaces.org/ui" 
        xmlns:pt="http://xmlns.jcp.org/jsf/passthrough" >
    
        <f:view contentType="text/html">
            <h:head>
                <h:outputScript library="primefaces" name="jquery/jquery.js" />
            </h:head>
    
            <h:body>
                <p:dataTable id="dataTable" widgetVar="dataTableWidget" value="#{switchController.switches}" var="switch" paginator="true" rows="5">
                    <p:column headerText="id" style="display:none">
                        <h:outputText value="#{switch.id}"/>
                    </p:column>
                    <p:column headerText="Name">
                        <h:outputText value="#{switch.name}"/>
                    </p:column>
                    <p:column headerText="status">
                        <h:outputText value="#{switch.status}" pt:id="dataTable:switch:#{switch.id}:status"/>
                    </p:column>
                </p:dataTable>
    
                <p:commandButton value="Change status" type="button" onclick="changeStatusExample()"/>
            </h:body>
        </f:view>
    </html>
    

    Backing Bean (just for the purpose of this example)

    public class SwitchController {
    
        List<Switch> switches;
    
        @PostConstruct
        public void init() {
            switches = new ArrayList<>();
            for (int i = 1; i < 11; i++) {
                switches.add(new Switch(i, "Switch " + i, "STATUS_UNKNOWN"));
            }
        }
    
        public List<Switch> getSwitches() {
            return switches;
        }
    
        public void setSwitches(List<Switch> switches) {
            this.switches = switches;
        }
    }
    

    where Switch is POJO with id, name and status fields.

    Javascript

    // SOLUTION 1
    function getTableCellByIdVer1(switchId, colNumber) {
        //get table rows
        var tableRows = PF('dataTableWidget').tbody[0].childNodes;
        //loop through rows    
        for (i = 0; i < tableRows.length; i++) {
            //get cells of current row
            var cells = tableRows[i].cells;
            //get value of hidden ID column cell
            var id = cells[0].innerText;
            if (id === switchId) {
                return tableRows[i].cells[colNumber];
            }
        }
        return null;
    }
    
    // SOLUTION 1
    function changeSwitchStatusVer1(changedSwitch) {
        var statusCell = getTableCellByIdVer1(changedSwitch.id, 2);
        if (statusCell) {
            //row exists...now we can change the status
            statusCell.innerText = changedSwitch.status;
        } else {
            console.log('Row with switch id=' + changedSwitch.id + ' not found');
        }
    }
    
    // SOLUTION 2
    function changeSwitchStatusVer2(changedSwitch) {
        //find cell element by html ID attribute given in xhtml
        var elementId='dataTable:switch:' + changedSwitch.id + ':status';
        var statusCell = document.getElementById(elementId);
        if (statusCell) {
            statusCell.innerText = changedSwitch.status;
        } else {
            console.log('Element with id=' + elementId + ' not found');
        }
    }
    
    // EXAMPLE
    function changeStatusExample() {
        //simulating situation when websocket pushes info about changed switch to browser page
        // SOLUTION 1
        var changedSwitch = {id: '2', status: 'STATUS_ON'};
        changeSwitchStatusVer1(changedSwitch);
    
        // SOLUTION 2
        //another switch status changed..using another approach to update table cell
        changedSwitch = {id: '4', status: 'STATUS_OFF'};
        changeSwitchStatusVer2(changedSwitch);
    }
    

    IMPORTANT: Note that both solutions will only work if (changed switch) ID's are part of currently 'visible' data table page.