Apache MyFaces (open source JSF implementation) – Using the Tree2 Component with JDeveloper 10.1.3

In the wake of my last post – Getting started with Java Server Faces (JSF) using Apache MyFaces – in JDeveloper 10.1.3EA 
– I will continue my explorations of MyFaces in this article. In
particular, I will take a look at the Tree2 Component that is shipped
with Apache MyFaces. I will make use of Oracle JDeveloper 10.1.3 EA as
my IDE. For details on setting up Apache MyFaces with JDeveloper
10.1.3, see the post I just mentioned. To make things easier for me –
and hopefully for some of you as well – I have created a
starter-project, a JDeveloper 10.1.3 Application Workspace with
MyFacesBase project that contains the files you need to get started
with MyFaces. You can simply download this zipfile: MyFacesBase.zip,
extract it on your PC and open the JWS file in JDeveloper 10.1.3. You
still need to set up the project with proper library definitions – see
the previous post for details.

Starting from this MyFacesBase Project we will create a beautiful tree-based JSF application. Sort of.

Steps to prepare

I will first gear the base environment to my specific Tree oriented project:

  • Extract MyFacesBase.zip to a directory; let’s refer to that directory as MyFacesTree_HOME
  • Change
    the string Base to Tree in the names of the subdirectories
    MyFacesTree_HOME\MyFacesBase and
    MyFacesTree_HOME\MyFacesBase\MyFacesBase
  • Open JDeveloper 10.1.3
  • Load the file MyFacesBase.jws; this will open the MyFacesBase application workspace
  • Rename the Application to MyFacesTree.
  • Change
    the name of the MyFacesBaseProject to MyFacesTreeProject. If you do not
    already see the project, then add it to the Application Workspace by
    adding the fiule MyFacesBaseProject.jpr.
  • Go to the Project
    Properties of the MyFacesTree project. Verify in the Libraries tab
    whether the libraries MyFaces and JSTL are set up correctly and added
    to the project. If necessary see the post mentioned above. Also check
    whether the JSP Tag Libraries MyFaces-Core, MyFaces-HTML and
    MyFaces-ext (or Tomahawk) have been added to the project.

Verifying the preparations

Now
is a good time to make sure that we can indeed create and run JSF
applications in the JDeveloper 10.1.3 environment and project we just
set up. Follow the these simple steps:

  • From the Right Mouse
    Button Menu on the MyFacesTreeProject project, go to the New Gallery.
    Select the node Web Tier, JSF and select the option JSF JSP. Walk
    through the Wizard, accepting all default choices – or making
    refinements as you like.
  • The wizard create a JSP with basic JSF
    layout – importing the required Tag Libraries and layout a
    <f:view> and a <f:form>. The JSP is opened in the editor.
    Type a few characters – just to have some output to look at in our
    browser. Note: at this point we have not made any changes to
    faces-config.xml or any other configuration file.
  • From the RMB
    menu on the JSP, choose the option run. The “application” should now
    open in the browser, displaying your trivial output.

If you have gotten this far, you are in good shape to start using the MyFaces Tree component.

Developing with the MyFaces Tree2 Component

Apache
MyFaces is a valuable open source project providing the base
implementation of the JSF standard and extending it with a large set of
advanced components, called Tomahawk. The set includes TabbedPane,
RssReader, Advanced Table features, inputDate and Calendar, HtmlEditor,
Pulldown Menu, and many others. To confuse matters, there is a Tree, a
Tree2 and a Tree Table component. It seems that Tree2 effectively
replaces Tree; Tree is labeled ‘sort of obsolete’. So I focus on Tree2
for now.

