Generating an Advanced ADF Faces Tree based application with JHeadstart 10.1.3

6

You may not be able to see the forest for all the trees I have been discussing lately. We are running a substantial project with the latest technology: ADF Faces and JHeadstart 10.1.3 (beta) on top of ADF Binding and ADF Business Components and Oracle 10g database – using supporting infrastructure with Maven 2, Subversion, Jira, Confluence Wiki etc. Some of the key user interfaces in the application we are developing hinge on tree components. The key data elements to be worked on in this application are hierarchically structured. The tree provides not only a very natural representation for the data, it also gives our end users a powerful navigation mechanism, allowing them to very rapidly access the element they need to work on.

To get them the best possible support, we need to work around some of the limitations in ADF as well as tweak the JHeadstart 10.1.3 generator a little (using the new extended custom template mechanism). One of the requirements put to us developers is the following: the tree should display node labels for types of nodes at one level, then all occurrences of those types under the labels. When we click on a node, we simply navigate to the Edit Node page like we always do, and when we click on the node label we want to navigate to a table layout page with records for all the nodes of the type we just selected.

In a previous article – Creating Multi-Type Node Children and Child Node labels in ADF Faces Tree Component – I have described how to set up the ADF BC model for introducing node labels into our tree. In this article, I will tell you the rest of the story.

Our objective in this article is to generate (with as few post-gen changes as we can get away with) the following user interface:
 

....
 

The steps in generating and post-generation modifying our application are:

Model 

  • create the ADF BC ViewObjects for the tree with view link between every pair of subsequent levels (as per the instructions in the article mentioned above)
  • create ADF BC ViewObjects for every type of node that will have to be edited
  • prepare the ADF BC Application Module’s data model: include the hierarchy of TreeLevel ViewObjects as well as create top-level usages for all ViewObjects for the editing of node types
  • run the ADF BC Tester to see if the Application Module’s model does what you want it to do

View/Controller

  • prepare the ViewController project for JHeadstart
  • create a bean TreeHelper
  • create a new default Application Definition file
  • create the Group with Detail Groups for the Tree layout
  • create Groups for each of distinct Node Types (and associated ViewObjects) that need to be edited – either Form or Table layout, depending on the Node Type; do not generate menu entries for these groups; deep link enable all groups with Form layout
  • generate the application using the JHeadstart Application Generator

post-generation:

  • add a managed bean to faces-config.xml: TreeHelper
  • implement the action method in the TreeHelper bean using the navigation outcomes available in the faces-config.xml file
  • add an actionListener to each tree node facet and modify its action

Preparing the Model Project 

The first step is described at length in a previous article – Creating Multi-Type Node Children and Child Node labels in ADF Faces Tree Component . Please read here how to create the ViewObjects for the Tree with the Node Labels and the multiple node types per parent node.

For every node type at every level, we need to determine whether it can be selected and if so, what should be the result. Typically one of two things can happen when a node is selected: either the Edit Page for a specific record type should be displayed or a multi record page must be displayed with all records associated with the selected node should be displayed. An example of the first case is the selection of the Node for Department ACCOUNTING upon which the Edit Department page for the ACCOUNTING Department should be displayed. The second case is demonstrated when the Clerks node under the ACCOUNTING node is selected. Now a Multi Record page must be displayed with all the Clerks (all child nodes of the selected Clerks node) in ACCOUNTING.

We need to create ViewObjects for both situations. However, there is distinction: the ViewObject for the first case – edit a single node of a specific type – is a normal ViewObject, no frills. The ViewObjects for the second case is different: it needs to have a bind parameter to restrict the selection of records to the set that belongs to the selected node. For example the Clerks node needs a ViewObject whose name reflects its purpose – something like ClerksInDepartmentView will do nicely – and with a WHERE clause that only returns CLERKS (JOB=’CLERK’) that work in the selected department (DEPTNO = :department). The :department is bind-parameter that should be created for this ViewObject. Later on we need to take care that when the Clerks node is selected, the correct deptno value is set for this :department bind-variable.

Setting up the Data Model in the Application Module should be fairly simple now: just create top-level usages for all ViewObjects for editing nodes and a hierarchy for the ViewObjects representing the various levels of the tree.

Initial Set Up of the ViewController project

