JDeveloper 12c: using Expression Language in pageDef to switch ControllerClass

An interesting question came up late 2018 in the JDeveloper & ADF forum. A user asked how to use Expression Language (EL) in a pageDef file to switch the ControllerClass at runtime depending on some condition of a page.

The ControllerClass can be used to add custom code into the lifecycle of a page or fragment (see the full details at 27.4 Customizing the ADF Page Lifecycle). A tip

Tip:
You can specify the value of the page definition’s ControllerClass attribute as a fully qualified class name or you can enter an EL expression that resolves to a class directly in the ControllerClass field.
When using an EL expression for the value of the ControllerClass attribute, the Structure window may show a warning indicating that e “#{YourExpression}” is not a valid class. You can safely ignore this warning.

given in the documentation mentioned that you can use EL to specify the ControllerClass. The missing information is exactly how to do it.

Use Case

For a JSF page, a ControllerClass should be defined at runtime. The selected ControllerClass should depend on a condition.

Solution

There are a couple of blogs available which use a custom ControllerClass in a pageDef, but they use the direct specification of the custom class in the pageDef. Only one sample (https://github.com/oracle/adf-samples/releases/download/v1.0.0/OnPageLoad.zip) from Duncan Mills uses EL to set the ControllerClass. However, this sample was built for JDev 10.3.1! None of the samples I found use EL to switch the ControllerClass at runtime.

In summary, it’s time for a fresh sample using JDev 12.2.1.3

Building the UI

We start by creating a new Fusion Web Application from the gallery. The steps to follow can be looked up at Why and how to write reproducible test cases, so I skip them here.

Once the basic Fusion Web Application is built we open the adfc-config.xml (the unbounded ADF task flow) and add a page onto it.

We name the page index and create it using a Quick Layout. I normally use

but you can use whatever layout like. We add a Text to the header section and an af:inputText and an af:button to the content section. The page markup will look like

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE html>
<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:document title="index.jsf" id="d1">
        <af:form id="f1">
            <af:panelGridLayout id="pgl1">
                <af:gridRow height="50px" id="gr2">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc1">
                        <!-- Header -->
                        <af:outputText value="Using EL to switch ControllerClass" id="ot1"
                                       inlineStyle="font-size:x-large;"/>
                    </af:gridCell>
                </af:gridRow>
                <af:gridRow height="100%" id="gr1">
                    <af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
                        <!-- Content -->
                        <af:panelGroupLayout id="pgl2" layout="horizontal">
                            <af:inputText label="Use Listener" id="it1"/>
                            <af:button text="Update" id="b1"/>
                        </af:panelGroupLayout>
                    </af:gridCell>
                </af:gridRow>
            </af:panelGridLayout>
        </af:form>
    </af:document>
</f:view>

Next, we open the properties of the af:button and edit the actionListener property. Here we create a new bean, name it ‘IndexBean’, create a new method ‘updatePage’. The bean we create in sessionScope. Once the bean has been created, we delete the actionListener from the af:button we just created. The ‘updatePage’ method in the IndexBean we delete too. We don’t need the listener and I only used it to create the bean in the right scope for other things.

I created the bean in session scope as I will use it to store the data from the inputText we added to the page. This is just a convenience, we could have used a pageDef variable for this.

We add a String property ‘usePPListener’ to the IndexBean add the needed getter and setter methods.

public class IndexBean {
    String usePPListener = "1";

    public IndexBean() {
    }

    public void setUsePPListener(String usePPListener) {
        this.usePPListener = usePPListener;
    }

    public String getUsePPListener() {
        return usePPListener;
    }
}

We set the default value of the ‘usePPListener’ to “1”. We set the ‘usePPListener’ to the value property of the af:inputText field of the page. And set the autoSubmit property of the af:inputText to true. The page markup for the content:

<af:gridCell width="100%" halign="stretch" valign="stretch" id="gc2">
    <!-- Content -->
    <af:panelGroupLayout id="pgl2" layout="horizontal">
        <af:inputText label="Use Listener" id="it1" value="#{IndexBean.usePPListener}"
                      autoSubmit="true"/>
        <af:button text="Update" id="b1"/>
    </af:panelGroupLayout>
</af:gridCell>

Running the application at this stage shows

Creating the PagePhaseListener Class

Now we can go on and create the custom PagePhaseListener classes which we then use to switch using an EL. To create such a custom class, the documentation tells us that all we have to do is to create a class which implements the ‘PagePhaseListener’ interface.

This will create the first PagePhaseListener named ‘MyPagePhaseListenerA’. The resulting class looks like

package de.hahn.blog.elpagedef.view.beans;

import oracle.adf.controller.v2.lifecycle.PagePhaseEvent;
import oracle.adf.controller.v2.lifecycle.PagePhaseListener;

public class MyPagePhaseListenerA implements PagePhaseListener {
    public MyPagePhaseListenerA() {
        super();
        System.out.println("MyPagePhaseListenerA created");
    }

    @Override
    public void afterPhase(PagePhaseEvent pagePhaseEvent) {
        // TODO Implement this method
        System.out.println("MyPagePhaseListenerA afterPhase called");
    }

    @Override
    public void beforePhase(PagePhaseEvent pagePhaseEvent) {
        // TODO Implement this method
        System.out.println("MyPagePhaseListenerA beforePhase called");
    }
}

We do it again but name the class ‘MyPagePhaseListenerB’. As you see I added some System.out.println(“…”) statements so that we can see which PagePhaseListener is used later.

Using EL in pageDef.xml

Finally, we start with the interesting part, the EL to use in the pageDef ControllerClass property to switch the listener. The use case demands that we switch the PagePhaseListener depending on a condition. The condition we use is pretty simple:

  • Use MyPagePhaseListenerA if the inputText value stored in usePPListener is equal to “1”
  • Use MyPagePhaseListenerB if the inputText value stored in usePPListener is equal to “2”

We have two options to make the decision,

  1. directly in EL in the pageDef
  2. use a method in a bean where we check the condition

The essential part to know is the type of result both options need to return to work. The ControllerClass expects an object of type PagePhaseListener, or a class which implements this interface.

Solution 1

The classes we created before are implementing this interface, so they should work. To implement option 1 we need to need to instantiate an object of one of the classes and return it on the EL like in the pageDef.xml:

ControllerClass=”#{IndexBean.usePPListener eq ‘1’ ? MyPagePhaseListenerA : MyPagePhaseListenerB}”

And we need to register the bean named ‘MyPagePhaseListenerA’ and ‘MyPagePhaseListenerA’ in the task flow. This will instantiate the object when the task flows starts.

Running this application this way we get

Entering ‘2’ into the af:inputText and clicking ‘Update’ we get

Heureka! The PagePhaseListener have switched as expected.

Solution 2

We implement a method in a bean which returns the right PagePhaseListener class. The EL in the pageDef.xml looks like

ControllerClass="#{IndexBean.pagePhaseListener2Use}"

The method getPagePhaseListener2Use() we implement in the IndexBean as

public PagePhaseListener getPagePhaseListener2Use() {
    if (getUsePPListener() != null && getUsePPListener().equals("2")) {
        return new MyPagePhaseListenerB();
    } else {
        return new MyPagePhaseListenerA();
    }
}

The method returns the PagePhaseListener interface instead of the real class object. This is necessary as a method can only return one object type.

Running the application in this configuration results in exactly the same output we saw before.

Using EL in PageDef for Fragments used in Regions

If you plan to use this technique for fragments which run in regions you would need to return a RegionController instead of a PagePHaseListener. However, as it turned out, you can’t use EL at all in a pageDef of a fragment!

The problem is, that the framework simply doesn’t evaluate the EL you specify for the ControllerClass of a fragment, but uses the EL as the class name. This results in a ‘ClassNotFoundException’ as there is no class named ‘#{your_EL}’.

I’m not sure if this is a bug or a feature. I could not think of a valid use case to use EL for a fragment pageDef.

If you do have one, share it in the comments to this post, please.

If you really think you need to switch the behavior for a RegionController at runtime, you can create one Class which implements the different in the overwritten methods and decide which part to execute inside the method.

Sample

You can download the sample application, which was built using JDev 12.2.1.3 and no DB connection, from GitHub BlogELPageDef

ADF TreeTable Advanced Sample

Building a tree table isn’t straightforward if you want an appealing design. By design, I don’t mean something fancy with different fonts or colors, but I’m talking about the definition of the tree and its sub-notes.

Look at an af:treetable which is generated automatically for you using the Departments and Employees tables of the HR DB.

<af:treeTable value="#{bindings.DepartmentsView1.treeModel}" var="node"
              selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}"
              rowSelection="single" id="tt1">
    <f:facet name="nodeStamp">
        <af:column id="c1">
            <af:outputText value="#{node}" id="ot2"/>
        </af:column>
    </f:facet>
    <f:facet name="pathStamp">
        <af:outputText value="#{node}" id="ot3"/>
    </f:facet>