Using the JSP JSF wizard from the New Gallery I create
a new JSP called LibraryTree.jsp. From the Component Palette, we drag
and drop the Tree2 component to the JSP. The page source looks like:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@ page contentType="text/html;charset=windows-1252"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://myfaces.apache.org/extensions" prefix="t"%>
<f:view>
    <html>
        <head>
            <meta http-equiv="Content-Type"
                  content="text/html; charset=windows-1252"/>
            <title>LibraryTree</title>
        </head>
        <body><h:form>
                <t:tree2 id="serverTree"
                         value="#{LibraryTreeHandler.treeModel.treeData}"
                         var="node" varNodeToggler="t" clientSideToggle="false">
                    <f:facet name="book">
                        <h:panelGroup>
                            <t:graphicImage value="/images/yellow-folder-open.png"
                                            rendered="#{t.nodeExpanded}"
                                            border="0"/>
                            <t:graphicImage value="/images/yellow-folder-closed.png"
                                            rendered="#{!t.nodeExpanded}"
                                            border="0"/>
                            <h:outputText value="Book: "
                                          styleClass="nodeFolder"/>
                            <h:outputText value="#{node.description}"
                                          styleClass="nodeFolder"/>
                        </h:panelGroup>
                    </f:facet>
                    <f:facet name="author">
                        <h:panelGroup>
                            <t:graphicImage value="/images/yellow-folder-open.png"
                                            rendered="#{t.nodeExpanded}"
                                            border="0"/>
                            <t:graphicImage value="/images/yellow-folder-closed.png"
                                            rendered="#{!t.nodeExpanded}"
                                            border="0"/>
                            <h:outputText value="#{node.description}"/>
                            <h:outputText value=" (Author)"
                                          styleClass="nodeFolder"/>
                        </h:panelGroup>
                    </f:facet>
                    <f:facet name="foo-folder">
                        <h:panelGroup>
                            <t:graphicImage value="/images/yellow-folder-open.png"
                                            rendered="#{t.nodeExpanded}"
                                            border="0"/>
                            <t:graphicImage value="/images/yellow-folder-closed.png"
                                            rendered="#{!t.nodeExpanded}"
                                            border="0"/>
                            <h:outputText value="#{node.description}"
                                          styleClass="nodeFolder"/>
                            <h:outputText value=" (#{node.childCount})"
                                          styleClass="childCount"
                                          rendered="#{!empty node.children}"/>
                        </h:panelGroup>
                    </f:facet>
                </t:tree2>
            </h:form></body>
    </html>
</f:view>

There
are a few things worth noting here. First of all, the tree2 components
has child facets; there is a facet for each type of node that the tree
will contain. Apparently our tree wil contain nodes of types
foo-folder, author and book. We will see later how we specify the node
type on the backing bean’s model data. Each facet describes how the
node type must be displayed; various node-types can make use of
different icons, styles, actions etc. Note how the Node Interface
provides properties like childCount, nodeExpanded and nodeSelected.

The
value for the tree2 component is set to:
value=”#{LibraryTreeHandler.treeModel.treeData}”. That suggests that
there is a Managed Bean called LibraryTreeHandler that exposes a
getTreeModel method that returns an object that has a method
getTreeData(). This last method must return an object that implements
the TreeNode interface – in package org.apache.myfaces.custom.tree2.

So there we go: create the required objects. First the LibraryHandler class:

package nl.amis.myfaces.tree;


public class LibraryHandler {

    private LibraryTreeModel treeModel;

    public LibraryHandler() {
      treeModel = new LibraryTreeModel();
    }
    
    public LibraryTreeModel getTreeModel() {
        return treeModel;
    }

}

This
class is a used for the managed bean called LibraryTreeHandler and
referenced by the value property of the tree2 component. See the
faces-config.xml file:

<?xml version="1.0" encoding="windows-1252"?>
<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config xmlns="http://java.sun.com/JSF/Configuration">
  <managed-bean>
    <managed-bean-name>LibraryTreeHandler</managed-bean-name>
    <managed-bean-class>nl.amis.myfaces.tree.LibraryHandler</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
  </managed-bean>
</faces-config>

The LibraryHandler class relies on the LibraryTreeModel class. Its code is as follows:

