DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Tools
  4. NetBeans Platform Idioms: Pluggable TopComponent (Part 4)

NetBeans Platform Idioms: Pluggable TopComponent (Part 4)

Fabrizio Giudici user avatar by
Fabrizio Giudici
·
Jan. 01, 10 · Interview
Like (0)
Save
Tweet
Share
4.75K Views

Join the DZone community and get the full member experience.

Join For Free

Let's go on with the analysis of the Pluggable TopComponent idiom. Today we're going to look at the main class, EnhancedTopComponent, that acts as the connection point between the idiom participants and the NetBeans Platform runtime, as well as a few more reusable roles.

This is the fourth part of this series. I'll make many references to portions of the API described in the previous  parts, that you should read before going on: 1, 2, 3. I'm also happy to say that the related code has been moved to the project at Kenai, and most of it is ready for inclusion in form of Maven artifacts.

The core of EnhancedTopComponent is very simple: each instance contains an instance of RoleSet (that has been described in previous articles) and that gets populated from the XML FileSystem (layer.xml) - for instance with a XML fragment such as:

<filesystem>
    <folder name="Roles">
        <folder name="it.tidalwave.geo.explorer.GeoExplorerPresentation">
            <file name="DataLoader.instance">
                <attr name="instanceClass" stringvalue="it.tidalwave.geo.explorer.impl.role.DefaultGeoCoderDataLoader"/>
            </file>
            <file name="SelectionStrategy.instance">
                <attr name="instanceClass" stringvalue="it.tidalwave.geo.explorer.impl.role.AutoSelectionStrategy"/>
            </file>
            ...
            <file name="NodeView.instance">
                <attr name="instanceCreate" methodvalue="it.tidalwave.netbeans.role.util.BeanFactory.createInstance"/>
                <attr name="class" stringvalue="it.tidalwave.netbeans.explorer.view.EnhancedBeanTreeView"/>
                <attr name="rootVisible" boolvalue="false"/>
                <attr name="dropTarget" boolvalue="false"/>
                <attr name="dragSource" boolvalue="false"/>
            </file>
        </folder>
    </folder>
    ...
<filesystem>

It's an excerpt of the real configuration of the forceTen project, and as you can see the roles can be instantiated in multiple ways (in the most sophisticated case some property values are initialized thanks to the BeanFactory utility that has been described in the previous post). I'm using a naming convention where the roles are instantiated from the path "/Roles/<topComponentId>".

package it.tidalwave.netbeans.windows;

public abstract class EnhancedTopComponent extends TopComponent
  {
    @Nonnull
    private final Lookup lookup;

    private final RoleSet roleSet = new RoleSet();

    @CheckForNull
    private final String id;

    public EnhancedTopComponent()
      {
        this(null);
      }

    public EnhancedTopComponent (@CheckForNull final String id)
      {
        this.id = id;
        lookup = new ProxyLookup(roleSet.getLookup(),
                                 Lookups.fixed(this), // needed by roles
                                 super.getLookup());
        setLayout(new BorderLayout());
      }

    public void addRole (@Nonnull final Object role)
      {
        roleSet.addRole(role);
      }

    public void removerole (@Nonnull final Object role)
      {
        roleSet.removeRole(role);
      }
      
    private void installRoles()
      {
        final String path = "/Roles/" + ((id != null) ? id : preferredID());
        logger.finer(">>>> looking up default roles from path %s...", path);

        roleSet.setInjectedLookup(getLookup()); // a subclass might have enhanced it
        roleSet.setStaticRoles(Lookups.forPath(path).lookupAll(Object.class));
        roleSet.initialize();

        final GUIBuilder guiBuilder = roleSet.getLookup().lookup(GUIBuilder.class);

        if (guiBuilder != null)
          {
            add(guiBuilder.createGUI(), BorderLayout.CENTER);
          }
      }
  }   

The initialization of roles is performed by installRoles(). An interesting point is that it manages in a special way a role, named GUIBuilder:

public interface GUIBuilder 
  {
    @Nonnull
    public JComponent createGUI();
  }

     