</af:treeTable>

This code, when put inside a simple page with an af:panelColletion, will produce this output:

As you see, there is only one column. You would not even know that you look at an af:treeTable. OK, the af:treeTable binding used is very simple:

For the first level and for the second level

In the remainder of this post, I show how to build a better-looking version with columns for the different data shown.

As usual, you can download the sample code from GitHub. See the link at the end of this post.

Small changes

To see the whole information in the one tree table column, we change the size of the column to 200px

<af:treeTable value="#{bindings.DepartmentsView1.treeModel}" var="node"
              selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}"
              rowSelection="single" id="tt1">
    <f:facet name="nodeStamp">
        <af:column id="c1" width=”200”>
            <af:outputText value="#{node}" id="ot2"/>
        </af:column>
    </f:facet>
    <f:facet name="pathStamp">
        <af:outputText value="#{node}" id="ot3"/>
    </f:facet>
</af:treeTable>

to get this output which shows all data, but still all in one column:

nodeStamp and pathStamp

When you look at the generated af:treeTable tag you’ll notice two facets:

nodeStamp and pathStamp

 

The nodeStamp facet is used to render the tree table and lets you drill down the data defined in the binding of the tree table.

To render the tree table a special EL, normally ‘#{node}’, is used to stamp out the data of each level. Which data is stamped is defined in the tree binding. We defined to show the DepartmentName name for the first level and EmployeeId, LastName and FirstName for the second level. This is why we get the output in the image above.