package nl.amis.myfaces.tree;

import org.apache.myfaces.custom.tree2.TreeModel;
import org.apache.myfaces.custom.tree2.TreeNode;
import org.apache.myfaces.custom.tree2.TreeNodeBase;

public class LibraryTreeModel  {
    public LibraryTreeModel() {
    }
    
    public TreeNode getTreeData()
    {
        TreeNode treeData = new TreeNodeBase("foo-folder", "Library", false);
        
        TreeNodeBase authorNode = new TreeNodeBase("author", "Ben Elton", false);
        TreeNodeBase bookNode = new TreeNodeBase("book", "Post Mortem", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "Blast from the Past", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "Stark", false);
        authorNode.getChildren().add(bookNode);
        treeData.getChildren().add(authorNode);

        authorNode = new TreeNodeBase("author", "Orson Scott Card", false);
        bookNode = new TreeNodeBase("book", "Ender's Game", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "Xenocide", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "Shadow Puppets", false);
        authorNode.getChildren().add(bookNode);
        treeData.getChildren().add(authorNode);

        authorNode = new TreeNodeBase("author", "Rod Johnson", false);
        bookNode = new TreeNodeBase("book", "J2EE Design and Development", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "J2EE without Enterprise Java Beans", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "Professional Java Development With The Spring Framework", false);
        authorNode.getChildren().add(bookNode);
        treeData.getChildren().add(authorNode);        
        return treeData;
    }
}

The
application looks like this in the browser. We can expand and collapse
nodes. Note that all tree manipulation is done server side: each node
expansion requires browser-to-server communication for updating the
treenodes.

Apache MyFaces (open source JSF implementation) - Using the Tree2 Component with JDeveloper 10.1.3 myfacesTree2 1

The Project with the application so far can be downloaded here:  MyFacesTree_Stage1.zip

Make the LibraryTree application a little more interesting

We
are now going to make the application a little more interesting. We
will allow Author nodes to be selected and we will display the
Biography of the currently selected author – marked in the tree with an
asterisk – next to the tree. It is remarkable how simple it is to add
these changes to the application.

Apache MyFaces (open source JSF implementation) - Using the Tree2 Component with JDeveloper 10.1.3 myfacesTree2 2

If
we select an Author node in the tree, the tree is redrawn and an
asterisk is placed in front of that node – in the screenshot the
selected node is for Orson Scott Card. At the same time, when the page
is refreshed, the Results of the currently selected author are updated
with the biography for the currently selected author.

The changes I had to make to realize this functionality are in three location:

1. First the JSP LibraryTree.jsp

I have added the asterisk for the currently selected Author node:

<h:outputText value="*" rendered="#{t.nodeSelected}"/>

You can see how simple it is: the asterisk is only rendered if the current node in the tree is selected.

To make the author node selectable, we add a commandLink:

  <h:commandLink action="nodeClicked"
     actionListener="#{LibraryTreeHandler.processAction}"
     value="#{node.description}"
     immediate="true">
  </h:commandLink>

When
the node is clicked on, the processAction method in the
LibraryTreeHandler bean is invoked; this method needs to have a return
type of void and accept a single ActionEvent input parameter.

The next change is displaying the biography for the current author:

  <h:panelGroup rendered="#{LibraryTreeHandler.authorBio!= null }">
    <f:verbatim>
      <h3>Results of the currently selected author</h3>
    </f:verbatim>
    <h:outputText style="font-size:70%; font-family:arial" value="#{LibraryTreeHandler.authorBio}"/>
  </h:panelGroup>

I
used a panelGroup for the rendered property: the entire panelGroup is
only displayed if there is indeed a Biography available. Note that the
bio is read from the authorBio property in the LibraryTreeHandler bean
that is managed based on the LibraryHandler class. We will see that
class in a moment.

I finally made use of a panelGrid to display
the tree and the bio next to each other. A panelGrid element in HTML is
typically rendered as table. Okay, it is not pretty but it will do the
trick.

2. The LibraryHandler class

This class now suddenly needs to provide an authorBio property and it has to absorb the processAction event.

package nl.amis.myfaces.tree;

import javax.faces.component.UIComponent;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;

import org.apache.myfaces.custom.tree2.HtmlTree;
import org.apache.myfaces.custom.tree2.TreeNodeBase;


public class LibraryHandler {

    private LibraryTreeModel treeModel;

    private String authorBio;

    public LibraryHandler() {
        treeModel = new LibraryTreeModel();
    }

    public LibraryTreeModel getTreeModel() {
        return treeModel;
    }

    public void setAuthorBio(String authorBio) {
        this.authorBio = authorBio;
    }

    public String getAuthorBio() {
        return authorBio;
    }

    public void processAction(ActionEvent event) throws AbortProcessingException {
        UIComponent component = (UIComponent)event.getSource();
        while (!(component != null && component instanceof HtmlTree)) {
            component = component.getParent();
        }
        if (component != null) {
            HtmlTree tree = (HtmlTree)component;
            TreeNodeBase node = (TreeNodeBase)tree.getNode();
            tree.setNodeSelected(event);
            setAuthorBio(((ExtendedTreeNode)node).getLongDescription());
        }
    }
}

You can see that we make use of a new class, the ExtendedTreeNode. So we also need to create that class:

3. The new ExtendedTreeNode class

In order to have the new biography property for our Authors, we introduce this class:

package nl.amis.myfaces.library;

import org.apache.myfaces.custom.tree2.TreeNode;
import org.apache.myfaces.custom.tree2.TreeNodeBase;

public class ExtendedTreeNode extends TreeNodeBase {

    private String longDescription;
    
    public ExtendedTreeNode( String type, String description, boolean p3, String longDescription) 
    { 
      super(type, description, p3);
      this.longDescription = longDescription;
    }

    public void setLongDescription(String longDescription) {
        this.longDescription = longDescription;
    }

    public String getLongDescription() {
        return longDescription;
    }
}

In
class LibraryTreeModel we use this class to populate the nodes of our
tree. The nodes we create for Authors are of the new ExtendedTreeNode
type that include the biography. So we modify the LibraryTreeModel.java
to:

package nl.amis.myfaces.tree;

import org.apache.myfaces.custom.tree2.TreeNode;
import org.apache.myfaces.custom.tree2.TreeNodeBase;


public class LibraryTreeModel  {
    public LibraryTreeModel() {
    }
    
