Platinum Partner
netbeans

NetBeans Platform Idioms: Pluggable TopComponent (Part 1)

With part of my NetBeans Platform project mavenized, I have been able to restart coding and refactoring - there are lots of things that I wrote in the past year for blueMarine that, after a clean up, will be the subject of my future posts.

So, I'm resuming my series about the Idioms for the NetBeans Platform, that I suspended just after the first post. Today we see the Pluggable TopComponent. As usual, there's no special invention here, but the use of well-known patterns specifically adapted for the NetBeans Platform context.

The idea, of course, is reuse and modularization. I'll illustrate some real code taken from forceTen that implements the features of the tree component that you can see in this screenshot:


The component with the tree is called "GeoExplorer" and it allows to browse and select nodes representing geographical entities; when you select one, the map renderer (called "GeoViewer") centers on it. The two components are decoupled by means of another Platform Idiom, the "Event Bus": whenever a geo entity is selected in the GeoExplorer, an event is posted to the Event Bus and received by the Geo Viewer.

 

 

The EventBus

 

Since I consider the Event Bus a pretty useful idiom to integrate different components, the design of GeoExplorer makes sense for my projects; but in a wider reuse context, you might want to do something different. There are also a lot of other details that can be done in a slightly different way: for instance, do you want the selection to happen when you single-click on a node? Or when you double-click? Or do you prefer to pop-up a contextual action? Do you need to implement complementary behaviours, such as changing the main window title in function of the selected window, or node, etc? How do you want to load data into a TopComponent, e.g. from a database or a remote service? In eager fashion, that is during initialization, or in lazy fashion, the first time the component is rendered?

Any of these choices make sense in a different context, and a really reusable GeoExplorer should allow a programmer to use it out-of-the-box, only adding integration code and/or configuration changes. The purpose of this article is to find a good design that allows to do that.

Prerequisites

This article is the first in a series - this introduction is easily readable by anyone with the very basic knowledge of the NetBeans Platform; next parts will have complete code examples and I'll assume that you have a good knowledge of some fundamendal things:

 

Introduction

Looking at how I defined my requirements, it's clear that I'm pursuing a modular approach: well, MVC is not the solution. Are you suprised? While MVC is the "father of all patterns" related to applications with a presentation layer and does a good job in decoupling the Model from the View and the Controller, unfortunately it is monolithic: in particular, the Controller is a single class that implements all the behaviours and it's hard to fine-tune.

When I started working with the Pluggable TopComponent idiom, several months ago, I was just thinking of a "MVC on steroids" specially adapted for the NetBean APIs, basically working on a decomposition of the Controller. Then two posts were recently brought to my attention about DCI - Data, Context, Interaction. The former is from past March at Artima and the latter is a screencast by Yarda Tulach and Geertjan Wielenga that describes the relationship between DCI and the NetBeans APIs. So far, I've only been able to watch Yarda's screencast and I am still figuring out what DCI is. I think Pluggable TopComponent is very similar in some parts to "DCI for NetBeans", as it's heavily based on the use of Lookup as an integration infrastructure, but my current limited knowledge of DCI prevents me from saying anything definitive. Still Yarda's post has influenced me and made me understand that indeed I really departed from MVC; thus I completely dropped any MVC reference for the names of the idiom participants.

What do you do when a class is monolithic? The solution is to split it in parts and compose them together. In the Pluggable TopComponent idiom, I call "Behaviours" the atomic components in which the controller has been decomposed. The Lookup in the TopComponent provides a common context by means of which they are able to cooperate.

How many different behaviours shall we define? In order to give an answer to that question, we must concentrate on the roles & responsibilities in a classic MVC.

 

There's a subtle point: if we look at the description of MVC roles & responsibilities, we only find a simplification of reality - it's ok, since patterns are a simplified model focusing on the most important things. In a desktop application, many things hide behind that "Selects view for response" responsibility; for instance, looking back at the examples in my introduction, changing the window title when the TopComponent gets the focus. Generally speaking, we must think of stuff related to the semantics of the UI (e.g. grab / release focus, drag & drop) and their specific implementation (e.g. Swing, NetBeans Platform). For instance, TopComponent has a special feature named "node activation" - quoting the documentation:

