Apache NetBeans
Apache NetBeans
Latest release

Apache NetBeans 23

Download

When do I use which registration method?

As described in DevFaqModulesGeneral, there are several different declarative registration mechanisms:

If you are implementing some API from another module, that module should tell you what to do. If it tells you something should be in the default lookup, that means to use @ServiceProvider (see caveats in DevFaqWaysToRegisterInDefaultLookup).

Deciding On A Registration Mechanism For Clients Of Your Module

If you are defining an SPI in your module, and other modules will implement it and provide their own classes, provide a declarative (plain text, no code) way to register them.

Define interfaces or abstract classes, and document where subclasses can be registered (typically the default Lookup or some folder in the system filesystem).

Starting with NetBeans 6.7, you can provide annotations which other modules can use to register their objects - so registration is declarative, but it is visible in the Java source file.

If you can possibly avoid it, don’t require your module (or modules that implement your SPI) to run code on startup to programmatically register their functionality. That slows startup time and does not scale.

Below are typical registration mechanisms and patterns, and when each is useful:

What When to Use It How Examples

Define a singleton service class (there should be only one) that should be global to the application

You are defining a service, but another module will provide an implementation of that service

  • Define your service-provider class. Typically in NetBeans it will have a static method getDefault() which tries to find an instance of itself in the default Lookup, and if that fails, returns some sort of non-null mock implementation (which may not really do anything, but is useful in unit test that call code which calls your module)

  • Document that it should be registered in the default Lookup and that it is expected to be a singleton.

  • Define and document a unique string token which modules can "provide" if they provide an implementation of your API - for example com.mymodule.MyService (it can be any string)

  • Modify your module’s manifest.mf file to use that token as follows:

    • If you provide no implementation of your service, but one is needed at runtime for proper functioning add the line OpenIDE-Module-Requires: com.mymodule.MyService to the manifest. If no module is present which provides this token, your module will not be loaded on startup - the user will be offered an option of exiting or disabling your module.

    • If you do provide some mock implementation of your service which is available in the case no other module is providing one then add the line OpenIDE-Module-Recommends: com.mymodule.MyService to the manifest. Your module will be loaded, no matter what. If no other module provides this token, a warning will be logged.

  • Document that modules which implement your service should include OpenIDE-Module-Provides: com.mymodule.MyService in their manifest(s).

  • The UI Utilities API defines StatusDisplayer. You can call StatusDisplayer.getDefault().setStatusText("Hello world") to change the text in the status bar of the main window. But the UI Utilities API does not provide the subclass of StatusDisplayer which is being called. In fact, the module core.windows, which is responsible for creating NetBeans' main window injects its own subclass into the default lookup, and that is what actually changes the status bar you see on the screen. It is that subclass which you are actually calling when you set the main window’s status text. But your module only depends on the API, not the windowing system. Your code doesn’t have to care whose subclass of StatusDisplayer it is calling. If a new version is created that displays status, say, in a translucent fading popup window, your code will work perfectly with that as well, without any changes or recompiling.

  • The IO API provides a way to write to the output window. In fact, there are two different output window implementations available for NetBeans - the default one, and a terminal emulator. The I/O API does not care which one is present, but it recommends that one should be, and provides a mock implementation that writes to System.out if none is present.

Define an interface or abstract class and look for multiple instances of it in the default Lookup and do something with those objects

The objects modules will register is are implementations/subclasses of a simple interface or class. Your module only needs to find all such registered objects and use them. Your module will need all of them at the same time.

  • Define an interface or class for others to implement.

  • Document that there can be multiple ones registered and that they should be registered in the default Lookup.

  • In your module, use Lookup.getDefault().lookup(MyClass.class).allInstances() to find all registered instances.

StatusLineElementProvider allows modules to contribute components to the status bar in the main window. All components are needed in order to show the status bar.

Define an interface or abstract class, and document your strategy for locating these objects in folders in the system filesystem

The objects modules will register is are implementations/subclasses of a simple interface or class, but not all objects are needed at any given time. At any time, some may be needed, based on what the user is doing (for example, the MIME type of the file the user is editing - MIME types map easily to folder paths, e.g. Editors/text/x-java/).

If you want to add an action to the popup menu that appears when you right-click in the text editor for a .java file, but not other kinds of files, you register an instance of javax.swing.Action in the system filesystem (via your module’s layer file) folder Editors/text/x-java/Actions. If the user never actually opens a Java file and right-clicks the editor, your Action will never be created, nor its class loaded.

Define a single folder in the system filesystem where objects should be registered, and optionally a factory method which will create the object.

  1. Other modules are not really registering their own subclasses, they are registering files. You want to read the files and create the objects in your code.

  2. Other modules are registering objects; however, there is useful metadata that can be used without ever creating the object.

  3. Other modules are registering objects. Creating those objects requires additional metadata which can be specified declaratively using file attributes

  • Define a static, public factory method which takes a Map.

  • Document that all registered files should list this factory method as their instanceCreate attribute (e.g. <attr name="instanceCreate" methodvalue="com.XClass.factoryMethod" />.

  • Find registered objects using Lookups.forPath("path/to/my/folder")).

Examples for the cases defined under When To Use It: . The simple.project.templates module defines a spec for using .properties files to list everything that should be created when the user wants a new project. It does not need a special file type or object instances - it will read the file and make the object it needs. . The Services tab in the IDE allows objects to be registered, which are shown as nodes in its UI. The icon and localized display name of these nodes can be declaratively specified as file attributes, so no classes need to be loaded until the first time the user selects one of these nodes. . As mentioned in 1., simple.project.templates defines a spec for describing a project template inside a regular .properties file. The javacard.project module reads defines several template files. But ''it also needs to know what "flavor" of project (applet, web, library, etc.) each file defines, so that it will ask the user the right questions in the New Project Wizard. It defines an additional file attribute to indicate what "flavor" of project a template represents.

Why Declarative Registration and Lazy Loading Is Important

For best performance and scalability, avoid actually instantiating the objects other modules register until the first time your code needs to call them. Avoid programmatic registration mechanisms, and delay instantiating declaratively registered objects until they really need to be used. This is for several reasons:

  • Object take up memory. Your application will use less memory and be faster if you do not create objects that you do not know you will call.

  • Java class loading happens the first time a class is needed, and loading one class can trigger loading many others. It means file I/O happens, blocking whatever thread first needs to load the class.

  • If you create objects only when your code really is going to call them, class loading and object creation still happens, but it happens in small chunks of time as things are needed, rather than causing long pauses

If there will potentially be a large number of subclasses of your interface, try to find a way to divide them into context-appropriate categories and use folders in the system filesystem to partition contexts.

Why Declarative Icon and Display Name Registration Is Particularly Important

Many pieces of user interface in NetBeans — almost any tree view — is a view of a folder on disk, or a folder in the system filesystem. The Services tab is such a view; the Projects tab composes several such views; the left and right sides of the first pages of the New File and New Project wizards are such views.

The need to simply show an icon and a name should not ever be the trigger for loading hundreds or even thousands of classes (bear in mind that loading your class may mean loading many other classes — and the Java Bytecode Verifier may trigger loading many more classes than you expect).

You can handle this very simply with .instance files:

<filesystem>
  <folder name="UI">
    <folder name="Runtime">
      <file name="MyNode.instance">
        <attr name="instanceClass" stringvalue=
          "org.netbeans.modules.stuff.MyNode"/>
        <attr name="iconBase" stringvalue=
          "org/netbeans/modules/stuff/root.png"/>
        <attr name="displayName" bundlevalue=
          "org.netbeans.modules.stuff.Bundle#MyNode"/>
        <attr name="position" intvalue="152"/>
      </file>
    </folder>
  </folder>
</filesystem>

and in your resource bundle file, define

MyNode=My Node

This was a serious problem in older versions of the NetBeans IDE - for example, opening the Options dialog (which used to be a tree of Nodes and a property sheet - modules that had settings provided their own Node, and you changed settings by changing properties) - simply trying to paint it for the first time originally triggered loading, literally, thousands of classes from many different JAR files.