    public TreeNode getTreeData()
    {
        String bio = null;
        TreeNode treeData = new TreeNodeBase("foo-folder", "Library", false);
        
        bio = " Already a successful comedian, Ben Elton turned to writing situation comedies during the 1980s and penned BBC classics such as \"The Young Ones\", \"Blackadder\", and during the 1990s \"The Thin Blue Line\".\n" + 
        "    He provided lyrics for Andrew Lloyd Webber Musical, The Beautiful Game, which was nominated for Best Musical at the Laurence Olivier Theatre Awards in 2001 (2000 season).\n" + 
        "    His comedy, Popcorn performed at the Apollo Theatre, was awarded the 1998 Laurence Olivier Theatre Award for Best New Comedy of the 1997 season.\n" + 
        "    Has three children : Bert, Lottie and Fred. Is co-writer of the Queen Musical 'We Will Rock You' with the band itself.\n" + 
        "Birth name: Benjamin Charles Elton. Height: 5' 8\" (1.73 m) \n"; 
        TreeNodeBase authorNode = new ExtendedTreeNode("author", "Ben Elton", false, bio);
        TreeNodeBase bookNode = new TreeNodeBase("book", "Post Mortem", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "Blast from the Past", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "Stark", false);
        authorNode.getChildren().add(bookNode);
        treeData.getChildren().add(authorNode);

        bio = "Nobody had ever won the Hugo and Nebula awards for best novel two years in a row, until Orson Scott Card received them for Ender's Game and its sequel, Speaker for the Dead, in 1986 and 1987. The third novel in the series, Xenocide, was published in 1991, and the fourth and seemingly final volume, Children of the Mind, was published in August 1996. However, the Ender cycle now includes the new parallel series that began with Ender's Shadow in 1999, followed by Shadow of the Hegemon in 2001, and continued with Shadow Puppets in 2002. Warner Brothers also recently announced that it has made a deal for director Wolfgang Petersen to bring Ender's Game to the big screen. Born in Richland, Washington, Card grew up in California, Arizona, and Utah. He lived in Brazil for two years as an unpaid missionary for the Mormon Church. He received degrees from Brigham Young University (1975) and the University of Utah (1981). He currently lives in Greensboro, North Carolina. He and his wife, Kristine, are the parents of five children: Geoffrey, Emily, Charles, Zina Margaret, and Erin Louisa (named for Chaucer, Bronte and Dickinson, Dickens, Mitchell, and Alcott, respectively).";
        authorNode = new ExtendedTreeNode("author", "Orson Scott Card", false, bio);
        bookNode = new TreeNodeBase("book", "Ender's Game", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "Xenocide", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "Shadow Puppets", false);
        authorNode.getChildren().add(bookNode);
        treeData.getChildren().add(authorNode);

        bio = "Rod Johnson is an enterprise Java architect with extensive experience in the insurance, dot-com, and financial industries. He was the J2EE architect of one of Europe's largest web portals, and he has worked as a consultant on\n" + 
        "a wide range of projects.\n Rod has an arts degree majoring in music and computer science from the University of Sydney. He obtained a Ph.D. in musicology before returning to software development. He has been working with both Java and J2EE since\n" + 
        "their release and is actively involved in the Java Community Process as a member of the JSR-154 (Servlet 2.4) and JDO 2.0 Expert Groups. He is the author of several best-selling books, like \"Expert One-on-One J2EE Design and Development\" (Wrox, 2002) and has contributed to several other books on J2EE since 2000. As founder of the Spring Framework (www.springframework.org), he speaks frequently at leading industry conferences.\n";
        authorNode = new ExtendedTreeNode("author", "Rod Johnson", false, bio);
        bookNode = new TreeNodeBase("book", "J2EE Design and Development", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "J2EE without Enterprise Java Beans", false);
        authorNode.getChildren().add(bookNode);
        bookNode = new TreeNodeBase("book", "Professional Java Development With The Spring Framework", false);
        authorNode.getChildren().add(bookNode);
        treeData.getChildren().add(authorNode);        
        return treeData;
    }
}

This
is all it takes to create a dynamic tree, driven from a backend data
model, including the select node action that updates the current
biography. In a next article I will actually use the EJB 3.0
Persistence functionality out of container (the GlassFish
implementation) for providing the backend data model. I will try to
make the nodes updateable as well.

Resources

The JDeveloper 10.1.3 MyFacesBase Application Workspace and Project:  MyFacesBase.zip

The JDeveloper 10.1.3 Application Workspace as it is at the end of this article:  MyFacesTree.zip

Overview of the Apache MyFaces Components – more specifically the Tree2 component

The MyFaces Wiki on Tree2

 

14 Comments

  1. Julian October 19, 2011
  2. Alb May 11, 2009
  3. dpak March 14, 2009
  4. Sanjeev Gour December 20, 2006
  5. monu September 25, 2006
  6. Arvind July 24, 2006
  7. Deepak k March 17, 2006
  8. florentina March 5, 2006
  9. Dipak Mehta February 13, 2006
  10. Arvind February 8, 2006
  11. vijay February 7, 2006
  12. Angus Rose January 26, 2006
  13. Angus Rose January 23, 2006
  14. Vofeka January 17, 2006