In summary: the nodeStamp renders the first column of the tree table.

The pathStamp is used for navigation inside the af:treeTable like when you select a node and make it the top node of the tree or tree table. For more info on this check the documentation.

Building columns:

An af:treeTable, like a table, can and should have columns. As you see in the image above, the af:treeTable looks like a normal tree when you don’t define columns yourself.

Columns are defined outside the ‘nodeStamp’ facet (and pathStamp facet) of an af:treeTable. The tag doc tells us:

Columns

Like the Table, the TreeTable’s children must be Column components (see Table Columns). Like the Tree, the TreeTable has a “nodeStamp” facet which renders the “Object Name” Column. The “Object Name” Column contains the primary identifier of an element in the hierarchy. For example, in an organization chart of employees, the “Object Name” Column might be the employee name.

 

Sample: to show columns for Lastname and FirstName of an Employee we add af:column tags like

<af:treeTable value="#{bindings.DepartmentsView1.treeModel}" var="node"
              selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}"
              rowSelection="single" id="tt1">
    <f:facet name="nodeStamp">
        <af:column id="c1" width=”200”>
            <af:outputText value="#{node}" id="ot2"/>
        </af:column>
    </f:facet>
    <f:facet name="pathStamp">
        <af:outputText value="#{node}" id="ot3"/>
    </f:facet>
    <af:column id="c2" width="200">
        <af:outputText value="#{node.LastName}" id="ot4"/>
    </af:column>
    <af:column id="c3">
        <af:outputText value="#{node.FirstName}" id="ot3"/>
    </af:column>
</af:treeTable>

outside the nodeStamp facet. This will get us this output

Now we have a problem. The second level nodeStamp prints out all attributes of hte node. This is exactly what the EL ‘#{node}’ does. The first level doesn’t have this problem as we only defined one attribute ‘DepartmentName’ for this node.