Finally, each top component may have associated with it a node selection, which is simply a list of Nodes that it decides to treat as "active". The node selection will have effects on other parts of the system - for example, NodeActions and CookieActions pay attention to it.

The selection may be set using TopComponent.setActivatedNodes(...).

If you don't call setActivatedNodes(), many features of the Platform won't work. 

Last but not least, while the Model can keep the View up to date by means of a regular Observer pattern (which is easily implemented with JavaBeans bound properties and the Nodes APIs), we still need to deal with initialization, that can be done with different strategies such as eager, lazy, etc. And let's ask questions such as: is our data source synchronously or asynchronously connected? Does make sense a "reload" feature? User driven or automated, with a polling cycle?

These extra responsibilities are "details", and mostly a large number of fine-grained details; since "the devil is in details", taking care of them is likely to create troubles to a design with a fat, monolithic Controller.

From this analysis, it turns out that our behaviours can originate from different areas: not only control-oriented, but also UI-oriented, builder-oriented, etc. A characteristic of the Pluggable TopComponent idiom is that, independently from their origin, behaviours share the same structure and the same way of cooperate together.

Behaviour

We can now start looking at some code. First, how we define a Behaviour? Well, it must be a generic class that could do anything. The only requirement that we put is that it can access a Lookup providing the context. In this way, any Behaviour can look up other resources and Behaviours whom to cooperate with. A first attempt could be defining a base abstract class:

package it.tidalwave.netbeans.behaviour;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.openide.util.Lookup;

public abstract class Behaviour implements Lookup.Provider
{
@CheckForNull
private Lookup lookup;

public void setLookup (@Nonnull Lookup lookup)
{
this.lookup = lookup;
}

@Override
@Nonnull
public Lookup getLookup()
{
if (lookup == null)
{
throw new IllegalStateException("Not initialized!");
}

return lookup;
}

public void initialize()
{
}
}

So, basically a Behaviour is an injectable Lookup.Provider. Alternatively, one might rely on a global context, such as Utilities.actionsGlobalContext()) - I think that Yarda uses a global context in its post. I don't like that approach, as I see a severe limitation: since Lookup is queried by class, you can't distinguish between instances of the same class. In other words, what if I need two different GeoExplorers in my application, each one with its specific set of behaviour instances? This couldn't work with a global context, but can be done if the context is provided by the Lookup of each TopComponent.

A simple, concrete behaviour could be:

public class MyBehaviour
{
public void doSomething()
{
getLookup().lookup(AnotherService.class).doSomething2();
}

public void initialize()
{
// some initialization stuff
}
}

But in 2009 do we really want to inherit from a base class? Everybody is using POJOs and so I wish. So I can anticipate that my concrete Behaviours look rather as:

import javax.annotation.Resource;
import javax.annotation.PostConstruct;

public class MyBehaviour
{
@Resource
private AnotherService anotherService;

public void doSomething()
{
anotherService.doSomething2();
}

@PostConstruct
private void initialize()
{
// some initialization stuff
}
}

These are Java 6 annotations with a coherent semantics in Java 6 and Spring. The idea is to have the TopComponent's Lookup to act as a Spring BeanFactory for all the Behaviours.

That's enough for now. In my next post I'll show you some simple code to implement a small Behaviour manager with support for resource injection and initialization.

In the meantime, all the code can be already checked out from ForceTen:

hg clone https://kenai.com/hg/forceten~src
hg update -C dzone-20091012

and the reusable parts will be made available to OpenBlueSky, as soon as that project is mavenized too. In any case, the Maven artifacts will be soon available in a repository and I plan to produce a screencast to show you how easy it is to reuse these components in your own application.

 

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}