4. The Nodes API (II)

You can learn many things from children. How much patience you have, for instance.

Franklin P. Jones

In this section we'll quickly learn how to create nodes with children asynchronously. I beg you to have patience.

4.1. Creating Nodes with Children, asynchronously

In the NetBeans Platform you can create Nodes with Children in different ways. Luckily none of them requires sex.

Probably the easiest way to create a Node with Children that are fetched asynchronously is by means of a ChildFactory. The NetBeans Platform manages all threading for you, so you don't have to worry about starting background threads, or about SwingWorkers, or about threading issues with the AWT.

Figure 3. Nodes, ChildFactories and the Model

Nodes, ChildFactories and the Model

(Click to enlarge)


In our example application each Query entity has a list of Tweets, and each Tweet has a TweetAuthor. We also have a QueryNode (a visual representation of a Query) and a TweetNode (that visually represents Tweets and TweetAuthors). What we'll be doing is to make a list of TweetNodes the children nodes of a QueryNode. We'll need a QueryNodeChildFactory object, because we'll be retrieving the list of Tweets asynchronously from the Query entity. Let's start building it little by little. The very first you need is to extend the ChildFactory class, like so:

class QueryNodeChildFactory
  extends ChildFactory<Tweet> 1
{
  private Query query;

  QueryNodeChildFactory( Query query ) 2
  {
    this.query = query;
  }
}

1

We extend a ChildFactory, which is a generic Java class. We specify the type of the entities that will be used to construct the nodes. In our case we will be building TweetNodes from Tweets, so we have to specify "Tweet" (the entity type) here.

2

Since we we'll need a Query entity to retrieve a list of Tweets, we pass the Query entity in the constructor, and keep a private variable so that we can use it later on.

4.1.1. Actually retrieving the list of Tweets from the Query

The next step is, of course, to retrieve the list of Tweet entities from a Query entity. We do this by implementing the "createKeys" method from the ChildFactory class, like so:

  /**
    * Populate "list" with the Tweet entities corresponding
    * to the Query entity.
    * Note: this method is invoked automatically in a worker thread.
    * @param list the List of Tweet to populate.
    * @return true if we're all set, false to be called again later
    *   to fetch even more results.
    */
 @Override
  protected boolean createKeys(List<Tweet> list) {
    
    // Retrieve the "Reloadable" ability from the query object
    // (now, abilities are cool, aren't they?)
    Reloadable r = query.getLookup().lookup( Reloadable.class ); 1

    if ( r != null )
    {
      try
      {
        r.reload(); 2
      }
      catch( Exception e )
      {
        // Empty
      }
    }

    // Add all the tweets in the query to the list
    list.addAll( query.getTweets() ); 3
    // Indicate that we're done
    return true; 4
  }
  

1

Remember that our Query object has the Reloadable ability? We could use that to retrieve the list of feeds in the Query: just retrieve the "Reloadable" ability here...

2

... and invoke the "reload()" method here. Of course you could use the TwitterSearchDAO here, and invoke appropriate methods to retrieve a collection of Tweet from the Query. If you were building a database application you could use some SQL, or JDBC or JPA here to retrieve some entities. I'm using the Reloadable ability here because it's cool (and illustrative), isn't it?.

3

The "createKeys" method that we're overriding here is passed a list as an argument. The objective of this method is to populate this list with all the entities we want to have. In our case we simply "addAll" the Tweets in the Query to this list.

4

Finally, since we're all set, we just return "true". If you returned "false" here then the NetBeans Platform would keep on calling the "createKeys" method again and again, until true is returned. This is handy if you have a huge number of entities (say one thousand) and you want to retrieve the entities little by little. We could, for instance, retrieve just 100 Tweets here and return false, and keep on adding Tweets until we reach one thousand, in which case we should be returning true. In-between each call to the "createKeys" method, the NetBeans Platform adds nodes to the list of children and repaints them in the UI, that improves the "perceived performance" of your application, allowing the user to "see" the first results of your query quickly, instead of waiting for one thousand Tweets to be retrieved from twitter.

4.1.2. A child at a time

