Saturday, 12 May 2007

Back to programming: Programmatic ADF Faces Tree component

Typically ADF Faces components in our JDeveloper 10.1.3 applications are data bound, meaning they map to objects in the binding layer as specified in separate pageDef files. This is expressed in our components through EL references to bindings}.

However what if we don't want to map to a binding layer object, but instead want to construct the data elements programmatically? For this blog we'll consider the example of an <af:tree> component to demonstrate this, showing how we construct the elements in the tree programmatically rather than relying on bindings.

Our goal is to construct an <af:tree> component comprised of <af:goLink> tags and looks as follows:

Logically understanding our hierarchy

Before we consider the <af:tree> component specifically, have a think about what the hierarchy represents. Each node in the tree has a text title, and a hyperlink destination. Each node is a child to a parent node, and each node may be comprised of 0, 1 or more child nodes. Collectively this represents our tree and is a simple logical concept.

Understanding the ADF Faces Tree component

The code for an <af:tree> in a web page would typically look as follows:

<af:tree var="node" value="#{treeModel.model}">
  <f:facet name="nodeStamp">
    <af:goLink text="#{node.text}" destination="#{node.destination}"/>
  </f:facet>
</af:tree>


In our example you'll note that the nodes within the tree are comprised of <af:goLink> tags that render the links in our tree above. You'll also note if you constructed a <af:tree> tag using the drag n drop facilities of JDeveloper based on a data bound component from the data control palette, the code would look very similar. In other words there isn't anything really special about the code above besides the use of <af:goLink> tags.

The <af:tree value> attribute demands at runtime an oracle.adf.view.faces.model.TreeModel instance. This class is the data model for the ADF Faces Tree component, where rows in the tree may contain other trees, or alternatively described, nodes in the tree can contain children nodes which in turn can also have children nodes and so on.

The nodeStamp facet of the <af:tree> walks the TreeModel, creating for each node the subtags contained within the <f:facet> tag, in this case an <af:goLink> tag. You'll note the <af:goLink> tag makes reference to the current node in the TreeModel as it walks the tree via the <af:tree> tag's var="node" attribute.

So we now logically have an understanding of what we want to model, and we also understand what the <af:tree> component wants. Lets consider our design solution.

Keeping it simple with a POJO

From the design point of view, to model the hierarchy we'd like to create our own very simple POJO TreeItem that stores a String text and String destination required for each goLink, along with a List of children TreeItem objects to reflect the hierarchy. At runtime we'll construct this tree programmatically populating the hierarchy in any way we desire.

The POJO approach is very easy to understand and implement. However the problem with our POJO approach is the datatype doesn't match that required by the <af:tree>, namely oracle.adf.view.faces.model.TreeModel. How do we address this?

Creating an Adapter between our TreeItem and TreeModel

In the OO world they have the concept of the adapter design pattern. An Adapter class is one that wraps another interface, to provide an interface the client expects. In our case we have our POJO TreeItem interface, but our client the <af:tree> demands an oracle.adf.view.faces.model.TreeModel, so we'll provide a class called TreeModelAdapter to work as the adapter. It will be this class that we'll define in our faces-config.xml file as a managed bean for the <af:tree> control to make use of at runtime.

The TreeModelAdapter will internally store our TreeItem hierarchy and TreeModel, and provide accessor methods to access and manipulate these items.

Coding the solution

The following describes the steps in coding our design solution. It assumes you've already created your web page with the <af:tree> code as previous:

Create our POJO Java Bean TreeItem class as follows:

package view;

import java.util.List;

public class TreeItem {
  private String _text = null;
  private String _destination = null;
  private List<treeitem> _children = null;

  public TreeItem(String text, String destination) {
    setText(text);
    setDestination(destination);
  }

  public String getText() { return _text; }
  public void setText(String text) { this._text = text; }

  public String getDestination() { return _destination; }
  public void setDestination(String destination) { this._destination = destination; }

  public List getChildren() { return _children; }
  public void setChildren(List children) { this._children = children; }
}

