From Pain to Gain: Swing and the NetBeans Platform in the Real World
The Lookup API
The NetBeans Platform components have a NetBeans Platform-controlled life-cycle (much like EJBs in a container), so they are not directly instantiated. In order to retrieve a reference to an existing module, you use the Lookup API. This API is very similar to other lookup mechanisms. You get a reference to an object starting from its "name", which is not a string but the corresponding Class object.
For instance, let’s suppose we have a module called it.tidalwave.catalog.CatalogImpl (implementing an interface it.tidalwave.catalog.Catalog). First you "register" the module by putting a special file in the classpath, under the META-INF/services directory. The file must be named after the implemented interface and contain the name of the implementation class. Whenever a module is loaded, the NetBeans Platform scans for these special files, instantiates the objects and puts them into the "default" Lookup object, from where any other piece of code can later retrieve it.
I usually wrap the lookup code using the Locator pattern, as shown in Listing 1, and then perform lookups like this:
Catalog catalog = CatalogLocator.findCatalog();
Listing 1. A Locator that uses the Lookup class. |
public class CatalogLocator {
|
This mechanism not only favors decoupling, but it also creates pluggable behavior. For instance, let's look at the map-rendering capability of blueMarine. As you might know, there are many map providers around, such as Google Maps, Microsoft Visual Earth, NASA, and others. I want people to be able to extend blueMarine by plugging new code into it for handling additional map providers. The solution is simple: first define an interface – MapProvider – which declares all the required capabilities, then write alternate implementations, each one in its own module, e.g., GoogleMapProvider, MicrosoftVisualEarthMapProvider, etc.
Each implementation is registered in the default Lookup instance, using the same "name": MapProvider (multiple registered objects for the same name are allowed). Now, retrieving the objects becomes an easy task. An example is shown in Listing 2. You can add modules with new map providers, and the retrieval code will find them at runtime.
Listing 2. Retrieving registered objects. |
private DefaultComboBoxModel mapProvidersModel = new DefaultComboBoxModel(); |
The Lookup API also promotes decoupling
The default Lookup instance also contains the current set of selected Node objects. This makes it possible to design an effective and loosely-coupled mechanism also for inter-module communication. It’s based on the Observer pattern: some modules publish their node selection to the default Lookup, while others listen for changes. And by implementing some filtering related to the kind of information associated to the changed nodes we get to the Publish/Subscribe design pattern.
For example, in blueMarine there are many ways to navigate the photo database and show a set of thumbnails – by exploring folders, the calendar, photos that share a tag, photos in the same gallery, and so on. The "explorer" modules just publish a selection of Nodes bound to PhotoDataObjects to the default Lookup; a Thumbnail Viewer receives notifications and updates itself appropriately (see Figure 5).
![]() |
Figure 5. The role of Node objects for inter-module communication. |
The explorer components do not depend on the Thumbnail Viewer. Actually, they are completely decoupled from it (we're applying Inversion of Control here). With this design I can add as many explorers as I want, even in independent modules that can be installed as add-ons. I can also easily add new viewers. For instance, I was able to include a Filmstrip Viewer as a completely decoupled component that can be used by itself or together with the original Thumbnail Viewer (see Figures 6 and 7).
![]() |
Figure 6. The Thumbnail Viewer and the Film Strip Viewer must show the same nodes – with the same selection too. |
![]() |
Figure 7. Multiple, synchronized views are implemented by just listening to the same Node’s changes. |
The Lookup API has many other uses, as many types of
objects (including Nodes themselves) have their own local Lookup instance. I’ve only shown the "tip of the iceberg" here.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)









Comments
Bryan Low replied on Sun, 2013/02/24 - 4:55am
A wonderful and unique lifestyle awaits you. Please see Bartley Ridge project details and units available for more information.
Bartley Ridge Project Details