The virgin ViewController project needs to be JHeadstart enabled. A default Application Definition file needs to be created. These steps have been described time and again and are probably quite familiar to you, so I will not go into them. See for example:Building an ADF Faces Tree Component with mixed (DEPT and EMP) nodes in 5 minutes using JHeadstart 10.1.3

Create the TreeHelper bean

We will make use of a bean into which we record details for the currently selected node. This bean will be the decoupling point between the tree on the one hand and the edit pages on the other. The bean itself is fairly simple:

package nl.amis.view;<br /><br />import java.util.ArrayList;<br />import java.util.List;<br /><br />import javax.faces.application.Application;<br />import javax.faces.context.FacesContext;<br /><br />import oracle.adf.model.binding.DCControlBinding;<br />import oracle.adf.model.binding.DCIteratorBinding;<br /><br />import oracle.jbo.Key;<br />import oracle.jbo.domain.Number;<br />import oracle.jbo.uicli.binding.JUCtrlActionBinding;<br />import oracle.jbo.uicli.binding.JUCtrlHierNodeBinding;<br />import oracle.jbo.uicli.binding.JUFormBinding;<br /><br />public class TreeHelper {<br />    public TreeHelper() {<br />    }<br />    private String key;<br />    private Number parentKey;<br />    private String parentType;<br />    private String nodeType;<br />    private Object node;<br /><br />    public void setKey(String key) {<br />        this.key = key;<br />    }<br /><br />    public String getKey() {<br />        return key;<br />    }<br /><br />    public void setNode(Object node) {<br />        this.node = node;<br />        setNodeType(((String)((JUCtrlHierNodeBinding)node).getRow().getAttribute(&quot;NodeType&quot;)));        <br />        setParentType((String)((JUCtrlHierNodeBinding)node).getRow().getAttribute(&quot;ParentType&quot;));        <br />        setKey(((Number)((JUCtrlHierNodeBinding)node).getRow().getAttribute(&quot;NodeKey&quot;)).toString());<br />        setParentKey((Number)((JUCtrlHierNodeBinding)node).getRow().getAttribute(&quot;ParentKey&quot;));<br />    }<br /><br />    public Object getNode() {<br />        return node;<br />    }<br /><br />    public void setNodeType(String nodeType) {<br />        this.nodeType = nodeType;<br />    }<br /><br />    public String getNodeType() {<br />        return nodeType;<br />    }<br /><br />    public void setParentKey(Number parentKey) {<br />        this.parentKey = parentKey;<br />    }<br /><br />    public Number getParentKey() {<br />        return parentKey;<br />    }<br /><br /><br />    public String action() {<br />     ...<br />    }<br /><br />    public void setParentType(String parentType) {<br />        this.parentType = parentType;<br />    }<br /><br />    public String getParentType() {<br />        return parentType;<br />    }<br />}<br />&nbsp;

It can be – and should be – configured in the faces-config.xml file – or any other config file included in the web.xml file:

    &lt;context-param&gt;<br />        &lt;param-name&gt;javax.faces.CONFIG_FILES&lt;/param-name&gt;<br />        &lt;param-value&gt;/WEB-INF/faces-config.xml,/WEB-INF/JhsCommon-beans.xml,/WEB-INF/...&lt;/param-value&gt;<br />    &lt;/context-param&gt;<br /><br />

Note that if you create your own config-file and add it here, you will have no issues with regeneration overwriting managed-bean entries. 

&lt;managed-bean&g
t;<
br />  &lt;managed-bean-name&gt;TreeHelper&lt;/managed-bean-name&gt;<br />  &lt;managed-bean-class&gt;nl.amis.view.TreeHelper&lt;/managed-bean-class&gt;<br />  &lt;managed-bean-scope&gt;session&lt;/managed-bean-scope&gt;<br />&lt;/managed-bean&gt;&nbsp;<br /><br />