Note the following about the TreeItem class:
  • Internally stores the Strings text and destination and provides appropriate accessors.
  • Contains a list of child TreeItems _children that may contain 0, 1 or many child TreeItem objects within the current TreeItem. This represents the hierarchy. Note that it is important the Java Bean provides accessors specifically named getChildren() and setChildren() to retrieve the private _children attribute as you'll see in a moment.
Create a TreeModelAdapter class:

package view;

import java.beans.IntrospectionException;
import java.util.ArrayList;
import java.util.List;
import oracle.adf.view.faces.model.ChildPropertyTreeModel;
import oracle.adf.view.faces.model.TreeModel;

public class TreeModelAdapter {
  private Object _instance = null;
  private transient TreeModel _model = null;

  public TreeModelAdapter() {

    ArrayList<TreeItem> rootTreeItems = new ArrayList<TreeItem>();

    TreeItem treeItem1 = new TreeItem("Fish", "http://www.someurl1.com");
    TreeItem treeItem2 = new TreeItem("Dog", "http://www.someurl2.com");
    TreeItem treeItem3 = new TreeItem("Cat", "http://www.someurl3.com");

    TreeItem treeItem2_1 = new TreeItem("Blue Heeler", "http://www.someurl4.com");

    TreeItem treeItem2_1_1 = new TreeItem("Rover", "http://www.someurl5.com");
    TreeItem treeItem2_1_2 = new TreeItem("Ruffus", "http://www.someurl6.com");

    rootTreeItems.add(treeItem1);
    rootTreeItems.add(treeItem2);
    rootTreeItems.add(treeItem3);

    ArrayList<TreeItem> treeItem2Children = new ArrayList<TreeItem>();
    ArrayList<TreeItem> treeItem2_1Children = new ArrayList<TreeItem>();

    treeItem2Children.add(treeItem2_1);
    treeItem2.setChildren(treeItem2Children);

    treeItem2_1Children.add(treeItem2_1_1);
    treeItem2_1Children.add(treeItem2_1_2);
    treeItem2_1.setChildren(treeItem2_1Children);

    this.setListInstance(rootTreeItems);
  }

  public TreeModel getModel() throws IntrospectionException {
    if (_model == null) {
      _model = new ChildPropertyTreeModel(_instance, "children");
    }
    return _model;
  }

  public void setListInstance(List instance) {
    _instance = instance;
    _model = null;
  }
}


Note the following about the TreeModelAdapter class:
  • It contains a private oracle.adf.view.faces.model.TreeModel _model attribute. This is the TreeModel we'll provide to the <af:tree> tag. It is exposed via the getModel() method, and you previously saw this method was called via the <af:tree value="#{treeModel.model"> EL expression.
  • The class also contains an Object named _instance. This is where we'll store our own programmatic tree constructed from our own home baked Java Bean TreeItem hierarchy. The nice thing about this implementation is it doesn't care if it gets our TreeItem class, or any other sort of Java Bean, as long as it follows the Java Bean spec by providing accessors to retrieve the attributes, including specifically accessors for _children (more on this soon). The setListInstance() accessor provides a method to set the Object _instance.
  • In our simple example here, the constructor creates 3 root TreeItem nodes "Fish", "Dog", "Cat". For the "Dog" TreeItem node it creates a child "Blue Heeler" node. For the "Blue Heeler" node it creates 2 TreeItem nodes "Rover" and "Ruffus" in turn. The constructors last action is to assign the TreeItem hierarchy we've populated into rootTreeItems into the TreeModelAdapter class's _instance private attribute.
  • The getModel() method requires further explanation. The ChildPropertyTreeModel class is a subclass of oracle.adf.view.faces.model.TreeModel. It allows the construction of a TreeModel based on a list of beans. This suites our purposes well as we've constructed a list of TreeItem beans stored in the TreeModelAdapter's _instance attribute. You'll note that the ChildPropertyTreeModel constructor for its 2nd parameter specifies the String "children". The ChildPropertyTreeModel class uses this name to work out what accessor methods it needs to use in the TreeItem Java Bean to access the hierarchical part of the TreeItem class. It's important that the string "children" matches the accessor names getChildren() and setChildren() in the TreeItem class following the Java Bean specification rules. If these mismatch you'll get a runtime error.
