ADF Super Code Snippets

Oracle’s ADF (Application Developer Framework) is J2EE technology stack to rapidly develop enterprise applications. As with all enterprise applications, ADF focuses on data handling and makes it quite easy to present the data to the users in variety of ways. As you will already know that ADF is a blanket term which encompasses many layers of the full tech stack. So ADF is divided into: –
* ADF Faces – Based on Java Server Faces 1.2 specification, this provides the UI of the application. It is built over Apache MyFaces Trinidad 1.2.x. So this is the View layer of ADF stack.
* ADFc – ADF Controller controls the life cycle of a typical ADF application. In fact it is part of ADF Faces. ADF Task Flows too form part of this layer.
* ADFm – ADF Model provides the brick and mortar to bind ADF Faces to the Model layer. In fact ADFm includes the Binding layer. This allows for loose coupling between the View and Model layers.
* ADF BC – ADF Business Components form the Model layer. It forms the data abstraction layer which does quite a lot of heavy lifting for the ADF stack.

The aim of this post is to list out some tips and code snippets which you may undoubtedly need while coding in ADF.

Some Important notes first

1 Clearing the selection on a ADF tree/tree-table/table from bean

First make sure the tree or tree-table or table does not have selectedRowKeys attribute set. Now you will notice that no row will be selected when the tree loads for the first time, but when user makes a selection then tree builds and stores the RowKeySet of the node the user selected. So, now whenever the tree loads and if the last selected row is visible then it will be shown selected. To work around this, we need to manually clear the tree’s selected rowkeyset after the user has made his selection (e.g. When we hide the tree, just before the tree is shown). Use the following code for that:-

RowKeySet rks = getTree().getSelectedRowKeys();
if (rks != null) {
    rks.clear();
}

2 Get the VO bound to an iterator

Sometimes we may need to manually execute an iterator, for this we need to get the handle to the VO instance bound to an iterator.

private ViewObject getVOFromIterator(DCIteratorBinding it) {
    ViewObject vo = null;
    if (it.hasRSI()) {
        return it.getRowSetIterator().getRowSet().getViewObject();
    } else {
        return it.getViewObject();
    }
}

3 Programmatically add partial target

AdfFacesContext.getCurrentInstance().addPartialTarget(targetComponent);

Make sure that targetComponent is not null else we may get NullPointerException.

4 Get query entered by user in Adf quickQuery component

QueryDescriptor qd = queryEvent.getDescriptor();
AttributeCriterion ac = qd.getCurrentCriterion();
String attrLabel = ac.getAttribute().getLabel();
Object attrValue = ac.getValues().get(0);

Note if you try ac.getValues() then it will mention that it has zero elements, even when get(0) returns a value.

5 Building Disclosed RowKeySet for nodes in a tree/tree-table

/**
* Ref: http://www.oracle.com/technetwork/developer-tools/adf/learnmore/61search-in-rendered-trees-177577.pdf
* @param treeBinding
* @param keys
* @return
*/
private RowKeySet buildDiscloseRowKeySet(JUCtrlHierBinding treeBinding, RowKeySet keys) {
    RowKeySetImpl discloseRowKeySet = new RowKeySetImpl();
    Iterator iter = keys.iterator();
    while (iter.hasNext()) {
        List keyPath = (List)iter.next();
        JUCtrlHierNodeBinding node = treeBinding.findNodeByKeyPath(keyPath);
        if (node != null && node.getParent() != null && !node.getParent().getKeyPath().isEmpty()) {
            //store the parent path
            discloseRowKeySet.add(node.getParent().getKeyPath());
            //call method recursively until no parents are found
            RowKeySetImpl parentKeySet = new RowKeySetImpl();
            parentKeySet.add(node.getParent().getKeyPath());
            RowKeySet rks = buildDiscloseRowKeySet(treeBinding, parentKeySet);
            discloseRowKeySet.addAll(rks);
        }
    }
    return discloseRowKeySet;
}

6 Select a node in tree/tree-table