It's a simple factory to which the creation of the Swing user interface is delegated. In this way, any Swing responsibility is moved away from EnhancedTopComponent, that becomes a mere controller. This also means that any specific TopComponent in a desktop application can just inherit from EnhancedTopComponent and specify the UI in a separate class; it also means that an existing UI can be replaced by an alternate one, by just overriding the configuration of a GUIFactory.

As I wrote in the preamble of this article, EnhancedTopComponents acts as the bridge between the Platform runtime and the roles. On this purpose, all the life-cycle methods have been implemented in the following way:

interface RoleRunner
  {
    public void run (final @Nonnull TopComponentRole role);
  }

@Override
protected void componentActivated()
  {
    super.componentActivated();
    
    runRoles(new RoleRunner()
      {
        @Override
        public void run (final @Nonnull TopComponentRole role)
          {
            role.notifyActivated();
          }
      });
  }

private void runRoles (final @Nonnull RoleRunner roleRunner)
  {
    if (!rolesInstalled)
      {
        installRoles();
        RoleInjector.injectLookup(this, getLookup());
        PostConstructorCaller.callPostConstructors(this);
        rolesInstalled = true;
      }
    
    assert rolesInstalled : "roles not initialized";
    
    for (final TopComponentRole role : getLookup().lookupAll(TopComponentRole.class))
      {
        roleRunner.run(role);
      }
  }

Each method makes sure that the roles have been loaded and initialized, and then calls a method with the same name on all the registered roles that implement TopComponentRole:

public interface TopComponentRole
  {
    public void notifyActivated();

    public void notifyDeactivated();

    public void notifyOpened();

    public void notifyClosed();

    public void notifyShowing();

    public void notifyHidden();
  }

In this way, multiple behaviours can be plugged from different modules and take part in the life cycle of the TopComponent. A companion abstract class, TopComponentRoleSupport, implements all the methods with empty body, so one can subclass it and only implement the ones he needs.

A typical operation that must be bound to the TopComponent life-cycle is loading the data that are going to be rendered. On this purpose, we can define an interface and a few classes:

public interface DataLoader
  {
    public void loadData();
  }

public abstract class TopComponentDataLoaderStrategy extends TopComponentRoleSupport implements DataLoader
  {
    @Inject
    private DataLoader dataLoader;

    @Override
    public final void loadData()
      {
        dataLoader.loadData();
      }
  }
  
@NotThreadSafe
public final class EagerDataLoaderStrategy extends TopComponentDataLoaderStrategy
  {
    private boolean initialized;

    @PostConstruct
    private void initialize()
      {
        if (!initialized)
          {
            loadData();
            initialized = true;
          }
      }
  }
  
@NotThreadSafe
public final class LazyDataLoaderStrategy extends TopComponentDataLoaderStrategy
  {
    private boolean initialized;

    @Override
    public void notifyShowing()
      {
        if (!initialized)
          {
            loadData();
            initialized = true;
          }
      }
  }

The concrete code for loading the data must be provided in an implementation of DataLoader; then, either EagerDataLoaderStrategy or LazyDataLoaderStrategy must be configured in layer.xml. The following layer.xml fragment is taken again from the forceTen project:

<filesystem>
    <folder name="Roles">
        <folder name="it.tidalwave.geo.explorer.GeoExplorerPresentation">
            <file name="DataLoader.instance">
                <attr name="instanceClass" stringvalue="it.tidalwave.geo.explorer.impl.role.DefaultGeoCoderDataLoader"/>
            </file>
            <file name="DataLoaderStrategy.instance">
                <attr name="instanceClass" stringvalue="it.tidalwave.netbeans.windows.role.LazyDataLoaderStrategy"/>
            </file>
        </folder>
    </folder>
    ...
<filesystem>

Splitting the application behaviour in these fine grained roles makes it possible to achieve a great flexibility. For instance, a customization module can change the policy from lazy to eager and a different data provider than the default one can be specified.

NetBeans

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • 10 Most Popular Frameworks for Building RESTful APIs
  • Java REST API Frameworks
  • 4 Best dApp Frameworks for First-Time Ethereum Developers
  • Best Practices for Setting up Monitoring Operations for Your AI Team

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: