Toni Epple works as a consultant for Eppleton (http://www.eppleton.de) in Munich, Germany. In his spare time he's an active member of the Open Source community as a community leader for JavaTools community (http://community.java.net/javatools/), moderator of the XING NetBeans User Group (http://www.xing.com/group-20148.82db20), founder of the NetBeans User Group Munich (http://tinyurl.com/5b8tuu), member of the NetBeans Dream Team (http://wiki.netbeans.org/NBDTCurrentMembers) and blogger (http://www.eppleton.de/blog). Toni is a DZone MVB and is not an employee of DZone and has posted 51 posts at DZone. You can read more from them at their website. View Full User Profile

How Do NetBeans Extension Points Work?

08.25.2008
| 21881 views |
  • submit to reddit

One of the main benefits of the NetBeans Platform is its module system. Regardless of which module system is best (my guess is there will soon be a version of NetBeans that can also run as OSGi bundles), it’s important to have a system that enables you to create a modular architecture for your application. A module system is an invitation to create a clean and maintainable architecture with defined dependencies and nice APIs with clearly defined and easy-to-use extension points. If you follow these principles, others can extend your application easily. Maybe the easiest way to provide extension points in NetBeans is via the layer.xml file (also known as the "layer file").

In a NetBeans module (also known as a "plugin"), the layer file is its central configuration file. NetBeans IDE uses the layer file to provide extension points for APIs. Objects can be created declaratively in the layer file and you can use the NetBeans Lookup API to listen for changes. You will use this approach whenever you create new Actions (which are invoked via menu items, toolbar buttons, and/or shortcut keys) or TopComponents (which provide "views" or "windows"), for example.

This quick tutorial shows how you can provide your own extension points via the layer file. The source code of the sample is found here in the NetBeans Plugin Portal.

Prerequisites

  1. NetBeans (I’m using 6.1, but this will also work with older/newer versions).
  2. Create a new module suite:
    1. Choose File > New Project (Ctrl-Shift-N). Under Categories, select NetBeans Modules. Under projects, select "NetBeans Platform Application" or ("Module Suite Project" in older versions) and click Next.
    2. In the Name and Location panel, type "layerextensionpoints" in Project Name. Change the Project Location to any directory on your computer, to store the application. Click Finish.
  3. Now create four modules inside the suite, called "extensionpointinterface", "messagereader", "messageprovider1" and "messageprovider2":
    1. Choose File > New Project (Ctrl-Shift-N) again. Under Categories, select NetBeans Modules. Under Projects, select Module and click Next.
    2. In the Name and Location panel, type the name in Project Name (i.e., one of the four names listed at the start of this step). The default in the wizard should be to create the module underneath the directory where you just created the suite, which is fine. Click Next.
    3. In the Basic Module Configuration panel, replace the Code Name Base with de.eppleton.<modulename>. Make sure to let the IDE create a layer file, by filling out the XML Layer field with de/eppleton/<modulename>/layer.xml. Click Finish.

Here is what you should now see:

Create a Service Provider Interface

We will use the module "extensionpointinterface" to define an interface that will be used by the two extension point providers as well as by the "messagereader" module, which uses the two extension points. Inside that module create interface "MessageProviderInterface" with the single method getMessage() that returns a String:

public interface MessageProviderInterface {

public String getMessage();

}

To make this part of the public API right click the project node, select API Versioning and select the check box of de.eppleton.extensionpointinterface. Now this package is accessible to other modules.

Create Service Provider Implementations

Now we need some modules that implement the MessageProviderInterface. We will use the modules "messageprovider1" and "messageprovider2" to do that. To implement the interface they both need a dependency on module "extensionpointinterface". For each of them do the following:

  1. Right click the project node, click Properties and, in the Project Properties dialog, select the "Libraries" category.
  2. Click "Add Dependency".
  3. Select "extensionpointinterface", and click "OK" twice.

Now that we have access to the interface in our two message providers, we can implement it. Again, for both modules, do the following:

  1. Select "New" > "Java Class".
  2. In the Wizard type "MessageProvider1" or "MessageProvider2" in the Class Name field, respectively, and select the main package in each case, for stroring the new class.
  3. Implement the interface. Each of them should provide a different String. In other words, we will provide "Hello " in MessageProvider1 and then we will provide "World!" in MessageProvider2:
import de.eppleton.extensionpointinterface.MessageProviderInterface;
public class MessageProvider1 implements MessageProviderInterface {
public String getMessage() {
return "Hello ";
}
}
import de.eppleton.extensionpointinterface.MessageProviderInterface;
public class MessageProvider2 implements MessageProviderInterface {
public String getMessage() {
return "World!";
}
}
}

