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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

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
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

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Is your software supply chain secure? Calling all security savants to share your experiences, tips, and insights with our dev community!

Data quality isn't just a technical issue: It impacts an organization's compliance, operational efficiency, and customer satisfaction.

Related

  • Configuring Anypoint Platform as an Azure AD Service Provider SSO
  • Automatic 1111: Adding Custom APIs
  • Cypress.io — The Rising Future of Web Automation Testing
  • What D'Hack Is DPoP?

Trending

  • How Clojure Shapes Teams and Products
  • How AI Is Changing the Way Developers Write Code
  • Mastering Fluent Bit: Installing and Configuring Fluent Bit Using Container Images (Part 2)
  • APIs for Logistics Orchestration: Designing for Compliance, Exceptions, and Edge Cases
  1. DZone
  2. Coding
  3. Tools
  4. How Do NetBeans Extension Points Work?

How Do NetBeans Extension Points Work?

By 
Toni Epple user avatar
Toni Epple
·
Aug. 25, 08 · News
Likes (0)
Comment
Save
Tweet
Share
27.8K Views

Join the DZone community and get the full member experience.

Join For Free

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.

 

 

 

NetBeans application Interface (computing) Service provider Context menu Attribute (computing)

Opinions expressed by DZone contributors are their own.

Related

  • Configuring Anypoint Platform as an Azure AD Service Provider SSO
  • Automatic 1111: Adding Custom APIs
  • Cypress.io — The Rising Future of Web Automation Testing
  • What D'Hack Is DPoP?

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!