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
- Do not reference or cast to classes in
oracle.adfinternal
package. This includes classes which have names that start with FacesCtrl. The package is named internal for a reason. This means that ADF has the liberty to modify the behavior of the classes and methods or remove them altogether if they feel that it is needed. - Good articles on row selection and SelectionListener for Tables and TreeTables.
- http://www.oracle.com/technetwork/developer-tools/adf/learnmore/25-generic-tree-selection-listener-169164.pdf
- http://jobinesh.blogspot.in/2010/02/common-mistake-while-iterating-through.html
- https://blogs.oracle.com/jdevotnharvest/entry/how_to_read_data_from
- http://www.oracle.com/technetwork/developer-tools/adf/learnmore/50-synchromize-form-treeselection-169192.pdf
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
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.