A while back, almost a year and a half to be exact, I wrote an article on using the ADF Faces Tree Component: Getting started with ADF Faces in JDeveloper 10.1.3 – Using the Tree Component, comparison with MyFaces Tomahawk Tree2 . At the time, I felt that was a fairly advanced article. However, today I have gained much more experience, and the article feels somewhat awkward and incomplete. So this is a new version of that article. In it, I will describe how to develop a TreeModel to provide data to the ADF Faces Tree, how to use the tree in an JSF page, tying it to the TreeModel and how to deal with Node Selection events.
It provides a build up a new article I will publish in a few days time in which I describe how to base an ADF Faces Tree on a SQL Query – without using the far from ideal ADF Faces Tree Data Binding.
This tree is created using a PoJo based Tree Model (created on the fly as managed bean) and demonstrates quite clearly I believe how to get going with the ADF Faces tree. By the way, some of the insights in this article are inspired by Chintan and Ric Smith.
To get the ADF Faces tree going
, we need an implementation of the TreeModel interface to inject into the Tree component. The TreeModel is where the tree finds the nodes to display. Fortunately, we have a pretty useful default implementation of the TreeModel, with the ChildPropertyTreeModel class. In this case, based on Ric’s suggestion, I have extended this class in my own SpecialTreeModel that overrides the isContainer() method. That is the method that helps the tree determine whether a node should be displayed as a leaf – no children – or a container -expandable, with children (potentially at least). The SpecialTreeModel is implemented as:
package nl.amis.adffaces.tree;
import oracle.adf.view.faces.model.ChildPropertyTreeModel;
public class SpecialTreeModel extends ChildPropertyTreeModel {
public SpecialTreeModel() {
}
public SpecialTreeModel(java.lang.Object p1, java.lang.String p2) {
super(p1, p2);
}
// indicates whether the getRowData() node is a container element i.e. could ever contain children
public boolean isContainer() {
TreeNode node = (TreeNode)getRowData();
return node.getChildCount()>0;
}
}
Of course we somehow need to create an instance of this class and inject it into the ADF Faces Tree. For this, I use the LibraryTreeModelManager class that gets instantiated as a managed bean by JSF, from the following bean definition in the faces-config.xml:
<managed-bean>
<managed-bean-name>LibraryTreeModelManager</managed-bean-name>
<managed-bean-class>nl.amis.als.model.LibraryTreeModelManager</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
The class itself sets up a tree with data on a few authors and some of their books:
package nl.amis.als.model;
import java.util.ArrayList;
import java.util.List;
import nl.amis.adffaces.tree.SpecialTreeModel;
import nl.amis.adffaces.tree.TreeNode;
import oracle.adf.view.faces.model.TreeModel;
public class LibraryTreeModelManager {
private TreeModel libraryTreeModel;
public LibraryTreeModelManager () {
TreeNode library = new TreeNode("Library", "folder");
TreeNode ben = new TreeNode("Ben Elton", "author");
TreeNode book = new TreeNode("Post Mortem", "book");
ben.getChildren().add(book);
book = new TreeNode("Blast from the Past", "book");
ben.getChildren().add(book);
book = new TreeNode("Stark", "book");
ben.getChildren().add(book);
TreeNode rod = new TreeNode("Rod Johnson", "author");
book = new TreeNode("J2EE Design and Development", "book");
rod.getChildren().add(book);
book =
new TreeNode("J2EE without Enterprise Java Bean", "book");
rod.getChildren().add(book);
book =
new TreeNode("Professional Java Development With The Spring Framework",
"book");
rod.getChildren().add(book);
TreeNode jared =
new TreeNode("Jared Diamond", "author");
TreeNode orson =
new TreeNode("Orson Scott Card", "author");
book = new TreeNode("Ender’s Game", "book");
orson.getChildren().add(book);
book = new TreeNode("Xenocide", "book");
orson.getChildren().add(book);
book = new TreeNode("Shadow Puppets", "book");
orson.getChildren().add(book);
library.getChildren().add(ben);
library.getChildren().add(rod);
library.getChildren().add(jared);
library.getChildren().add(orson);
// create the list of root nodes:
List nodes = new ArrayList();
nodes.add(library);
libraryTreeModel = new SpecialTreeModel(library, "children");
}
public TreeModel getLibraryTreeModel() {
return libraryTreeModel;
}
}
See how the last line of the constructor method creates the instance of SpecialTreeModel that is then saved in the private member libraryTreeModel.
The final class I have used in this very simple project is called TreeHandler. It is a class that takes care of several background tasks connected with the tree, such as keeping track of the currently selected node, binding the tree (to have it available to our server side code) and making the TreeModel available to the Tree Component. An instance of this class is instantiated as Managed Bean:
<managed-bean>
<managed-bean-name>LibraryTreeHandler</managed-bean-name>
<managed-bean-class>nl.amis.adffaces.tree.TreeHandler</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>treemodel</property-name>
<value>#{LibraryTreeModelManager.libraryTreeModel}</value>
</managed-property>
</managed-bean>
The libraryTreeModel that was set up in the LibraryTreeModelManager bean is injected into this managed bean, through the treemodel managed property. The generic TreeHandler class is implemented as:
package nl.amis.adffaces.tree;
import java.util.ArrayList;
import java.util.List;
import oracle.adf.view.faces.component.core.data.CoreTree;
import oracle.adf.view.faces.model.TreeModel;
public class TreeHandler {
private List focusRowKey = new ArrayList();
private Object selectedNode;
private TreeModel treemodel;
private CoreTree jsfTree;
public TreeHandler() {
}
public void setSelectedNode(Object selectedNode) {
this.selectedNode = selectedNode;
}
public Object getSelectedNod e() {
return selectedNode;
}
public void setTreemodel(TreeModel treemodel) {
this.treemodel = treemodel;
}
public TreeModel getTreemodel() {
return treemodel;
}
public void setJsfTree(CoreTree jsfTree) {
this.jsfTree = jsfTree;
}
public CoreTree getJsfTree() {
return jsfTree;
}
public void setFocusRowKey(List focusRowKey) {
this.focusRowKey = focusRowKey;
}
public List getFocusRowKey() {
return focusRowKey;
}
}
Now we have done all the setting up we need, and we can continue to create a new JSF page with the ADF Faces Tree Component in it and bind that to the LibraryTreeHandler managed bean.
The JSF source for this page looks like this:
<?xml version='1.0' encoding='windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:afh="http://xmlns.oracle.com/adf/faces/html"
xmlns:af="http://xmlns.oracle.com/adf/faces">
<jsp:output omit-xml-declaration="true" doctype-root-element="HTML"
doctype-system="http://www.w3.org/TR/html4/loose.dtd"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
<jsp:directive.page contentType="text/html;charset=windows-1252"/>
<f:view>
<afh:html>
<afh:head title="Most Recent Salary Change Events">
<meta http-equiv="Content-Type"
content="text/html; charset=windows-1252"/>
</afh:head>
<afh:body>
<h:form>
<af:tree binding="#{LibraryTreeHandler.jsfTree}"
focusRowKey="#{LibraryTreeHandler.focusRowKey}"
varStatus="nodeStatus"
value="#{LibraryTreeHandler.treemodel}" var="node">
<f:facet name="nodeStamp">
<af:commandLink text="#{node.description}">
<af:setActionListener from="#{LibraryTreeHandler.jsfTree.rowKey}"
to="#{LibraryTreeHandler.focusRowKey}"/>
<af:setActionListener from="#{node}"
to="#{LibraryTreeHandler.selectedNode}"/>
<af:resetActionListener/>
</af:commandLink>
</f:facet>
</af:tree>
</h:form>
</afh:body>
</afh:html>
</f:view>
</jsp:root>
Note how the tree has been bound to the LibraryTreeHandler using the binding attribute and how the LibraryTreeHandler’s treemodel is set in the value attribute of the tree.
We have but a single type of node in this particular tree – if not we could have used the ADF Faces Switcher component inside the nodeStamp facet. When a node is clicked on, two setActionListener elements ‘fire’ and copy the rowkey of the the selected node to the LibraryTreeHandler bean as well as the node itself to the selectedNode property.
The focusRowKey property on the tree component is used to tell the tree which node is currently selected and should therefore be highlighted, specified in a list with Integer components that together indicate the node as xth node on root level, yth node on first child level, zth node on second child level etc.
You have seen the screenshot already. Well, another one of a simple tree. Nice things about it:
- all data is presented in a hierarchical fashion
- PPR (AJAX) is used to show additional nodes when their parent is expanded
- nodes that do not have children show up as a leaf
- nodes can be selected, are then highlighted and the backing bean gets a reference to the selected node
Resources
Download JDeveloper 10.1.3.2 Project: https://technology.amis.nl/wp-content/uploads/images/pojobasedadffacestree.zip
I got the following error in my code
Â
ADF_FACES-60098:Faces lifecycle receives unhandled exceptions in phase RENDER_RESPONSE 6
javax.faces.model.NoRowAvailableException
at javax.faces.model.ScalarDataModel.getRowData(ScalarDataModel.java:146)
at org.apache.myfaces.trinidad.model.SortableModel.getRowData(SortableModel.java:127)
at org.apache.myfaces.trinidad.model.ChildPropertyTreeModel.getRowData(ChildPropertyTreeModel.java:216)
at org.cap.performance.metricTree.SpecialTreeModel.isContainer(SpecialTreeModel.java:17)
at oracle.adfinternal.view.faces.model.FlattenedTreeCollectionModel.modifyExpanded(FlattenedTreeCollectionModel.java:499)
at oracle.adfinternal.view.faces.renderkit.rich.TreeRendererUtils.initModel(TreeRendererUtils.java:483)
at oracle.adfinternal.view.faces.renderkit.rich.TreeRendererUtils.wrapModel(TreeRendererUtils.java:732)
at oracle.adfinternal.view.faces.renderkit.rich.TreeRendererUtils.getCollectionObject(TreeRendererUtils.java:524)
at oracle.adfinternal.view.faces.renderkit.rich.TreeRenderer.getCollectionObject(TreeRenderer.java:2098)
at oracle.adfinternal.view.faces.renderkit.rich.TreeRenderer._storeHeightAutosized(TreeRenderer.java:2701)
at oracle.adfinternal.view.faces.renderkit.rich.TreeRenderer.encodeAll(TreeRenderer.java:309)
at oracle.adf.view.rich.render.RichRenderer.encodeAll(RichRenderer.java:1452)
at org.apache.myfaces.trinidad.render.CoreRenderer.encodeEnd(CoreRenderer.java:493)
at org.apache.myfaces.trinidad.component.UIXComponentBase.encodeEnd(UIXComponentBase.java:913)
at org.apache.myfaces.trinidad.component.UIXCollection.encodeEnd(UIXCollection.java:617)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1659)
at org.apache.myfaces.trinidad.render.CoreRenderer.encodeChild(CoreRenderer.java:606)
at oracle.adf.view.rich.render.RichRenderer.encodeChild(RichRenderer.java:3201)
at oracle.adfinternal.view.faces.renderkit.rich.PanelGroupLayoutRenderer._encodeChild(PanelGroupLayoutRenderer.java:447)
at oracle.adfinternal.view.faces.renderkit.rich.PanelGroupLayoutRenderer.access$1500(PanelGroupLayoutRenderer.java:30)
at oracle.adfinternal.view.faces.renderkit.rich.PanelGroupLayoutRenderer$EncoderCallback.processComponent(PanelGroupLayoutRenderer.java:734)
at oracle.adfinternal.view.faces.renderkit.rich.PanelGroupLayoutRenderer$EncoderCallback.processComponent(PanelGroupLayoutRenderer.java:637)
at org.apache.myfaces.trinidad.component.UIXComponent.processFlattenedChildren(UIXComponent.java:187)
at org.apache.myfaces.trinidad.component.UIXComponent.processFlattenedChildren(UIXComponent.java:318)
at org.apache.myfaces.trinidad.component.UIXComponent.encodeFlattenedChildren(UIXComponent.java:283)
at oracle.adfinternal.view.faces.renderkit.rich.PanelGroupLayoutRenderer.encodeAll(PanelGroupLayoutRenderer.java:360)
at oracle.adf.view.rich.render.RichRenderer.encodeAll(RichRenderer.java:1452)
at org.apache.myfaces.trinidad.render.CoreRenderer.encodeEnd(CoreRenderer.java:493)
at org.apache.myfaces.trinidad.component.UIXComponentBase.encodeEnd(UIXComponentBase.java:913)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1659)
at org.apache.myfaces.trinidad.render.CoreRenderer.encodeChild(CoreRenderer.java:606)
at oracle.adf.view.rich.render.RichRenderer.encodeChild(RichRenderer.java:3201)
at org.apache.myfaces.trinidad.render.CoreRenderer.encodeAllChildren(CoreRenderer.java:623)
at oracle.adf.view.rich.render.RichRenderer.encodeAllChildrenInContext(RichRenderer.java:3062)
at oracle.adfinternal.view.faces.renderkit.rich.FormRenderer.encodeAll(FormRenderer.java:274)
at oracle.adf.view.rich.render.RichRenderer.encodeAll(RichRenderer.java:1452)
at org.apache.myfaces.trinidad.render.CoreRenderer.encodeEnd(CoreRenderer.java:493)
at org.apache.myfaces.trinidad.component.UIXComponentBase.encodeEnd(UIXComponentBase.java:913)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1659)
at org.apache.myfaces.trinidad.render.CoreRenderer.encodeChild(CoreRenderer.java:606)
at oracle.adf.view.rich.render.RichRenderer.encodeChild(RichRenderer.java:3201)
at org.apache.myfaces.trinidad.render.CoreRenderer.encodeAllChildren(CoreRenderer.java:623)
at oracle.adf.view.rich.render.RichRenderer.encodeAllChildrenInContext(RichRenderer.java:3062)
at oracle.adfinternal.view.faces.renderkit.rich.DocumentRenderer.encodeAll(DocumentRenderer.java:1277)
at oracle.adf.view.rich.render.RichRenderer.encodeAll(RichRenderer.java:1452)
at org.apache.myfaces.trinidad.render.CoreRenderer.encodeEnd(CoreRenderer.java:493)
at org.apache.myfaces.trinidad.component.UIXComponentBase.encodeEnd(UIXComponentBase.java:913)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1659)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1655)
at oracle.adfinternal.view.faces.component.AdfViewRoot.encodeAll(AdfViewRoot.java:91)
at com.sun.faces.application.view.JspViewHandlingStrategy.doRenderView(JspViewHandlingStrategy.java:431)
at com.sun.faces.application.view.JspViewHandlingStrategy.renderView(JspViewHandlingStrategy.java:233)
at org.apache.myfaces.trinidadinternal.application.ViewDeclarationLanguageFactoryImpl$ChangeApplyingVDLWrapper.renderView(ViewDeclarationLanguageFactoryImpl.java:350)
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:131)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:273)
at org.apache.myfaces.trinidadinternal.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:165)
at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl._renderResponse(LifecycleImpl.java:1027)
at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl._executePhase(LifecycleImpl.java:334)
at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:232)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:313)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:300)
at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.adf.model.servlet.ADFBindingFilter.doFilter(ADFBindingFilter.java:173)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.adfinternal.view.faces.webapp.rich.RegistrationFilter.doFilter(RegistrationFilter.java:121)
at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:468)
at oracle.adfinternal.view.faces.activedata.AdsFilter.doFilter(AdsFilter.java:60)
at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:468)
at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl._doFilterImpl(TrinidadFilterImpl.java:293)
at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl.doFilter(TrinidadFilterImpl.java:199)
at org.apache.myfaces.trinidad.webapp.TrinidadFilter.doFilter(TrinidadFilter.java:92)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.security.jps.ee.http.JpsAbsFilter$1.run(JpsAbsFilter.java:111)
at java.security.AccessController.doPrivileged(Native Method)
at oracle.security.jps.util.JpsSubject.doAsPrivileged(JpsSubject.java:313)
at oracle.security.jps.ee.util.JpsPlatformUtil.runJaasMode(JpsPlatformUtil.java:413)
at oracle.security.jps.ee.http.JpsAbsFilter.runJaasMode(JpsAbsFilter.java:94)
at oracle.security.jps.ee.http.JpsAbsFilter.doFilter(JpsAbsFilter.java:161)
at oracle.security.jps.ee.http.JpsFilter.doFilter(JpsFilter.java:71)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at oracle.dms.servlet.DMSServletFilter.doFilter(DMSServletFilter.java:136)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:27)
at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3715)
at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3681)
at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:120)
at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2277)
at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2183)
at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1454)
at weblogic.work.ExecuteThread.execute(ExecuteThread.java:209)
at weblogic.work.ExecuteThread.run(ExecuteThread.java:178)
You should add the adf-faces-impl.jar and the jsf-impl.jar to the \ViewController\public_html\WEB-INF\lib directory. These files are located in the \jlib directory.
I downloaded your adf faces tree project, and ran with JDeveloper 10.1.3.2, but I got the following error:
500 Internal Server Error
java.lang.NullPointerException
at javax.faces.webapp.FacesServlet.init(FacesServlet.java:165)
at com.evermind[Oracle Containers for J2EE 10g (10.1.3.1.1) ].server.http.HttpApplication.loadServlet(HttpApplication.java:2361)
at com.evermind[Oracle Containers for J2EE 10g (10.1.3.1.1) ].server.http.HttpApplication.findServlet(HttpApplication.java:4810)
at com.evermind[Oracle Containers for J2EE 10g (10.1.3.1.1) ].server.http.HttpApplication.findServlet(HttpApplication.java:4734)
at com.evermind[Oracle Containers for J2EE 10g (10.1.3.1.1) ].server.http.HttpApplication.getRequestDispatcher(HttpApplication.java:2957)
at com.evermind[Oracle Containers for J2EE 10g (10.1.3.1.1) ].server.http.HttpRequestHandler.doProcessRequest(HttpRequestHandler.java:735)
at com.evermind[Oracle Containers for J2EE 10g (10.1.3.1.1) ].server.http.HttpRequestHandler.processRequest(HttpRequestHandler.java:447)
at com.evermind[Oracle Containers for J2EE 10g (10.1.3.1.1) ].server.http.HttpRequestHandler.serveOneRequest(HttpRequestHandler.java:215)
at com.evermind[Oracle Containers for J2EE 10g (10.1.3.1.1) ].server.http.HttpRequestHandler.run(HttpRequestHandler.java:117)
at com.evermind[Oracle Containers for J2EE 10g (10.1.3.1.1) ].server.http.HttpRequestHandler.run(HttpRequestHandler.java:110)
at oracle.oc4j.network.ServerSocketReadHandler$SafeRunnable.run(ServerSocketReadHandler.java:260)
at oracle.oc4j.network.ServerSocketAcceptHandler.procClientSocket(ServerSocketAcceptHandler.java:239)
at oracle.oc4j.network.ServerSocketAcceptHandler.access$700(ServerSocketAcceptHandler.java:34)
at oracle.oc4j.network.ServerSocketAcceptHandler$AcceptHandlerHorse.run(ServerSocketAcceptHandler.java:880)
at com.evermind[Oracle Containers for J2EE 10g (10.1.3.1.1) ].util.ReleasableResourcePooledExecutor$MyWorker.run(ReleasableResourcePooledExecutor.java:298)
at java.lang.Thread.run(Thread.java:595)