So we have populated a list of Tweets entities from the Query entity. What now? Of course, we need to create visual representation for those, i.e., actually creating the list of children nodes from the list of entities. How do we do this? By overriding one more method of the ChildFactory class, of course, like so:

  @Override
  protected Node createNodeForKey(Tweet key) {
    return new TweetNode(key);
  }

Simple: for each Tweet entity that we added in the list in the "createKeys" method, the NetBeans Platform will invoke the "createNodeForKey" method and pass it the Tweet entity. All we have to do is to create a TweetNode from the Tweet entity, by using the TweetNode constructor that we built in the previous section. Oh, well, it isn't that complex, is it?

4.1.3. Associating the QueryNode and the QueryNodeChildFactory

The final step is to associate the QueryNode with the QueryNodeChildFactory. We do this in the QueryNode constructor, passing it the QueryNodeChildFactory instead of the Children.LEAF constant we used previously for leaf nodes. The code looks like this.

**
 * QueryNode is a node that represents a query. The children of this
 *   node are the results of the query.
 * @author Antonio Vieiro (antonio@antonioshome.net)
 */
public final class QueryNode
  extends AbstractNode {

  private Query query;
  private InstanceContent instanceContent;

  /**
   * Public constructor from an entity.
   * @param query The entity that this node represents visually.
   */
  public QueryNode(Query query) { 1
    this(query, new InstanceContent());
  }

  /**
   * Private constructor from an entity and an InstanceContent
   * @param query The entity that this node represents visually.
   * @param ic The InstanceContent object that keeps this node's abilities.
   */
  private QueryNode(Query query, InstanceContent ic) {
    // Invoke the super constructor, passing it a list of children 
    // to be retrieved with a QueryNodeChildFactory, and
    // a ProxyLookup that combines this node's abilities with the entity's
    super(Children.create(new QueryNodeChildFactory(query), true), 2
      new ProxyLookup( // Combination of lookups 
        query.getLookup(), // The entitie's abilities
        new AbstractLookup(ic))); // This node's abilities
    // Keep the entity and the instancecontent on member variables
    this.query = query;
    this.instanceContent = ic;
    // Add a new ability for this node to be reloaded
    this.instanceContent.add(new ReloadableNode()  { 3
      public void reloadChildren() throws Exception {
        // To reload this node just set a new set of children
        // using a QueryNodeChildFactory object, that retrieves
        // children asynchronously
        setChildren(
          Children.create(
            new QueryNodeChildFactory(QueryNode.this.query), 
            true));
      }
    });
  }

1

Since we want to mix the abilities of our entity and our node we use the same idiom as for the leaf nodes: we invoke a private constructor with an InstanceContent that will hold the abilities of the node.

2

Here comes the important part, instead of passing the super constructor a "Children.LEAF" constant, we're invoking the Children.create method, passing it out QueryNodeChildFactory and a boolean.

The important thing is the boolean. If you pass a "true" value then the children nodes will be created asynchronously on a worker thread: the NetBeans Platform will start that worker thread for us, so we don't have to worry at all about threading issues. All we have to do is to populate the list in the createKeys method!

3

Finally we're adding an ability to our node to be reloaded. We have defined a ReloadableNode ability for our QueryNode, and what we do here is to reload the node. Note that reloading nodes is not the same thing as reloading entities, so we use the ReloadableNode for refreshing nodes and the Reloadable ability for refreshing entities (one calls the other, of course, through our ChildFactory implementation). This "reload ability" for node is easy to implement: we're invoking the "setChildren" method of the AbstractNode with a new ChildFactory, as we did in the constructor.

4.2. Summary

In this section we've learned how to create Nodes with Children, without sex, without threads. The ChildFactory is a great way to build a list of children asynchronously, isn't it?.

We've also learned how to fetch a list of entities from another one (using the Reload ability) and we've seen how to add a reload ability to our Nodes (the ReloadableNode ability) that uses the "setChildren" method to regenerate the children of the node.

Time now to have a rest, download the source code from Kenai.com and take a look at it. Meanwhile I'll be preparing a new section of the tutorial: creating nodes synchronously.


blog comments powered by Disqus