NetBeans Platform Idioms: Pluggable TopComponent (Part 2)
Continuing from my previous post, this time we're going to see three simple classes that I'll need in my next post. They can be used to instantiate a set of cooperating classes (that I called "behaviours" in my previous post) both in declarative and in programmatic mode and perform a very simple annotation-based dependency injection.
I'm recalling the last code example from my previous post - this is what a simple behaviour looks like:
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
}
}
BehaviourSet
This class is a collection of Behaviours that can be installed in two ways: declarative and programmatically. The former group is set once and never changed, the latter group is made of objects that can be added and removed at any time. Thus, I call them "static" and "dynamic" behaviours.
The implementation is straightforward - it could be a useful exercise to read the code listing, as it uses mutable Lookup instances, a beast that is less frequently seen than immutable versions:
package it.tidalwave.netbeans.behaviour;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;
import java.util.ArrayList;
import java.util.Collection;
import org.openide.util.Lookup;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;
import org.openide.util.lookup.ProxyLookup;
import it.tidalwave.logger.Logger;
import it.tidalwave.netbeans.behaviour.util.BehaviourInjector;
import it.tidalwave.netbeans.behaviour.util.PostConstructorCaller;
@NotThreadSafe
public class BehaviourSet implements Lookup.Provider
{
private final InstanceContent dynamicBehaviours = new InstanceContent();
private final InstanceContent staticBehaviours = new InstanceContent();
private final AbstractLookup dynamicBehavioursLookup = new AbstractLookup(dynamicBehaviours);
private final AbstractLookup staticBehavioursLookup = new AbstractLookup(staticBehaviours);
private final Lookup lookup = new ProxyLookup(dynamicBehavioursLookup, staticBehavioursLookup);
private boolean setupPerformed = false;
private Lookup injectedLookup;
public void setInjectedLookup (final @Nonnull Lookup injectedLookup)
{
this.injectedLookup = injectedLookup;
}
@Override
@Nonnull
public Lookup getLookup()
{
return lookup;
}
public void addBehaviour (@Nonnull final Object behaviour)
{
//
// It is allowed to call this method at any time, still Behaviours will be all initialized
// in initialize(). Once initialization has been completed, Behaviours are installed as they
// are added.
//
if (setupPerformed)
{
initialize(behaviour);
}
dynamicBehaviours.add(behaviour);
}
public void removeBehaviour (@Nonnull final Object behaviour)
{
dynamicBehaviours.remove(behaviour);
}
public void setStaticBehaviours (final @Nonnull Collection<?> staticBehaviours)
{
for (final Object behaviour : staticBehaviours)
{
this.staticBehaviours.add(behaviour);
}
}
public void initialize()
{
final Collection<Object> behaviourToInitialize = new ArrayList<Object>();
behaviourToInitialize.addAll(staticBehavioursLookup.lookupAll(Object.class));
behaviourToInitialize.addAll(dynamicBehavioursLookup.lookupAll(Object.class));
for (final Object behaviour : behaviourToInitialize)
{
initialize(behaviour);
}
setupPerformed = true;
}
private void initialize (final @Nonnull Object behaviour)
{
BehaviourInjector.injectLookup(behaviour, injectedLookup);
PostConstructorCaller.callPostConstructors(behaviour);
}
}
A mutable Lookup is constructed by wrapping an InstanceContent, so we just have a two pairs InstanceContent + Lookup for static and dynamic behaviours; a ProxyLookup is used to merge the two sets and make them available as a single, publicly available Lookup.
A typical sequence of use could be:
BehaviourSet behaviourSet = new BehaviourSet();
Lookup lookup = new ProxyLookup(behaviourSet.getLookup(),
/* other Lookup instances if needed */);
behaviourSet.setInjectedLookup(lookup);
behaviourSet.setStaticBehaviours(
Lookups.forPath("/Behaviours/foobar").lookupAll(Object.class));
behaviourSet.initialize();
...
behaviourSet.addBehaviour(behaviourA);
...
Lookups.forPath() is responsible for the instantiation of the declarative behaviours, for instance from a layer.xml section such as:
<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
<filesystem>
<folder name="Behaviours">
<folder name="foobar">
<file name="Behaviour1.instance">
<attr name="instanceClass" stringvalue="my.behaviours.Behaviour1"/>
</file>
<file name="Behaviour2.instance">
<attr name="instanceClass" stringvalue="my.behaviours.Behaviour2"/>
</file>
</folder>
</folder>
</filesystem>
If you're thinking of a Spring analogy, you're right. Basically BehaviourSet is working in a similar fashion as a Spring BeanFactory - and thanks to the use of the @Resource annotation we even enjoy a (limited) compatibility of our code.
Indeed when we'll look at real integration examples (in next posts) we'll see that this combo with layer.xml is much better than Spring for many respects.
- Login or register to post comments
- 862 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)