Alternatively we can also have TreeHelper extend from oracle.jheadstart.controller.jsf.bean.TreeBean. In that case, we do not have to add the setActionListener element to the HrmTereTree.jspx node-facets. However, we would then have to change the generated HrmTree-beans.xml (modify the managed-bean-class element.

Edit the Application Definition File

Let’s create the Groups in the JHeadstart Application Definition file. Let’s start with the tree.

We will create a group for the root of our tree – based on the HrmTreeFirstLevelView1 VO usage. The Layout Style is either Tree – when the node cannot be selected – or Tree-Form when it can be selected. The Descriptor Item is NodeLabel (for all groups in the tree). Note that the Data Collection will be the same as the Tree Data Collection.

Under the root we create a Detail Group for HrmTreeSecondLevelView1 – the only option available in the drop down list for the Tree Data Collection property. All other settings are the same as for the Root group.

Now create nested Detail Groups for all levels in the tree.

 

 

Create Groups for Selected Nodes – Edit Form Page or Multi Record Page

 

For each type of node that will be selectable, we will need to create a Group. As discussed before, we will either create Table Layout Style for Groups to be associated with Nodes like Clerks or Subordinates and Form Layout Style for Groups associated with Nodes for individual Department or Employee records. These Groups are just normal groups like any you may create in a JHeadstart Application. There are two important things to deal with:

  • In the Table Groups, we usually need to define a bind parameter: the View Object underlying the Table Groups – like ClerksInDepartment or SubordinatesUnderEmployee – will typically have a bind-parameter – like department or manager – that we need to supply a value for. In the Group definition for the Table Groups, go to the Query Bind Parameters property and specify the expression for this bind-parameter. For example: department=#{TreeHelper.key} or manager=#{TreeHelper.key}. The key property in the TreeHelper bean will contain the NodeKey for the selected node; this should be the Deptno for the department under which we have selected the Clerks node or the Empno of the Employee under which we have selected the Subordinates node.

 

  • In the Form Groups, we need to enable Deep Linking. Switch to Expert mode in the Application Definition Editor by clicking on the little Pawn Icon. If you do not see a pawn and you do see a crown – in the upper right hand corner of the property palette – then you are in Expert Mode. Go to the Deep Linking section in the property palette. Choose Query by Key Value as Type of Deep Linking. Set the Deep Linking Key Expression to #{TreeHelper.key}. This ensures that if we navigate to the Edit (Form) page generated for this Group, it will query the underlying ViewObject using the value in TreeHelper.getKey() as primary key. Thus we can go edit the currently selected Department or Employee if TreeHelper.getKey() returns the deptno or empno of the selected Department or Employee.

Generate the Application

 

Generate the application using the JHeadstart Application Generator. When done, you can run it – the tree should work, but selecting nodes either takes you to the Edit Node page, instead of the Edit Employee or Edit Department page, or will fail altogether. We need some post-gen modifications first.

Post Generation Modifications – add tree to Edit Pages and set up the Navigation

 

In summary, we have to modify two things in the now generated application:

  • We have to integrate the Tree with all the Edit Pages – both in terms of JSPX file as well as ADF PageDefinitions
  • We have to extend the generated Tree to have it interact with our TreeHelper bean in the proper way – setting the correct TreeHelper.key value (essential for setting the bind parameter for the table pages and the key value for the deep link to the form pages) as well as the proper Action outcome (depending on the currently selected node type, a node on a certain level can direct to various pages).

Integrate the tree into all edit pages 

First the integration of the tree in all other generated pages. Open the HrmTree.jspx page that was generated for the RootGroup in our tree (it can be called something else, it just is the page where the tree starts). Locate the section af:facet with name equals menu3.

&lt;f:facet name=&quot;menu3&quot;&gt;              <br />    &lt;af:region id=&quot;HrmTreeTree&quot; value=&quot;#{bindings}&quot; regionType=&quot;view.region.HrmTreeTree&quot;&gt;<br />    &lt;/af:region&gt;<br />&lt;/f:facet&gt;<br />&nbsp;

Copy this section (select and ctrl+c). Paste this facet in every one of the generated pages intended for link-from-nodes-in-the-tree. It goes right below the messages facet.

The second part of the tree integration deals with the PageDefinition files. Go to the PageDefinition file for the page generated for the RootGroup in our tree – HrmTreePageDef.xml in my case. Locate the iterator for the tree – in the <executables> section of the PageDefinition:

    &lt;iterator id=&quot;HrmTreeTreeIterator&quot;<br />              Binds=&quot;AppModuleDataControl.HrmTreeFirstLevelView1&quot;<br />              DataControl=&quot;AppModuleDataControl&quot; RangeSize=&quot;-1&quot;/&gt;<br />&nbsp;

Copy and Paste this iterator to the PageDefinition file for all generated pages we want to link to from the nodes in our tree. This means for each of these pages, we can access this iterator, used for populating the tree model.

Go back to the PageDefinition file for the page generated for the RootGroup in our
tree – HrmTreePageDef.xml in my case. Locate the tree
component – in the <bindings> section of the PageDefinition:

    &lt;tree id=&quot;HrmTreeTree&quot; IterBinding=&quot;HrmTreeTreeIterator&quot;<br />          ApplyValidation=&quot;false&quot;&gt;<br />      &lt;AttrNames&gt;<br />        &lt;Item Value=&quot;NodeKey&quot;/&gt;<br />        &lt;Item Value=&quot;NodeType&quot;/&gt;<br />        &lt;Item Value=&quot;NodeLabel&quot;/&gt;<br />        .... <br />

Copy and Paste this <tree> component to the bindings section of the PageDefinition file for all
generated pages we want to link to from the nodes in our tree. This makes the tree model that is referenced from the tree region we included into every page using the menu3 facet available in the PageDefinition for every page where we need it. 

Extend the Generated tree – bind to the TreeHelper bean

We need small modifications in the generated tree itself. You will find the tree under the node Web Content\regions in the JDeveloper Applications Navigator. In my case the file is called HrmTreeTree.jspx – the name of the RootGroup with Tree appended to it.

In this file, you will find the <af:tree> component which contains an <af:facet  name="nodeStamp"> which contains a <af:switcher> element. Within this switcher, you will see facet-components for each node level in our tree. Nodes that can be selected contain two af:setActionListener elements. You should add one:

&lt;af:setActionListener from=&quot;#{node}&quot;<br />                      to=&quot;#{TreeHelper.node}&quot;/&gt;<br />

Note: you will have to add this setActionListener to each facet corresponding to a selectable node-level! This setActionListener takes care that before any ActionListener is invoked or the method bound to the action attribute is invoked, the value in #{node} is copied to #{TreeHelper.node}. That ensures that the TreeHelper bean is immediately aware of the currently selected node: type, key and parent. Using this information it will make the key available from the getKey() method – used for setting bindParameters and deepLinking key values – as well as return the proper action value for navigating to the proper page when a node is selected.

For this latter to actually happen, we need to bind the action attribute of the af:commandLink in every node to the action method in the TreeHelper bean:

  &lt;f:facet name=&quot;HrmTreeSecondLevelView1Node&quot;&gt;<br />        &lt;af:commandLink text=&quot;#{node.NodeLabel}&quot; <br />           immediate=&quot;true&quot;  onclick=&quot;return alertForChanges()&quot;<br />           action=&quot;#{TreeHelper.action}&quot;<br />        &gt;<br />      &lt;af:setActionListener from=&quot;#{HrmTreeTree.tree.rowKey}&quot;<br />                            to=&quot;#{HrmTreeTree.focusRowKey}&quot;/&gt;<br />      &lt;af:setActionListener from=&quot;#{node}&quot;<br />                            to=&quot;#{HrmTreeTree.selectedNode}&quot;/&gt;<br />      &lt;af:setActionListener from=&quot;#{node}&quot;<br />                            to=&quot;#{TreeHelper.node}&quot;/&gt;<br />&nbsp;

When the command link is activated – the user selects a node in the tree – the action method on the TreeHelper bean is invoked. It will return a String that is used to lookup the appropriate navigation in the faces-config.xml file. Remove the actionListener attribute that may have been set up in the node facets (something like: actionListener="#{data.HrmTreeSecondLevelPageDef.setCurrentRowWithKeyHrmTreeSecondLevel.execute}" should be removed).

Now that we have an overview of all node types and pages we want to link to from the tree, we can implement the action method in the TreeHelper bean.

    public String action() {<br />        if (&quot;subLabel&quot;.equalsIgnoreCase(nodeType))<br />          return &quot;StartSubordinatesUnderEmployee&quot;;<br />        if (&quot;clerksLabel&quot;.equalsIgnoreCase(nodeType))<br />          return &quot;StartClerksInDepartment&quot;;<br />        if (&quot;salesmenLabel&quot;.equalsIgnoreCase(nodeType))<br />          return &quot;StartSalesmenInDepartment&quot;;<br />          <br />        if (&quot;loc&quot;.equalsIgnoreCase(nodeType))<br />          return &quot;DeepLinkLocations&quot;;<br />        if (&quot;dept&quot;.equalsIgnoreCase(nodeType))<br />          return &quot;DeepLinkDept&quot;;<br />        if (&quot;emp&quot;.equalsIgnoreCase(nodeType))<br />          return &quot;DeepLinkEmp&quot;;<br />        return &quot;&quot;;  <br />    }<br /><br />

 This implementation will have to be modified if we re-generate pieces of the application and thereby alter the faces-config.xml file. 

 

Next Steps

From the screenshots in this article, it is clear that there are several things that need tweaking:

  • the menu contains too many items
  • the context title is not correct in all cases – Edit Clerks should also say Edit Clerks in Department 30 (or SALES)
  • the bread-crumbs are confusing – every edit page is included in the bread-crumbs, which is not desirable 

Resources

Download the JDeveloper 10.1.3/JHeadstart 10.1.3 Beta (build 84) Application with the above outlined implementation: JHSLabeledHrmTree.zip


Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

6 Comments

  1. Hi Lucas,

    Sorry, I think I hastily sent you the previous message. I will work through your article and see how it goes. Will post another message if I get stuck.

    You mentioned “Linking the tree node action to a backing bean method that uses the setCurrentRowWithKey operation is perhaps the most complex step “. Any pointers on this?

    Also what about traversing through the main form/table which inturn highlights the selected node record in the tree???

    Thanks
    Rahul

  2. Hi Lucas,

    Thanks for the reply.

    I am new to JDeveloper and infact new to java. I am currently working on a simple prototype for an application that searches, updates, adds data to tables.

    I have a page developed in jdeveloper (without jheadstart) that has an adf tree on the left hand side and a form with navigational/create/submit buttons on the main panel. Both are coming from the same view. This was the easy bit.

    I had a look through your article (http://technology.amis.nl/blog/?p=1306) which talks about a helper bean. I am unable to compile this class due to errors in “import javax.faces.application.Application;
    import javax.faces.context.FacesContext; as well as due to no code in action() “ public String action() { … }”. Is it the helper bean that handles the link between the tree and the form/table? Do you have sample code for the bean that uses the setCurrentRowWithKey operation.

    Also what about traversing through the main form/table wich nturn highlights the selected node record in the tree???

    I would be very greatful if you could provide some assistance.

    Thanking You

    Rahul

  3. Hi Rahul,

    Of course you can. JHeadstart is a combination of a generator that generates ADF Faces pages we could also code by hand and a run-time library in Java that performs a number of supporting tasks – that we can code ourselves as well.

    There is nothing in JHeadstart that is magic, Oracle internal stuff. You can do the same things!

    Creating a tree is standard JDeveloper – ADF Faces and ADF Model drag & drop functionality. Changing the tree node to be an actionable link is described in my article. Embedding the tree in a page that also contains a table component is a piece of cake. Linking the tree node action to a backing bean method that uses the setCurrentRowWithKey operation on the iterator backing the table is perhaps the most complex step – but again no magic is involved.

    Good luck!

    Lucas

  4. Hi,

    Here you are relying on jheadstart. Can you achieve the same functionality without jheadstart. (Selecting a node in the tree to automatically navigate to the correct record in the table or form on the detail frame of the pag

  5. Hello Lucas,

    I’ve sent a mail to infoATamis.nl.
    Could you contact me please, my company needs some expertise and i’d like to work with you.

    Thanks a lot,
    Seb.

  6. Hi,
    Your articles are very useful.
    I’m particularly interested in tje following :
    “using supporting infrastructure with Maven 2, Subversion, Jira, Confluence Wiki etc”
    I’d like to use the same infrastructure and i’d like to know how you effectively put it in place.
    I’m particularly interested in Maven 2 integration. I know a plugin exists for JDeveloper taken from Trinidad project and i’ve read your article on it.
    Could you write an article explaining more in depth how you managed this integration with details please ?

    Thanks a lot for you valuable work for the community.
    Seb.