We don’t like to see the sting in the 2nd level. There exist two solutions to solve this problem.

  1. We use the EL ‘#{node.DepartmentName}’ instead of ‘#{node}’. As the attribute DepartmentName doesn’t exist in level 2, the EL evaluates to nothing (or empty string). However, keep in mind that this only works if the attribute only exists in level one.

  1. We define an EL for the node depending on their level. This solution will work in all cases. To implement this we use an af:switcher component which we use to find out which level we are currently stamping out. This allows us to use attributes defined for the level and build some output for the nodeStamp. Details for this solution follows below.

How to find out which node level currently is stamped out?

One easy way is to look at the view objected used for the level. If you are using some other source you can add an attribute (static) for each level which can then be used in the EL for the af:switcher.

To find out the name of The view object used we can look at the source of the tree binding in the pagedef

  <bindings>
    <tree IterBinding="DepartmentsView1Iterator" id="DepartmentsView1">
      <nodeDefinition DefName="de.hahn.blog.treetable.model.views.DepartmentsView" Name="DepartmentsView10">
        <AttrNames>
          <Item Value="DepartmentName"/>
        </AttrNames>
        <Accessors>
          <Item Value="EmployeesView"/>
        </Accessors>
      </nodeDefinition>
      <nodeDefinition DefName="de.hahn.blog.treetable.model.views.EmployeesView" Name="DepartmentsView11">
        <AttrNames>
          <Item Value="EmployeeId"/>
          <Item Value="LastName"/>
          <Item Value="FirstName"/>
        </AttrNames>
      </nodeDefinition>
    </tree>
  </bindings>

The name we are looking for is the DefName of the nodeDefinition. But how do we access this information in an EL?

Frank Nimphius blogged about this here https://blogs.oracle.com/jdevotnharvest/how-to-determine-the-adf-tree-node-type-using-el. We use the EL

#{node.hierTypeBinding.viewDefName}

which returns the nodes DefName from the binding.

With this info, we can build the af:treeTable

<af:treeTable value="#{bindings.DepartmentsView1.treeModel}" var="node"
              selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}"
              rowSelection="single" id="tt1">
    <f:facet name="nodeStamp">
        <af:column id="c1" width="200">
            <af:switcher facetName="#{node.hierTypeBinding.viewDefName}"
                         defaultFacet="default" id="sw1">
                <f:facet name="de.hahn.blog.treetable.model.views.DepartmentsView">
                    <af:outputText value="#{node}" id="ot2"/>
                </f:facet>
                <f:facet name="de.hahn.blog.treetable.model.views.EmployeesView">
                    <af:outputText value="ID: #{node.EmployeeId}" id="ot11"/>
                </f:facet>
                <f:facet name="default">
                    <!-- use this facet if the other facets don't match! -->
                    <af:outputText value="#{node}+++++#{node.hierTypeBinding.viewDefName}"
                                   id="otd11"/>
                </f:facet>
            </af:switcher>
        </af:column>
    </f:facet>
    <af:column id="c2" width="200">
        <af:outputText value="#{node.LastName}" id="ot4"/>
    </af:column>
    <af:column id="c3">
        <af:outputText value="#{node.FirstName}" id="ot3"/>
    </af:column>
</af:treeTable>

to get this output

Using the same technique in the columns allows showing even more information.

How about showing the manager of each department next to the department in the column where the employees are shown?

Easy, after changing the DeparmentView to return the manager info together with the department

and adding attributes for MgrEmployeeId, MgrLastName and MgrFirstName we can implement this using the following code