Finally in our faces-config.xml file we declare our managed bean as follows:

<managed-bean>
  <managed-bean-name>treeModel
  <managed-bean-class>view.TreeModelAdapter
  <managed-bean-scope>request
</managed-bean>

Note that the request scope used for treeModel will result in the instantiation of TreeModelAdapter for each request to the page containing the treeModel reference, potentially an expensive exercise. If the tree is duplicated across multiple screens a session scope may be appropriate.

Credit

To give credit the inspiration for the TreeModelAdapter in this solution is a modification of MenuTreeModelAdapter from Oracle's ADF Developer's Guide for Forms/4GL Developers 10.1.3 (page 19-8). Those with lateral thinking should be able to see the ability to create programmatic <af:page> and <af:panelPage> menus with this technique.

Final Caveat

The code above is not applicable for JDev 11g and its new Rich Faces components as the class structure for the components has changed. Once Oracle releases the JavaDocs for the components I'll look at revising this article on request.

Friday, 11 May 2007

JDeveloper in the Aussie media wild

JDeveloper made it into the local Aussie online media again thanks to Oracle's JavaOne announcements, with a news story at Builder.au. Mind you I'm not so sure why the journalist refers to JBuilder and who "a company" is? Nice to see the tool is getting some local coverage though.

Addition: a more detailed post at itNews.com.au reports the announcement.

Wednesday, 9 May 2007

Boost your productivity with JDev 11g's new faster "Red" IDE

Thanks to Brian Duff I've happily discovered we can now all experience JDeveloper in our favourite colour scheme. Thanks to the Tools -> Preferences -> Environment -> Theme option we have a relative rainbow of colour schemes to choose from under the JDev 11g preview edition.

If you run JDev 11g under the Sky Red theme your productivity will increase by no less than a guaranteed 3.824% according to the new 11g documentation. Rumours are that at this year's San Fran OOW the JDev team will announce a new "Racey Red" colour scheme that will boost programmer productivity by a minimum 12.66%.

Nice work on the Swing themes JDev team!

Tuesday, 8 May 2007

JDev 11g Tech Preview is here

Many congtraz to the JDev team for finishing the JDeveloper 11g Tech Preview (as blogged by Shay Shmeltzer) available from here for JavaOne. Just this morning I was telling customers I wished JDev had a page templating mechanism and some sort of task flow facility, and "Yeehaw!".... it arrived today :)

Now if only they would turn the whole IDE interface to "Racey-Red" like I requested some time back.

Thursday, 3 May 2007

Canberra ACTOUG JDeveloper demonstration

For those readers in Canberra Australia, the ACT Oracle User Group (ACTOUG) has invited me to present a 1 day demonstration of Oracle's JDeveloper 10.1.3, focusing on ADF Business Components and ADF Faces, on the 17th May. As I'm always keen to support local Oracle user groups and the Oracle community, I readily agreed to show how simple JDev is for web application development.

More details regarding the presentation are below. If you're interested in attending please head to the ACTOUG website to retrieve the registration details. Hope to see you there.

Taking the J out of Oracle JDeveloper - web page development without the fear of Java

Scared of JDeveloper because you don't know Java, but dying to develop Oracle web applications? Fear not! Let the ACT Oracle User Group introduce you to JDeveloper web development without a single line of Java! In this 1 day presentation Chris Muir from Sage Computing Services will introduce you to the beauty of JDeveloper's drag 'n' drop, wizard and editor IDE for quickly developing a web application using ADF Business Component and ADF Faces.

Agenda

Morning session:

  • Introduction to JDeveloper and the Application Development Framework (ADF)
  • JDeveloper installation and creating a database connection
  • Creating an application workspace and projects
  • Using the ADF Business Components from Table wizard
  • Adding Entity Object business default values, formatting and validation rules
  • Adding View Object SQL calculate attributes and bind variables

Afternoon session:

  • Introduction to ADF Faces and the binding layer
  • Creating JavaServer Faces web pages, read only tables, editable input forms and master-detail pages
  • Implementing page navigation rules
  • Enhancing your web pages