In order to make our MessageProviders available as services, add these entries in the layer of the two modules. Between the the layer file's <filesystem> tags, add the following, i.e., in the first module add the first set of tags and add the second set of tags in the second module:

<folder name ="MessageProviders">
<file name="de-eppleton-messageprovider1-MessageProvider1.instance" />
</folder>

and

<folder name ="MessageProviders">
<file name="de-eppleton-messageprovider2-MessageProvider2.instance"/>
</folder>

 

This will create an instance of our MessageProviders using the standard constructor. The trick is that those instances will be accessible from outside the modules, via the NetBeans System FileSystem.

Now that you have created your services, the next step shows how you can access them, without even needing to set a module dependency for them.

Find and Use the Service Providers

We will use the module "messagereader" to display the messages provided by our two MessageProviders. To do so we will create a TopComponent, within the "messagereader" module:

  1. In the Project Properties dialog for "messagereader", set a dependency on "extensionpointinterface", exactly as shown earlier.
  2. Right-click on the "messagereader" project node and choose "New" > "Window Component". Choose "Output", which will determine where the new window will be displayed. Make it show on startup by ticking the checkbox. Click Next.
  3. Enter "MessageReader" for the class name prefix and click "Finish".
  4. The TopComponent class will open in Design View. Drop a JScrollPane from the palette in the window and make it fill the whole area. Add a JTextArea to it, making it fit the whole area too.
  5. In the source view (i.e., click "Source" at the top of the Design view) add this to the end of the constructor:
Lookup lkp = Lookups.forPath("MessageProviders");

for (MessageProviderInterface mi : lkp.lookupAll(MessageProviderInterface.class)) {
jTextArea1.append(mi.getMessage());
}

The code above will lookup the folder you created via the layer file (Lookups.forPath("MessageProviders")), search for classes implementing the interface (lookupAll(MessageProviderInterface.class)) and call the interface method on all instances.

Let’s try it out!

Run the Module Suite. You will see the window either displaying "Hello World!" or "World!Hello ". As we can see in this very simple example, the order in which the ServiceProviders are called can be important for the result. So in the next step I will show you a trick to guarantee the correct order.

Sort the Service Providers

The NetBeans Platform provides a way to sort layer entries. This mechanism is used, for example, to provide the order of actions in menus and toolbars. Since 6.0, this is done via the position attribute in the layer file. So this won’t work in older versions:

In the layer file of the two modules, insert the "position" attributes that you see below, i.e., the first set of tags below belongs to "messageprovider1", while the second belongs to "messageprovider2":

<folder name = "MessageProviders">
<file name="de-eppleton-messageprovider1-MessageProvider1.instance" >
<attr name="position" intvalue="30"/>
</file>
</folder>

and

<folder name = "MessageProviders">
<file name="de-eppleton-messageprovider2-MessageProvider2.instance" >
<attr name="position" intvalue="40"/>
</file>
</folder>

Now the messages will always be displayed in the correct order: "Hello world!". Simply swap these values to reverse the order of the messages. When you do something like this, make sure that you choose your values large enough to add services in between the initial ones, so that there’s room for your application to grow! Now try and see what happens when you set the same value for both attributes!

Summary

The intention of this article is to illustrate how simple it is to implement loose coupling in a NetBeans Platform application. Note that the module that uses the services (i.e., "messagereader") has no dependencies on the modules that provide the services ("messageprovider1" and "messageprovider2"). And not only does the NetBeans Platform provide a very simple mechanism to provide and lookup services, but it also provides an easy way to declaratively order them.

Appendix: Alternative Registration Mechanism, Using "META-INF/services"

In the previous sections, I showed how to register the Service Providers via the layer file. As stated correctly in the comment to this article, by Casper Bang, there's an alternative way to register the service providers. Using the META-INF/services folder is the standard approach that is supported by Java since version 1.3. This additional sections below show how it works.

Register the Services

There are only some small changes needed to change the registration mechanism:

  1. In module messageprovider1 create a folder via the context menu of "Source Packages" > "New" > "Other" > "Folder". Enter "META_INF/services" as Folder Name; the folder will show up like a normal package.

  2. Create a file via the context menu of this new package "META-INF.services" > "New" > "Other" > "Empty file", name the file "de.eppleton.extensionpointinterface.MessageProviderInterface".

  3. Edit the file and enter "de.eppleton.messageprovider1.MessageProvider1"

  4. Copy and paste the "META_INF" folder to the "Source packages" folder of module messageprovider2 and change the content of file "de.eppleton.extensionpointinterface.MessageProviderInterface" to "de.eppleton.messageprovider2.MessageProvider2"

