Thursday, September 2, 2021

displaying busy indicator on primefaces wizard component

 Using primefaces 8 to build a java web application, I had trouble figuring out how to display a busy indicator from a primefaces wizard component.  Other views use onstart/oncomplete attributes for button actions to display a "loading dialog" that indicates the application is busy while the action completes.  That approach doesn't work for wizard navigation, because the action is performed asynchronously from the button click.  I found a stack overflow thread with a description of the solution.

In a nutshell, I used the "onnext" attribute of the p:wizard tag to open the busy dialog, and the flowListener method (onFlowProcess()) to close it.  Here are a couple of code snippets.

First the xhtml code for the wizard:

        <p:wizard id="importWizard"
                  flowListener="#{wizardController.onFlowProcess}" 
                  widgetVar="#{rootViewId}"
                  showStepStatus="false" 
                  showNavBar="false"
                  onnext="PF('loadingDialog').show();">

The code for the modal loading dialog:

    <p:dialog modal="true" 
              id="loadingDialog"
              widgetVar="loadingDialog" 
              showHeader="false" 
              styleClass="viewTransparentBackgroundDialog"
              resizable="false">
        <h:outputText value="Loading Results... Please Wait..." />
        <p/>
        <p:graphicImage library="images" name="ajax-loader.gif" />
    </p:dialog>

The java server code:

     public String onFlowProcess(FlowEvent event) {
        String result = onFlowProcessHandler(event);
        SessionUtility.executeRemoteCommand("PF('loadingDialog').hide();");
        return result;
    }

 

Wednesday, March 31, 2021

sql logging for jsf application in netbeans

To enable SQL logging to the netbeans console for a JSF application running in Payara with EclipseLink, add the following properties to the persistence.xml file: 
<property name="eclipselink.logging.level" value="FINE"/>
<property name="eclipselink.logging.logger" value="DefaultLogger"/>
More information here and here.

On a related note, logging can also be enabled directly in mysql, if that's the underlying database.  Here are some notes about doing that:

log in to mysql as root:
-> use mysql; 
-> SET global log_output = 'table'; 
-> SET GLOBAL general_log = 1;
then you can see the logged statements using:
select * from general_log;

or count the number of sql statements:
select count(*) from general_log; 
and clean it up with:
truncate general_log; 

Friday, March 12, 2021

java method reference

I can never remember the syntax for passing a method reference in Java, so I'm pasting a note about it here.  Here is a decent link explaining the idea.

Here is a method that accepts a method reference as a parameter, and calls that method in its body:

protected ItemDomainEntity findByPath_(String path, Function<ItemDomainEntity, ItemDomainEntity> parentGetterMethod) {

    <snip>
    ItemDomainEntity candidateParent = parentGetterMethod.apply(candidateItem);
    <snip>

}

And then an example of calling that method:

findByPath_(path, ItemDomainMachineDesign::getParentMachineDesign);


Tuesday, February 2, 2021

dynamic datatable components in primefaces

I'm making notes about this here for future reference. I have an JSF application where I'm using a primefaces datatable component. 

One thing that I don't love about the current approach is that the rows in the table are domain objects in the application, and each column therefore must be a property of the object in order to display the column values. Some of the columns I want to display in the table are not naturally properties of the underlying domain object, but just temporary values associated with the task at hand. So to add them as table columns, I have to define them as properties of the object and I don't always like that. 

 More than once I've wondered if it would be better to just use a list of strings as each row in the table. This way, some of the columns might map to properties of the domain object, while others are temporary task-specific values. Anyway, I'm stashing a couple of notes here about how to do this in case I want to experiment with it later. 

Here is a snippet for displaying lists of strings as the rows for the datatable based on this thread

                            <p:dataTable id="#{rootViewId}TableContent"
                                         value="#{wizardController.rows}"
                                         rendered="true"
                                         var="row">    

                                <p:columns value="#{wizardController.rows[0]}" var="column" columnIndexVar="i">
                                    #{row[i]}
                                </p:columns>

And here is a second thread for displaying dynamically selected columns. This still uses properties on the domain object, but I wonder if I could retrieve the values from some sort of map associated with the domain object instead? Here is a snippet of the view components from that thread: 

<h:form>
    <p:selectCheckboxMenu value="#{employeeBean.selectedColumns}"
                          label="Table Columns">
        <f:selectItems value="#{employeeBean.columnMap.entrySet()}"
                       var="entry"
                       itemValue="#{entry.key}"
                       itemLabel="#{entry.value}"/>

        <p:ajax event="change" update="table"/>
    </p:selectCheckboxMenu>
    <br/>

    <p:dataTable id="table" var="emp" value="#{employeeBean.employeeList}">
        <p:columns value="#{employeeBean.selectedColumns}" var="colKey">
            <f:facet name="header">
                <h:outputText value="#{employeeBean.columnMap[colKey]}"/>
            </f:facet>
            <h:outputText value="#{emp[colKey]}"/>
        </p:columns>
    </p:dataTable>
</h:form>
And the corresponding bean code:
@ManagedBean
@ViewScoped
public class EmployeeBean {
    private List<String> selectedColumns = new ArrayList<>();
    private List<Employee> employeeList = new ArrayList<>();
    private Map<String, String> columnMap = new LinkedHashMap<>();

    @PostConstruct
    private void postConstruct() {
        initColumnProperties();
        initEmployeeList();
    }

    private void initColumnProperties() {
        addColumn("id", "ID");
        addColumn("name", "Name");
        addColumn("phoneNumber", "Phone Number");
        addColumn("address", "Address");
        selectedColumns.addAll(columnMap.keySet());
    }

    private void addColumn(String propertyName, String displayName) {
        columnMap.put(propertyName, displayName);
    }

    private void initEmployeeList() {
        DataFactory dataFactory = new DataFactory();
        for (int i = 1; i < 20; i++) {
            Employee employee = new Employee();
            employee.setId(i);
            employee.setName(dataFactory.getName());
            employee.setPhoneNumber(String.format("%s-%s-%s", dataFactory.getNumberText(3),
                    dataFactory.getNumberText(3),
                    dataFactory.getNumberText(4)));
            employee.setAddress(dataFactory.getAddress() + "," + dataFactory.getCity());
            employeeList.add(employee);
        }
    }

    public List<Employee> getEmployeeList() {
        return employeeList;
    }

    public List<String> getSelectedColumns() {
        return selectedColumns;
    }

    public void setSelectedColumns(List<String> selectedColumns) {
        this.selectedColumns = selectedColumns;
    }

    public Map<String, String> getColumnMap() {
        return columnMap;
    }
}