Geertjan's Blog

  • September 13, 2009

More on @ConvertAsJavaBean

Geertjan Wielenga
Product Manager
Improved version of previous blog entry on the new @ConvertAsJavaBean annotation.

  1. Create a bean with PropertyChangeSupport, annotated with @ConvertAsJavaBean:
    public class CountryBean {
    protected String name;
    protected String food;
    private PropertyChangeSupport propertyChangeSupport;
    public CountryBean() {
    propertyChangeSupport = new PropertyChangeSupport(this);
    public String getFood() {
    return food;
    public void setFood(String newValue) {
    String oldValue = this.food;
    this.food = newValue;
    propertyChangeSupport.firePropertyChange("food", oldValue, newValue);
    public String getName() {
    return name;
    public void setName(String newValue) {
    String oldValue = this.name;
    this.name = newValue;
    propertyChangeSupport.firePropertyChange("name", oldValue, newValue);
    public void addPropertyChangeListener(PropertyChangeListener listener) {
    public void removePropertyChangeListener(PropertyChangeListener listener) {

  2. Create a root Node with an action that creates a new ".settings" file in a "Countries" folder whenever a new bean is created. It also opens the Properties sheet so that the values can be customized immediately. Changes made in the Properties sheet will automatically be persisted to the ".settings" file because of the PropertyChangeSupport on the bean.
    class RootNode extends AbstractNode {
    public RootNode(Children kids) {
    public Action[] getActions(boolean bln) {
    Action[] actions = new Action[]{
    new CreateNewCountryBeanAction()
    return actions;
    public class CreateNewCountryBeanAction extends AbstractAction {
    public CreateNewCountryBeanAction() {
    putValue(Action.NAME, "Create new country");
    public void actionPerformed(ActionEvent arg0) {
    CountryBean bean = new CountryBean();
    try {
    NodeOperation.getDefault().showProperties(new BeanNode(bean));
    InstanceDataObject ido = InstanceDataObject.create(
    null, true
    } catch (Exception ex) {

  3. Then set that root Node as the root context of an ExplorerManager, together with this ChildFactory, which builds child nodes based on CountryBeans found in the "Countries" folder:
    public class CountryChildFactory extends ChildFactory<CountryBean> implements LookupListener {
    Result<CountryBean> res;
    public CountryChildFactory() {
    res = Lookups.forPath("Countries").lookupResult(CountryBean.class);
    protected boolean createKeys(List<CountryBean> list) {
    return true;
    protected Node createNodeForKey(final CountryBean countryBean) {
    BeanNode countryNode = null;
    try {
    countryNode = new BeanNode(countryBean);
    } catch (IntrospectionException ex) {
    return countryNode;
    public void resultChanged(LookupEvent le) {

That's it. Thanks Jaroslav Tulach for this code.

Join the discussion

Comments ( 1 )
  • Jesse Glick Monday, September 14, 2009

    I don't believe you need to (or should) call refresh in the ChildFactory constructor; if and when the node is expanded, you will be asked for keys.

    FindBugs would remind you to return null (or, better, rethrow an AssertionError) when catching IntrospectionException; otherwise you would just immediately get an NPE.

    I believe BeanNode will automatically pick up its name from a getName method.

    putValue(Action.NAME, ...) in an AbstractAction constructor can just be replaced with a call to the super constructor taking String.

Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.