Variant 1

private void selectNode(List<List<?>> pathKeys, org.apache.myfaces.trinidad.component.UIXTree targetComponent) {
    List<Object> lstKeys = new ArrayList<Object>();
    for(int level = 0; level < pathKeys.size(); level++) {
        List<?> keys = pathKeys.get(level);
        Object[] oKeys = new Object[keys.size()];
        for (int index = 0; index < oKeys.length; index++) {
            oKeys[index] = keys.get(index);
        }
        lstKeys.add(new Key(oKeys));
    }
    if(lstKeys.size() == 1) {
        lstKeys = Collections.singletonList(lstKeys.get(0));
    } else {
        lstKeys = Collections.unmodifiableList(lstKeys);
    }
    RowKeySet keyset = new RowKeySetImpl();
    keyset.setCollectionModel((CollectionModel)(targetComponent.getValue()));
    keyset.add(lstKeys);
    targetComponent.setSelectedRowKeys(keyset); //After this targetComponent.setDisclosedRowKeys()
                                                //needs to be called to reveal the node is its parents
                                                //are collapsed.
}

Where pathKeys is of the following form:- Take an example, where we want to select node C, in the following tree.

A [Row from VO1, keys A1]
|--B [Row from VO2, keys B1, B2, B3]
    |--C [Row from VO3, keys C1, C2]

Then pathKeys will be a list like shown below:-

{ {A1}, {B1, B2, B3}, {C1, C2} }

Variant 2 (Maybe this is a better approach, but not tested)

private void selectNode(List<List<?>> pathKeys, org.apache.myfaces.trinidad.component.UIXTree targetComponent) {
    List<Object> lstKeys = new ArrayList<Object>();
    for(int level = 0; level < pathKeys.size(); level++) {
        List<?> keys = pathKeys.get(level);
        Object[] oKeys = new Object[keys.size()];
        for (int index = 0; index < oKeys.length; index++) {
            oKeys[index] = keys.get(index);
        }
        lstKeys.add(new Key(oKeys));
    }
    if(lstKeys.size() == 1) {
        lstKeys = Collections.singletonList(lstKeys.get(0));
    } else {
        lstKeys = Collections.unmodifiableList(lstKeys);
    }
    JUCtrlHierBinding treeBinding = (JUCtrlHierBinding) ((CollectionModel)targetComponent.getValue()).getWrappedData();
    JUCtrlHierNodeBinding nodeBinding = treeBinding.findNodeByKeyPath(lstKeys);
    Key rowKey = nodeBinding.getRowKey();
    JUIteratorBinding iterator = nodeBinding.getIteratorBinding();
    iterator.setCurrentRowWithKey(rowKey.toStringFormat(true));
}

7 Create a flexible query (search box) which allows you to use custom ViewCriteria with ADF quickQuery

Custom Quick Query ADF Component

Custom Quick Query ADF Component

To create search box like the one above.

UI code

<af:quickQuery id="qq1" queryListener="#{pageFlowScope.Bean.invokeSearch}"
    searchDesc="Quick Search" shortDesc="Quick Search" label="Quick Search">
    <af:inputText id="itQq1" value="#{pageFlowScope.Bean.searchKeyword}"
        simple="true" partialTriggers="cb3" shortDesc="Quick Search"/>
    <f:facet name="end">
        <af:commandButton text="Clear" id="cb3" actionListener="#{pageFlowScope.Bean.clearSearch}"/>
    </f:facet>
</af:quickQuery>
  • We have defined our own input box so that Go button and the auto-generated input boxes get disabled if quickQuery doesn’t have any model.
  • Our custom input box allows us to directly read and modify the input box’s content. This is needed by Clear functionality.
  • Input box has simple set to true, so that its label is not displayed.
  • Official quickQuery reference – http://jdevadf.oracle.com/adf-richclient-demo/docs/tagdoc/af_quickQuery.html.

Bean code (in Bean.java)

private String searchKeyword;