<af:treeTable value="#{bindings.DepartmentsView1.treeModel}" var="node"
              selectionListener="#{bindings.DepartmentsView1.treeModel.makeCurrent}"
              rowSelection="single" id="tt1">
    <f:facet name="nodeStamp">
        <af:column id="c1" width="200">
            <af:switcher facetName="#{node.hierTypeBinding.viewDefName}"
                         defaultFacet="default" id="sw1">
                <f:facet name="de.hahn.blog.treetable.model.views.DepartmentsView">
                    <af:outputText value="#{node.DepartmentName}" id="ot2"/>
                </f:facet>
                <f:facet name="de.hahn.blog.treetable.model.views.EmployeesView">
                    <af:outputText value="ID: #{node.EmployeeId}" id="ot11"/>
                </f:facet>
                <f:facet name="default">
                    <!-- use this facet if the other facets don't match! -->
                    <af:outputText value="#{node}+++++#{node.hierTypeBinding.viewDefName}"
                                   id="otd11"/>
                </f:facet>
            </af:switcher>
        </af:column>
    </f:facet>
    <af:column id="c2" width="200">
        <af:switcher facetName="#{node.hierTypeBinding.viewDefName}" defaultFacet="default"
                     id="sw2">
            <f:facet name="de.hahn.blog.treetable.model.views.DepartmentsView">
                <af:outputText value="#{not empty node.MgrLastName ? 'Manager: ' : ''}"
                               id="ot5" inlineStyle="font-weight:bold;"/>
                <af:outputText value="#{not empty node.MgrLastName ? node.MgrLastName : ''}"
                               id="ot6"/>
                <af:outputText value="#{not empty node.MgrLastName ? ', ' : ''}" id="ot4"/>
                <af:outputText value="#{not empty node.MgrLastName ? node.MgrFirstName : ''}"
                               id="ot7"/>
            </f:facet>
            <f:facet name="de.hahn.blog.treetable.model.views.EmployeesView">
                <af:outputText value="#{node.LastName}" id="ot211"/>
            </f:facet>
            <f:facet name="default">
                <!-- use this facet if the other facets don't match! -->
                <af:outputText value="#{node}+++++#{node.hierTypeBinding.viewDefName}"
                               id="otd211"/>
            </f:facet>
        </af:switcher>
    </af:column>
    <af:column id="c3">
        <af:outputText value="#{node.FirstName}" id="ot3"/>
    </af:column>
</af:treeTable>

Will produce this tree table:

You can add more, like icons in each column or …

You can download the sample application from GitHub: BlogTreeTable. The sample was built using JDev 12.2.1.3 but the same technique should work in other JDev versions too (11g or newer).

Empty Test for String Values using Expression Language

On the OTN ADF & JDeveloper space (aka forum) I often read use cases where an action depends on the state of an af:inputText, or better the value entered in the component.
This is problematic as a String value can either be null or it can be empty. The problem is that is you want to test a String value using Expression Language (EL) in the UI, you can’t use e.g.

#{bindings.myText1.inputValue eq ''}

The solution is an existing, but mostly unknown operator of Expression Language (EL) called ’empty’. This operator checks a String value if it’s empty. Usage of the operator is a bit different from the other operators which are used after the value. The ’empty’ operator is used in front of the valeu like

#{empty bindings.myText1.inputValue}

Sample:
consider the following use case: a button should be enabled only if a inputText component is not empty. For this we can use this code

            <af:panelGroupLayout layout="scroll" xmlns:af="http://xmlns.oracle.com/adf/faces/rich" id="pgl1">
              <af:inputText label="Label 1" id="it1" value="#{bindings.myText1.inputValue}" autoSubmit="true"/>
              <af:commandButton text="commandButton 1" id="cb1" disabled="#{empty bindings.myText1.inputValue}" partialTriggers="it1"/>
              <af:outputText value="Working (empty bindings.myText1.inputValue): #{empty bindings.myText1.inputValue} --- not working(bindings.myText1.inputValue eq '': #{bindings.myText1.inputValue eq ''}" id="ot1"
                             partialTriggers="it1"/>
            </af:panelGroupLayout> 

As you see, the af:inputText component stored it’s value in a pageDef variable (or a VO row) and submits the entered value using the autoSubmit property set to ‘true’. The af:commandButton gets enabled only if the EL
#{empty bindings.myText1.inputValue}
returns false. This is the case when you enter anything into the af:inputText. The final piece to make it work is the partial trigger on the af:button component listening to the change of the input value.
The outputText below the button is just to show that the EL
#{bindings.myText1.inputValue eq ''}
does not work correctly.