dev@jsftemplating.java.net

Re: dynamic tree node children woes

From: Ken Paulsen <Ken.Paulsen_at_Sun.COM>
Date: Thu, 21 Dec 2006 23:04:19 -0800

Hi Mike,

Rather than answer your questions as you asked them, I'll try to explain a high-level overview and then drill down on the points you need to know.  I think in the process, your questions will get answered.  Feel free to call me or email me if I don't provide enough information in this email.

The concept of the "DynamicTreeNodeFactory" is it will generate a node *and* 0 or more nodes under that node.  It will do this from *any* data source as long as their is an "adaptor" to that data source.

So in your case, you have children of a tree node that you want to display based on data returned for your api's (or however you get the data... that's not important for this overview).  So first off, the parent of the dynamic children is going to be the "dynamicTreeNode" -- I think you already know that, but I want to make sure.  The children exist in some data structure which you obtain via some backend calls (not important).  The DynamicTreeNode factory will require an "adaptor" to know how to interpret your data and generate TreeNode objects for them.  So, your work will be to create this adaptor class that understands your data structure.

So conceptually:

* Page contains dynamicTreeNode
* dynamicTreeNode uses a specific "adaptor" that can walk your data structure
* DynamicTreeNodeFactory will use your adaptor to walk your data structure and create the children.

Before we look at the adaptor class, lets look at the .jsf file.  Here's an example from peTree.jsf:

<dynamicTreeNode id="enterpriseApplications"
    treeAdaptorClass="com.sun.enterprise.tools.admingui.tree.MBeanTreeAdaptor"
    objectName="com.sun.appserv:type=applications,category=config"
    methodName="getAllDeployedJ2EEApplications"

    text="$resource{i18n.tree.enterpriseApps}"
    url="/applications/enterpriseApplications.jsf"
    target="main"

    attributeName="name"
    childImageURL="resource/images/application.gif"
    childTarget="main"
    childExpanded="$boolean{false}"
    childURL="applications/enterpriseApplicationsEdit.jsf?appName=#{$this{valueBinding}.text}">
    <!facet image>
        <sun:iconHyperlink id="abc" icon="TREE_FOLDER" url="/applications/enterpriseApplications.jsf" target="main" border="$int{0}" immediate="$boolean{true}" />
    </facet>
    <!filterTree filterSystemApps() />
</dynamicTreeNode>

First notice that the dynamicTreeNode uses the "treeAdaptorClass" attribute to specify the adaptor instance.  Your instance belongs here.

Next notice "objectName", "methodName", and "attributeName"... these are *NOT* used by the dynamicTreeNode factory.  If you look in MBeanTreeAdaptor... you'll find they are used *there*.  These properties are specific to the MBeanTreeAdaptor (see the init() method).  You will most likely want to have your own properties, you can use whatever you'd like... but you're responsible for retrieving them in your Adaptor class.

Next, look at "text", "url", "target".  These are properties for the parent node (remember, this factory will create a node + dynamic children... these properties define the "node").  However, these too are interpreted by the MBeanTreeAdaptor class.  This means you'll need to do something similar or identical in your code (see MBeanTreeAdaptor.getFactoryOptions).  The reason this is done this was is to give full control to the Adaptor class, even for the parent node.

Next look at the facet "image".  JSFTemplating will automatically set this on the component (the parent of the dynamic children), so nothing is done in the Adaptor class for this (although it can be added via the Adaptor code if you prefer this vs. the .jsf file).

Next look at the "child*" properties.  The MBeanTreeAdaptor class uses these properties when specifying properties for the dynamic children.

Last, look at the "filterTree" event... I'm not going to explain this unless you need it.  It allows you to filter the children, here we're getting rid of system applications that shouldn't be shown to the user.

Ok, so that shows where the information comes from.  Now lets look at how its processed and what you need to do.  First lets look at the factory.  You don't need to do anything in DynamicTreeNodeFactory.  There is some useful documentation at the top of this file, though (or at least I hope its useful).  Key operations this factory invokes:

1) Instantiates your TreeAdaptor implementation
2) Calls yourTreeAdaptor.init() -- your opportunity to do setup tasks (i.e. get your data)
3) Calls yourTreeAdaptor.getTreeNodeObject() -- your implementation can return *anything* that is useful to you
4) Walks your data structure by calling:
    * yourTreeAdaptor.getId(obj)
    * yourTreeAdaptor.getFactoryClass(obj)
    * yourTreeAdaptor.getFactoryOptions(obj)
    - Creates UIComponent based on information from above calls
    * yourTreeAdaptor.getFacets(comp, obj)
    * yourTreeAdaptor.getHandlersByType(comp, obj)
    * yourTreeAdaptor.getChildTreeNodeObjects(obj)
    - Loop throw all childTreeNodeObjects recursing back to the beginning of Step 4.

So as you can see, the TreeAdaptor does all the "work" while the factory walks through the algorithm and extracts the required information and does the actual instantiation and other mundane JSF required tasks.  You can also see that in step 3, *your TreeAdaptor implemenation* returns some object.  This object means *nothing* to the DynamicTreeNodeFactory, but is used in *every* method call to your TreeAdaptor implementation.  This "obj" is anything that helps you retrieve data from your data structure.  This could include objects in your data structure itself, or identifiers to objects in your data structure.  It looks like MBeanTreeAdaptor uses the "objectName" as the value returned from step #3.

Notice the last * under step #4, it returns a List<Object> which are *new* "obj" values that will be passed into each method called in step #4.  So it is important that your TreeAdaptor use the "obj" value to do something useful with it.  It is your only way to identify where you are in your own data structure.

Finally... lets look at the Adaptor class you will need to write.  Looking at the methods above, you'll need to implement all of these... although the TreeAdaptorBase provides an implementation for some of this.  You are not likely to need "getHandlersByType" -- so don't worry about it unless you think you need it.  The rest you'll probably need.  Take a look at the JavaDoc for:

    com.sun.jsftemplating.component.factory.tree.TreeAdaptor;

This JavaDoc should explain what each method does.  Please let me know if you need clarification for any of these methods... I'll fix the JavaDoc.  Also, look at TreeAdaptorBase in the same package to see what it provides for you (very small class... easy read).

I hope this provides you with all the information you need to create an Adaptor that is capable of walking your data structure... let me know if you need more!

Thanks!

Ken Paulsen
ken.paulsen@sun.com



Mike Wright wrote:
Hi Ken,


    I'm trying to get JBI leaf nodes working.    I implemented a JBITreeAdaptor that gets control with a "method" parameter for one of three kinds of children.  However the getChildTreeNodeObjects method is always called with a null nodeObject argument.  If I ignore that and always return a list of children, I get a stack overflow.  I have several questions:


    1. the existing examples I could find use an MBean tree adaptor and a web services tree adaptor, but both depend on AMXUtils and MBeans with ObjectNames, methodNames, attributeNames, etc.  JBI doesn't have anything like this.  I was hoping to just have the tree adaptor return a list of child node names. 1a. how do I get the nodeObject passed in?  which treeNode attribute(s) do I need to worry about?  methodName is getting passed okay.  I tried to remove objectName or set it to "na" but that didn't seem to affect anything (since I don't have any AMXUtil/ObjectName related code in my JBITreeAdaptor)
1b. what is the type of element in the List (built from an Object[])?  Will Strings work?  (the problem here is that, due to the null argument, I never return this List, so I cannot yet tell if Strings will work, or do I need to create some wrapper object, and if so, what type?)

       2. can you give me an overview of the essential flows I need to worry about?   i.e. which methods get called in what order, for the dynamic tree nodes?

Regards,
Mike
-/-