public String getSearchKeyword() {
    return searchKeyword;
}

public void setSearchKeyword(String keyword) {
    searchKeyword = keyword;
}

public void invokeHierSearch(QueryEvent queryEvent) {
    if (searchKeyword != null && !searchKeyword.isEmpty()) {
        DCIteratorBinding it = ADFUtil.findIterator("VOIterator");
        ViewObject vo = getVOFromIterator(it);
        vo.applyViewCriteria(vo.getViewCriteriaManager().getViewCriteria("SearchCriteria"));
        vo.setNamedWhereClauseParam("keyword", searchKeyword);
        vo.executeQuery(); //Iterator will be executed after UI is updated.
        searchVisible = true;
    } else {
        searchVisible = false;
    }
    AdfFacesContext.getCurrentInstance().addPartialTarget(...); //Update UI
}

public void clearSearch(ActionEvent e) {
    searchKeyword = null;
    invokeHierSearch(null);
}

private ViewObject getVOFromIterator(DCIteratorBinding it) {
    ViewObject vo = null;
    if (it.hasRSI()) {
        return it.getRowSetIterator().getRowSet().getViewObject();
    } else {
        return it.getViewObject();
    }
} 

8 Get reference to Bindings from bean

DCBindingContainer bindings = ((DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry());

9 Programmatically execute iterator

DCBindingContainer bindings = ((DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry());
bindings.findIteratorBinding("Iterator").executeQuery();

10 Get reference to method binding (operation binding) in bean

Get bindings from 8.

OperationBinding meth = bindings.getOperationBinding("methodBindingId");
Map args = meth.getParamsMap();
args.put(“arg1”, arg1Value);
…
meth.execute();

11 Get reference to pageFlowScope in bean

AdfFacesContext.getCurrentInstance().getPageFlowScope()

This returns a Map which we can use to read or set values.

12 Evaluate random EL value expressions in bean

FacesContext fc = FacesContext.getCurrentInstance();
ELContext elContext = fc.getELContext();
ExpressionFactory ef = fc.getApplication().getExpressionFactory();
ValueExpression exp = ef.createValueExpression(elContext, "#{expression}", ClassOfOutcomeOfExpression.class);
ClassOfOutcomeOfExpression obj = (ClassOfOutcomeOfExpression) exp.getValue(elContext);

ClassOfOutcomeOfExpression can be Object if you don’t care about the outcome. It has too much boilerplate code.

So place the following code in bean.

private <T> T evaluateEL(String el, Class<T> type) {
    FacesContext fc = FacesContext.getCurrentInstance();
    ELContext elContext = fc.getELContext();
    ExpressionFactory ef = fc.getApplication().getExpressionFactory();
    ValueExpression exp = ef.createValueExpression(elContext, el, type);
    return (T) exp.getValue(elContext);
}

13 Evaluate random EL method expressions in bean

FacesContext fc = FacesContext.getCurrentInstance();
ELContext elContext = fc.getELContext();
ExpressionFactory ef = fc.getApplication().getExpressionFactory();
MethodExpression meth = ef.createMethodExpression(elContext, "#{expression}", Outcome.class, new Class[]{Arg1.class, Arg2.class, …});
Outcome obj = (Outcome) meth.invoke(elContext, new Object[]{arg1Obj, arg2Obj, …});

Example: You need to code the below if you override treetable’s selectionListener.

MethodExpression meth = ef.createMethodExpression(elContext, "#{bindings.treeBinding.treeModel.makeCurrent}",
    Object.class, new Class[]{SelectionEvent.class});
meth.invoke(elContext, new Object[]{selectionEvent});

14 Get value from attribute binding in bean

Get bindings from 8.

((oracle.binding.AttributeBinding)bindings.getControlBinding("bindingName")).getInputValue();

15 Get value from list binding in bean

Get bindings from 8.

((JUCtrlListBinding)bindings.getControlBinding("bindingName")) .attributeValue();

16 Implementing custom selection listener

In this use-case we are providing our own selection listener for the TreeTable because we are interested in capturing the selected node’s VO row instance.

public void onTreeSelect(SelectionEvent selectionEvent) {
    //Invoking makeCurrent manually since it will no longer be called as we have provided our own.
    FacesContext fc = FacesContext.getCurrentInstance();
    ELContext elContext = fc.getELContext();
    ExpressionFactory ef = fc.getApplication().getExpressionFactory();
    MethodExpression meth = ef.createMethodExpression(elContext, "#{bindings.treeBinding.treeModel.makeCurrent}",
        Object.class, new Class[]{SelectionEvent.class});
    meth.invoke(elContext, new Object[]{selectionEvent}); //Selection done now getting data from model.
    RichTreeTable myTree = (RichTreeTable) selectionEvent.getSource(); //changed for Serialization
    RowKeySet rowKeys = selectionEvent.getAddedSet();
    if (rowKeys != null) {
        CollectionModel treeModel = (CollectionModel) myTree.getValue();
        JUCtrlHierBinding treeBinding = (JUCtrlHierBinding) treeModel.getWrappedData();
        Iterator selection = rowKeys.iterator();
        while (selection.hasNext()) {
            //Will loop only once since we have allowed single selection.
            List key = (List) selection.next();
            JUCtrlHierNodeBinding nodeBinding = treeBinding.findNodeByKeyPath(key);
            Row row = nodeBinding.getRow(); //Your custom code which will use row.
                                            //row will be the instance of ViewRowImpl
                                            //which backs the currently selected node.
        }
    }
}

17 Get all rows (nodes) of a Tree or TreeTable

In my use-case I wanted to iterate over all the rows in the TreeTable to find if it has any rows with pending changes. This was to prevent the user from carrying out some actions if there were unsaved changes. There are many ways to achieve this, like check if transaction or view port is dirty, but I did not care about the state of various other components on the page which are outside the tree.

public List<Row> getTreeNodes(CollectionModel treeModel) {
    List<Row> rows = new ArrayList<Row>();
    JUCtrlHierBinding treeBinding = (JUCtrlHierBinding) treeModel.getWrappedData();
    Queue<JUCtrlHierNodeBinding> nodeBindings = new LinkedList<JUCtrlHierNodeBinding>();
    nodeBindings.add(treeBinding.getRootNodeBinding());
    while (!nodeBindings.isEmpty()) {
        List<JUCtrlHierNodeBinding> childNodes = nodeBindings.remove().getChildren();
        if (childNodes != null) {
            for (JUCtrlHierNodeBinding _node : childNodes) {
                nodeBindings.add(_node); rows.add(_node.getRow());
            }
        }
    }
    return rows;
}

18 Programmatically loading resource bundle

Recently I had one requirement where I needed to programmatically load a resource bundle from a managed bean and read the translated strings. I could not find any official documentation on this, and the net is rife with many alternatives which did not work for me. Anyway here is my version which is simple and works well for me.

//import java.util.ResourceBundle;
//import oracle.javatools.resourcebundle.BundleFactory;
ResourceBundle myBundle = BundleFactory.getBundle("fully.qualified.class_name.of.the.bundle");
String msg = getMsg(myBundle, "myKey");

Where the method getMsg() is

public String getMsg(ResourceBundle bundle, String key) {
    if (bundle == null) {
        return "";
    }
    try {
        return bundle.getString(key);
    } catch (MissingResourceException ex) { //Log error here. }
    return "";
}

Please make sure that the bundle class is available on the classpath.

Disclaimer: The views expressed on this blog are my own and do not necessarily reflect the views of my employer. This is may or may not be the recommended way. This is provided here merely in the spirit of sharing. You are more than welcome to post a correction or make an addition in the comment section below. I will happily update this post accordingly.

One Comment

  1. IMPLEMENTING CUSTOM SELECTION LISTENER…………this topic useful for me to implement When-new-record-Instance forms’s equivalent trigger in ADF

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.