Fabrizio Giudici is a Senior Java Architect with a long Java experience in the industrial field. He runs Tidalwave, his own consultancy company, and has contributed to Java success stories in a number of fields, including Formula One. Fabrizio often appears as a speaker at international Java conferences such as JavaOne and Devoxx and is member of JUG Milano and the NetBeans Dream Team. Fabrizio is a DZone MVB and is not an employee of DZone and has posted 67 posts at DZone. You can read more from them at their website. View Full User Profile

NetBeans Platform Idioms: Pluggable TopComponent (Part 3)

11.25.2009
| 6276 views |
  • submit to reddit

In my previous post of this series, I recalled with a quick example about how it is possible to instantiate objects by means of the layer.xml facility in the NetBeans Platform. The example code was at the bottom of the first page of the previous post:

<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
<filesystem>
<folder name="Roles">
<folder name="foobar">
<file name="Role1.instance">
<attr name="instanceClass" stringvalue="my.roles.Role1"/>
</file>
<file name="Role2.instance">
<attr name="instanceClass" stringvalue="my.roles.Role2"/>
</file>
</folder>
</folder>
</filesystem>
Incidentally, you should note that in the meantime I've updated my naming convention, using now the term “role” in place of “behaviour”.


What if you don't want to just instantiate a bean, but also need to set some properties? The Platform allows a variation of the layer.xml syntax as follows:

<file name="TitleActivator.instance">
<attr name="instanceCreate" methodvalue="it.tidalwave.netbeans.windows.role.TitleActivator.createInstance"/>
<attr name="pattern" stringvalue="[%s]"/>
</file>

Here you see that I'm using a static factory method that is available in the TitleActivator class; this method can initialize a “pattern” property. Unfortunately, this approach needs some cooperation from the instantiated class, as you need to code yourself the static factory method:

public class TitleActivator
{
protected String pattern = "%s";

@Nonnull
public static TitleActivator createInstance (@Nonnull final FileObject fileObject)
{
final TitleActivator instance = new TitleActivator();
instance.setPattern((String)fileObject.getAttribute("pattern"));
return instance;
}

...
}

Also, you need to write boilerplate code for each supported property and you're introducing in your instantiated class a dependency to the FileSystems API (by means of FileObject). You can't instantiate a plain Java Bean, such as a Swing component - unless you provide a specific static factory method in a utility class, including the initialization code for each property.

This is a annoying as requires too much boilerplate code, so I wrote a universal bean factory for layer.xml, that works with any regular Java Bean. For instance, you can write:

<file name="Bean.instance">
<attr name="instanceCreate" methodvalue="it.tidalwave.netbeans.role.util.BeanFactory.createInstance"/>
<attr name="class" stringvalue="com.acme.AcmeBean"/>
<attr name="property1" stringvalue="value1"/>
<attr name="property2" intvalue="2"/>
<attr name="property3" boolvalue="false"/>
</file>

Properties are accessed by introspection (for my convenience I've used BeanProperty from (Better)BeansBinding). The code of BeanFactory is listed below.

See you next time for the fourth part of this series.


package it.tidalwave.netbeans.role.util;

import javax.annotation.Nonnull;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.jdesktop.beansbinding.BeanProperty;
import org.jdesktop.beansbinding.Property;
import org.openide.filesystems.FileObject;
import org.openide.util.Lookup;
import org.openide.util.Parameters;

public final class BeanFactory
{
private BeanFactory()
{
}

@Nonnull
public static Object createInstance (@Nonnull final FileObject fileObject)
throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
Parameters.notNull("fileObject", fileObject);

final String className = (String)fileObject.getAttribute("class");

if (className == null)
{
throw new IllegalArgumentException("No required fileobject attribute: class");
}

final ClassLoader globalClassLoader = Lookup.getDefault().lookup(ClassLoader.class);
final Class<?> clazz = globalClassLoader.loadClass(className);
final Object instance = clazz.newInstance();

final List<String> propertyNames = Collections.list(fileObject.getAttributes());
propertyNames.removeAll(Arrays.asList("class", "instanceCreate"));

for (final String propertyName : propertyNames)
{
final Object propertyValue = fileObject.getAttribute(propertyName);
final Property property = BeanProperty.create(propertyName);
property.setValue(instance, propertyValue);
}

return instance;
}
}
Published at DZone with permission of Fabrizio Giudici, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Fabrizio Giudici replied on Fri, 2009/11/27 - 2:39am

Tim Boudreau pointed out that Swing components must be created in the EDT thread. Since we're talking of a Pluggable TopComponent, you'll discover that the Lookup.forPath() method is created from the EDT thread. But in order to make BeanFactory as much reusable as possible, I've patched it so line 34 has been replaced by the invocation of this method:

protected static Object instantiate (final Class<?> clazz)
{
if (EventQueue.isDispatchThread() || !Component.class.isAssignableFrom(clazz))
{
try
{
return clazz.newInstance();
}
catch (InstantiationException e)
{
return e;
}
catch (IllegalAccessException e)
{
return e;
}
}
else
{
try
{
final Object[] result = new Object[1];

EventQueue.invokeAndWait(new Runnable()
{
@Override
public void run()
{
try
{
result[0] = clazz.newInstance();
}
catch (InstantiationException e)
{
result[0] = e;
}
catch (IllegalAccessException e)
{
result[0] = e;
}
}
});

return result[0];
}
catch (Exception e)
{
return e;
}
}
}

If an instance of Exception is returned, it is thrown.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.