There are different ways to lookup the services. You can either use the default lookup to do so, or you can use the ServiceLoader mechanism introduced in Java 1.6. Let's begin with the method using the default lookup.

Using Lookup to retrieve ServiceProviders 

The global lookup will automatically provide these services for you, so only a little change is needed. In the module "messagereader", edit "MessagereaderTopComponent", and replace this line:

        Lookup lkp = Lookups.forPath("MessageProviders");

with this line:

        Lookup lkp = Lookup.getDefault();

Afterwards, you can build and run the application as before. You may notice that the order of the services has changed. As before you can fix this by adding an additional position attribute when you define the ServiceProvider. Edit both "de.eppleton.extensionpointinterface.MessageProviderInterface" files in modules messageprovider1 and messageprovider2. Add the lines "#position=10" and "#position=20" respectively. Run the application and play with the values to change the order as before.

Using ServiceLoader to Locate the ServiceProviders

If you're using JDK 1.6 or later, you can edit "MessagereaderTopComponent" and replace the code we've added before with the following:

ServiceLoader<MessageProviderInterface> serviceLoader = ServiceLoader.load(MessageProviderInterface.class); 
for (MessageProviderInterface mi : serviceLoader) {
     jTextArea1.append(mi.getMessage());
}

Note that the position attribute is now ignored, since it's not a part of the standard JDK but an extension added by the NetBeans Platform.

 

 

 

AttachmentSize
figure-1.png17.62 KB
Published at DZone with permission of Toni Epple, 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

Casper Bang replied on Mon, 2008/08/25 - 12:21pm

Not everything is done through layer files though. Some things rely on the good old Service Provider, where you register i.e. org.netbeans.api.java.source.JavaSourceTaskFactory in /META-INF/services

 

 

 

Toni Epple replied on Mon, 2008/08/25 - 2:09pm

Sure, there are two ways to make a ServiceProvider available. The /META-INF/services approach is part of the JAR File Specification and a standard approach that can also be used without NetBeans.

Nevertheless there are small differences: With the standard approach the ServiceProvider needs a standard constructor, while the Services Folder can also use a static instanceCreate method ( e.g. a getDefault() ).

The second difference is that you can sort the ServiceProvider instances as shown here. This is not possible with the standard approach. (Well, to be honest in NetBeans it is possible, because there is an extension to the standard approach to let you add a position attribute to the configuration file )

Toni

 

 

 

 

Toni Epple replied on Fri, 2008/09/05 - 1:31am

Geertjan has written a blog entry about the two approaches to provide services with some discussion about the up and downsides of both approaches.

Arnaud Grosjean replied on Tue, 2010/07/13 - 5:43pm

Hello,

I wonder how to call another constructor than the default one with this technique.

 I mean,  in my app, I use the layer to create new nodes in the project logical view. The nodes needs to own a reference to the project, so their constructor looks like MyNode(MyProject project)

How to call the right constructor when I build my logical view?

I am on a wrong way, should I use another mechanism?

 

thx

 

Bryan Low replied on Fri, 2013/02/01 - 10:33pm

Many other places of interest including prestigious schools such as Singapore American School as well as recreation venues such as Woodlands Waterfront and Woodlands stadium are within the vicinity of Twin Fountains EC.

Twin Fountains Location

Nathan Dias replied on Mon, 2013/03/18 - 11:34pm

Not everything is done through layer files though. Some things rely on the good old Service Provider, where you register i.e. 

pedreiro 

Matt Coleman replied on Mon, 2013/04/01 - 3:58am

its great that its compatible with older NB versions

buffalo freelance web designer 

Morkel Abie replied on Wed, 2013/04/17 - 3:25am

A module system is an invitation to create a clean and maintainable architecture with defined dependencies and nice APIs with clearly defined and easy-to-use extension points. If you follow these principles, others can extend your application easily. Directory of Best Sites

Bryan Low replied on Tue, 2013/04/23 - 2:37am

A wonderful and unique lifestyle awaits you. Please see Ecopolitan EC project details and floor plans for more information.Punggol EC

Bryan Low replied on Tue, 2013/06/04 - 12:58am

Designed by renowned architect Ole Scheeren, the project is connected to Bugis MRT Interchange that will link the East-West Line and the upcoming Downtown Line. Duo Residences has full and unique facilities, which includes a guard house, clubhouse, children's playground, swimming pool, piano room, pool room, indoor gym, hydrotherapy beds, hydrotherapy baths, reading room, function room, onsen, jacuzzi. DUO

John Brown replied on Wed, 2013/07/24 - 12:50am

 Very basic guides for newbie like me. Now I can use this extension very good.

Oud oil

Comment viewing options

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