Overview

Javadoc

You may want to look at the Javadoc, particularly that for the ModuleInstall class. Other classes in the package are generally not useful to module authors.

Contents

Modules API

What are Modules?

Modules permit the IDE to be extended dynamically. All of the Open APIs are designed to be used for purposes of implementing modules. Modules may range in complexity from a single Java class, properly packaged, to do something elementary such as add a menu item to the Edit menu to display the contents of the clipboard; to a full-scale integration of a major external application, such as a Java profiling suite.

All modules are distributed and installed as JAR files. The basic format should be rather familiar; classes constituting the module are archived in the JAR, and special entries in the manifest file are recognized by the IDE.

Related Standards

To the greatest extent possible, NetBeans has designed the module system to reuse standard technologies when they are sufficient, and to follow the style of others when not.

The basic idea for the format of modules is taken from the Java Extension Mechanism. The Package Versioning Specification is used to handle dependencies both between modules and of modules to the system.

All modules have some set of basic properties that indicate which types of features they provide, which Java classes implement these features, and what special option settings should be used when installing these features into the IDE. All of this information is listed in the manifest file using the customary format, and NetBeans-specific attributes. The Java Activation Framework, as well as JDK-internal features such as support for executable JAR files, was used as a model for how to specify the useful contents of a JAR in a data-driven fashion: many modules will need no special installation code other than attributes in the manifest. The Enterprise JavaBean deployment model is used for the standard sections. JavaHelp is used for the context help.

How to Create a New Module

For basic cases, you should need to do very little to create a module besides writing the basic source code.

Please see a list of tips on writing modules, installing them into the IDE, testing and debugging them.

Build a JAR

All module implementation classes must reside in a JAR file. If you want to split up a large application into several pieces, perhaps so as to make independent upgrades possible, you should do so by creating multiple modules and relating them using versioning.

To create the JAR, just use the standard JDK's JAR tool. You will need to add a custom manifest file, so use the -m option to specify the additional contents. (Note that -m will automatically merge the contents of your manifest file with anything it would normally add, so e.g. signatures will be automatically created properly.)

Writing the manifest

A module is recognized as such by the IDE, by virtue of its having a special magic tag in the global section of the manifest: OpenIDE-Module. Its value should be an arbitrary identifier (with a format similar to that of a Java package name) identifying the module for purposes of upgrades and dependencies. You are encouraged (but not required) to use as this tag the name of a Java package containing the principal classes for the module; this ensures that there will not be conflicts between modules produced by different developers or institutions, provided that you follow the JavaSoft recommendations on naming packages according to an Internet domain name you control. See the section on versioning for details on what this tag is used for.

There are a few other global tags which are optional but encouraged:

OpenIDE-Module-Name
Gives a human-presentable display name for the module. The name may be localized by adding additional tags for each locale, e.g. OpenIDE-Module-Name_fr.
OpenIDE-Module-Short-Description
A short description of what the module does (about one sentence, like a tool tip). May be localized as for the display name.
OpenIDE-Module-Long-Description
A longer description of what the module does (roughly paragraph-length). May be localized as for the display name.
OpenIDE-Module-Display-Category
A phrase giving a category for the module. Modules with the same category may be visually grouped together in various parts of the UI. May be localized as for the display name.
OpenIDE-Module-Description
Allows the IDE to provide JavaHelp and context help for the module.
OpenIDE-Module-Install
The name of a custom module class.
OpenIDE-Module-Layer
The name of an installation layer.
Various versioning-specific tags
Please see the section on versioning for details on these.
OpenIDE-Module-Module-Dependency-Message and OpenIDE-Module-Package-Dependency-Message
Localizable pleasant messages to display to a user in case a module or package dependency fails. In some cases it may be quite normal for a dependency to fail and it is desirable to provide a specific and helpful message to the user explaining where to get the required dependency or why the module depending on it is not needed by this user. May be localized as for the display name. Since 1.26
The presentation-oriented tags (display name and category, short and long description) may be in HTML if you prefer; this should be done in the standard Swing fashion, by beginning the text with the magic string <html>.

Since 1.24: A more flexible way to provide human-readable information is to declare the attribute OpenIDE-Module-Localizing-Bundle. Its value should be the resource path to the (base locale of) a bundle file providing localizable attributes for the module (display name, category, short and long description). The bundle file may have localized variants as usual, and the keys should be the same as the desired manifest attribute names, e.g. OpenIDE-Module-Name. If it is necessary to localize an attribute from a section (currently only filesystem sections with their Display-Name attribute), you may again place these in the bundle, where the key will be prefixed by the section name and a slash, e.g. org/foo/MyFileSystem.class/Display-Name.

All other tags are bound to a particular module section. Naturally you may use other standard manifest attributes.

In summary, here is an example manifest file that could be included with the JAR tool's -m option:

Manifest-Version: 1.0
OpenIDE-Module: com.modulemakers.clip_disp/2
OpenIDE-Module-Specification-Version: 2.0.1
OpenIDE-Module-Implementation-Version: build-213a
OpenIDE-Module-Name: Clipboard Displayer
OpenIDE-Module-Name_cs: Prohlizec schranky
OpenIDE-Module-Description: com.modulemakers.clip_disp.docs.ClipHelpSet
OpenIDE-Module-Install: com/modulemakers/clip_disp/Installer.class
OpenIDE-Module-Layer: com/modulemakers/clip_disp/Layer.xml
X-Comment-1: I am a comment (just a deliberately meaningless
X-Comment-2: header) - "Sealed" is a standard manifest attribute.
Sealed: true

Name: com/modulemakers/clip_disp/DisplayClipboardAction.class
OpenIDE-Module-Class: Action

Module authors are strongly encouraged to use the API Support module which among other advantages, provides interactive parsing of manifests that can help the beginning API developer immediately see how the IDE will parse his manifest, including possible parse errors.

If you have troubles with a manifest, check to make sure you have the suggested extra blank line at the end. Some JDKs may have trouble parsing it otherwise.

Using a custom module class

With the OpenIDE-Module-Install attribute, you may specify a custom class which will handle any complex aspects of the module's installation (or uninstallation). This class is only necessary to write if the standard module sections and layers do not cover everything you need to do. Even if you do write such a class, standard sections and layers may still be used for any part of the module's integration into the IDE which is conventional - the main class need only handle the exceptional parts.

To use a main install class, just extend the ModuleInstall base class. Your class must be able to be instantiated as a JavaBean. There are several methods which you may override, and may do anything which is required to make the module cleanly enter and exit the IDE.

For example:

package com.modulemakers.clip_disp;
import org.openide.modules.ModuleInstall;
import org.openide.filesystems.FileUtil;
import java.net.*;

public class ModuleHandler extends ModuleInstall {
  public void installed() {
    // This module has been installed for the first time! Notify authors.
    HttpURLConnection conn = (HttpURLConnection)
      (new URL ("http://www.modulemakers.com/clip_disp/installed.cgi").openConnection ());
    conn.getResponseCode ();
    conn.disconnect ();
    // Handle setup within this session too:
    restored ();
  }

  public void restored() {
    FileUtil.setMIMEType("test", "text/x-clipboard-content-test");
  }

  // Do not need to do anything special on uninstall.
  // Tools action will be removed automatically.
  // public void uninstalled() {
  // }

  public boolean closing() {
    // Ask the user to save any open, modified clipboard contents.
    // If the user selects "Cancel" on one of these dialogs, don't exit yet!
    return DisplayClipboardAction.askAboutExiting ();
  }
}
Some common things that module main classes tend to do:
  1. Install editor kits.
  2. Or, install an HTTP server.
  3. Install a class element finder into the system.

The installed handler may do more or less what it wishes - it will be called in a running IDE. The same applies to uninstalled and closing. However, restored is more delicate in that it may be called during earlier phases of IDE initialization. Therefore, it should not use presentation-oriented features of the IDE, such as the Explorer or Window System APIs. However, the other APIs are acceptable to use in restored, including the TopManager's dialog notification methods.

The ModuleInstall.updated(...) method will be called just once when a module is updated to a new version - when the new version is loaded into the IDE for the first time (in a new session), the method is called and the previous version number is accessible. Neither installed nor uninstalled will be (automatically) called in this case. It is a module author's responsibility to make sure that new versions of a module are capable of "cleaning up" obsoleted installations created by older versions, as far back as a user is likely to be directly upgrading.

Some module installer classes may desire to keep state across IDE sessions, for example if they require specific instructions on what to uninstall that can only be gotten during the installation process. All installers are automatically Externalizable and module authors may decide to override the two methods of this interface to read and write its state from an object source. Typically you would want to serialize and deserialize shared class fields in the object using these methods. It is a good idea to call the super methods as the first action when overriding.

In some cases, a module relies on some external resource to be present in order for it to be used. For example, an external application may need to be installed in order for it to do anything. Or it may require a valid license key. In such cases, the ModuleInstall.validate() method may be overridden to check for this external resource and throw a (preferably politely localized) IllegalStateException if something is wrong. Throwing this exception will cancel the module installation (or restoration) cleanly. Note that such an installer should not expect anything related to the module to already be loaded when this method is called, as it may be done when deciding whether to even begin loading.

"Installation-clean" modules

Since the IDE can better manage modules when their resources are declared to exist, rather than procedurally installed, it is desirable to use declarative APIs whenever possible. Consider the following definition of an "installation-clean module":
  1. Either the module has no specified ModuleInstall; or
  2. It does have a ModuleInstall but this installer:
    1. Does nothing of consequence during initialization of the ModuleInstall object (static or instance fields with initializers or initializer blocks or the initialize method).
    2. Does not override the readExternal nor writeExternal methods, nor any other related methods such as readResolve or writeReplace. Thus it has no externalized state.
    3. Does not override installed nor updated (these will then just call restored).
    4. May override uninstalled but only to undo the effects of restored or other changes made by the module in the IDE while running. May override closing and/or close but only to undo the effects of changes made by the module while running.
    5. May override restored and/or validate.
Modules which are not installation-clean may not work as well in conjunction with future "session" support for projects, and may interfere with creation of "virgin" builds of the IDE (those that merge module resources into an installation directory but do not actually prime the IDE by running it once).

Additionally it is recommended not to use restored to add an object to the running IDE where use of the module layer would suffice, nor to modify persistent state in restored or validate that might affect a future call to the same method. When possible it is best not to have a ModuleInstall at all.

Furthermore, the following practices should be avoided wherever possible in order to simplify installation of the module from Auto Update and via "ad-hoc" addition of the module JAR by a user (note that mentioning options here does not imply that they are officially supported by the APIs or even unofficially possible at any particular time):

  1. Including any libraries in the lib/ext/ directory, as these cannot be added to or reloaded while the IDE is running.
  2. Using the docs/ directory as a place to store module JavaHelp rather than inside the JAR.
  3. Using Class-Path from within the module JAR or its extensions to point to resources which are not in or below the directory containing the referring JAR (i.e. inclusion of ../ or absolute paths).
  4. Including any files in the IDE installation other than the module JAR in the modules/ directory and any extensions declared directly or indirectly via Class-Path beneath the modules/ directory (for example in modules/ext/) and any locale variants of the above JARs; except insofar as the presence of such files does not in and of itself change the functionality of the IDE and is not required for the module to operate (for example mounts of documentation ZIPs declared in the layer); even then this is discouraged if there is an alternative as it complicates ad-hoc module installation though Auto Update may be fine.

Using an installation layer

Starting with NetBeans 3.1, you may specify the global tag OpenIDE-Module-Layer in addition to or instead of a module main class; as a rule, use a layer for a particular task in preference to a module installer, if you can. The tag value should point to an XML file inside the module which is in the format understood by XMLFileSystem. Its contents specify some files that the module will add to the system file system controlling much of the IDE's configuration.

The primary advantage of using layers is that they are declarative rather than procedural, so you can install many kinds of extensions to the IDE's behavior without any Java code, only XML. A related advantage is that unlike ModuleInstall, there is no need for multiple kinds of logic for module installation, restoration, uninstallation, and upgrading; the "files" added by the layer are not stored to disk and are loaded by the IDE in every session from the XML, so when the module is uninstalled or upgraded its associated files are correctly removed or changed, respectively, without any extra work. (If the user customizes the files, however, their customizations are stored to disk, and thus form a permanent "patch" against the baseline configuration provided by the module.) Currently only some parts of the IDE can be customized using files on the system file system and thus by layers, but in the future more and more things will be installable this way.

Some common things that layers tend to be used for:

  1. Install actions into the IDE's global menus, toolbars, or keymap.
  2. Create templates in the folder Templates/.
  3. Add a startup file to Startup/. It should be executable (typically a Java source file with associated classfile). Startup files should normally be executed when first added by an installed method, and they will be automatically executed by the IDE every time it starts up (recompiling first if needed). They are generally intended to provide a way for users to customize some aspects of configuration using arbitrary code as an alternative to a module's restored() method. (But if your configuration can be decently controlled using normal GUI settings than this should be done. Using the startup folder is mainly for expert users with complex needs.)
  4. Other things such as bookmarks, Component Palette beans, Welcome panel buttons, and so on - according to the structure of the system file system.
  5. Most generally, to install arbitrary services into the system.

By default files listed in XML layers are not ordered in any particular way, just as files found on disk would not have a particular order. For purposes of some kinds of installation, it is desirable to order data objects in data folders in a particular way - for example, menus are built from their menu items in the order the instance-bearing data objects occur in the data folder. The call DataFolder.setOrder(DataObject[]) suffices to arbitrarily change folder order, but usually it is desirable to create the proper order declaratively in the XML. For this reason, DataFolder understands special attributes (set on the FileObject forming the folder) which provide relative ordering constraints on the objects in the folder. Specifically: if there are data objects A and B in the folder, represented by primary files afile and bfile, and there is an attribute on the folder containing them named afile/bfile whose value is Boolean.TRUE, then the folder will attempt to place A before B in its ordering. For example, the XML:

<folder name="SomeFolder">
    <file name="first.txt" url="first.txt"/>
    <attr name="first.txt/second.txt" boolvalue="true"/>
    <file name="second.txt" url="second.txt"/>
</folder>
will cause the IDE to try to keep first.txt before second.txt. Things to remember:
  1. Modules may provide layers some of whose files specify relative constraints on files in the same folder position but installed by the core, or other modules. That is fine and the constraints will be honored - assuming the files being constrained both exist. Constraints for which one or both of the affected files do not exist, are simply ignored.
  2. It is fine to provide multiple constraints from a single file, for example if there are several possible files it might need to be installed after; all relevant constraints will be attempted.
  3. If constraints are contradictory, they may be ignored (and a warning issued).
  4. If the user reorders the folder manually or DataFolder.setOrder(DataObject[]) is called, the explicit order overrides the existing constraints. Subsequently added constraints might have an effect, however.
  5. Only the names of primary files from data objects have any bearing on the ordering.
  6. A constraint specifying that A comes before B does not force the folder to place it immediately before B. If you want to ensure that A in fact comes after some other files you know come before B, thus trying to make them adjacent, this must be done explicitly. Another module might explicitly interpose a file C between A and B.

The resource path pointing to the layer is automatically localized by the IDE every time the module is installed or restored; if there are locale-specific variants, they are merged with the main layer. More-specific variants can simply add files to the set installed by the module; however they take precedence over the less-specific variants, and thus can replace files, or even remove (suppress) them, using *_hidden masks as used by MultiFileSystem. In fact, if module A depends on module B, then A's layer(s) will take precedence over B's layers, and it may replace or remove files installed by B. In the same way, any module may replace or remove files installed by the core.

(Compatibility note: in NetBeans 3.1, only the most specific locale variant of a layer is used, rather than merging them all together. You may write layers which are conformant with this API and which also work correctly in 3.1 by ensuring that all variants mention all of the common files, as well as their particular overrides and masks.)

Display-oriented file attributes

In several IDE releases including NetBeans 3.3, the IDE recognizes two special file attributes, typically set in layers, which can improve UI. These are not officially supported by the Open APIs and this behavior may in fact change in a future release, especially if another approach is found to be more efficient at runtime or easier to use.

SystemFileSystem.localizingBundle

If set on a file object in the default file system serving as the primary file for a data object, the data node's display name as annotated by the filesystem will be taken from that resource bundle (given as a string). The bundle will be found using NbBundle.getBundle(String), meaning that it should be of the form org.domain.pkg.Bundle and will be found using the default classloader and locale. The key in the bundle should be the full resource path of the file.

SystemFileSystem.icon

If set on a file object on the default file system, the data node as above will be given the 16x16 icon supplied by the attribute value, given as a URL (java.net.URL, not java.lang.String). Similarly SystemFileSystem.icon32 for the 32x32 icon, if desired.

By using these attributes, modules may provide a more pleasant and localized user interface for files they install (typically via layer) whose data nodes would be seen by a user (for example, templates or services). If support for these file attributes is removed or incompatibly changed in the future, such data nodes (and anything driven by display aspects of data nodes, for example the labels of menus created from folders beneath Menu/) will still appear and be functional, however the names and icons will be unattractive.

Using versioning

Module versioning provides a way for modules to specify which module they are (i.e. that one JAR is an upgrade from another); which version they are; whether they have introduced incompatible API changes since a previous version; and (importantly) whether they depend on other modules or system features, and if so how. While very simple modules may not require special versioning support, this system should be used for any module published on a general release schedule, or which it is expected other modules may want to make use of.

Modules can do three things with versioning:

  1. Specify what they are. This is done by the special OpenIDE-Module tag, whose value should be a unique (programmatic) identifier for the module, as mentioned above. Not be confused with the display name, which is free-form and may be changed at will, this code name should not be changed arbitrarily - pick a name and stick with it throughout module releases.
  2. Specify which version they are. In line with the Java Versioning Specification, modules can indicate two pieces of version information about themselves using the OpenIDE-Module-Specification-Version and the OpenIDE-Module-Implementation-Version tags.
  3. Specify which features they depend on. Again, this is done using the Versioning Specification - modules can request general or specific versions of other modules (OpenIDE-Module-Module-Dependencies), Java packages (OpenIDE-Module-Package-Dependencies), Java itself (OpenIDE-Module-Java-Dependencies), or the IDE (OpenIDE-Module-IDE-Dependencies).
At the heart of all these tags are the conventions used in the Package Versioning Specification created by JavaSoft. While their documentation should be referred to for the full details and justification of this system, the basic idea is that features (modules) have two versions: a specification version, in Dewey-decimal format (e.g. 1.2.1), and an implementation version, which is some free-form text. Specification versions may be incremented to indicate compatible API extensions, in which case the check to make sure that the feature requested is available may be done using a (lexicographical) compare on the Dewey-decimal numbers (e.g. 1.0 < 1.0.1 < 1.1). Implementation versions may be requested according to an exact match only. Thus, specification versions are generally used when you depend on some general and specified behavior of another feature; implementation versions are used to tie builds of different features together closely, as when you maintain and release all the features yourself, and wish to distribute them in a well-tested unit.

So, modules may specify either or both kinds of versions in their manifest, and correspondingly request such versions from other modules. The Versioning Specification provides similar numbering for (some, but not all) Java packages, as well as the core Java Platform APIs and Virtual Machine Specification. The IDE also has such versions which may be used to request a general level of Open API support (using specification versions) or a particular NetBeans build (using implementation versions).

Since the Versioning Specification does not address incompatible changes (those which would break existing code), but these are in practice occasionally necessary (however painful), both modules and the IDE itself may provide a separate integral number giving the incompatible release version. You may only request a particular release version, not subsequent ones, and you must specify the release version if there is one when giving any module dependency. It only differs from a complete change of the feature in that the module installer tool may prompt the user to make a "major upgrade" if a new release version of a module is available.

To use all of these tags once you understand them is not too hard. First of all, feature names: a module is named according to the OpenIDE-Module tag, which should look like a Java package name, but may be followed by a slash and release number (e.g. com.mycom.mymodule/3); the IDE itself is named IDE, again usually with a slash and release number; Java packages are just named by the package name; the Java Platform APIs are named by Java; and the Java Virtual Machine by VM.

Now, a module may list its own specification and/or implementation versions using the tags above. To specify a dependency on other features, it may use some or all of the four dependency tags. The value of each will be a comma-separated list of dependencies of that general type, each of which can be of the form:

FEATURENAME
Just requests that the feature be present, not any particular version. Useful for modules and Java packages (usually, Java standard extensions) that need to be installed, but that is all.
FEATURENAME > SPECVERSION
Requests that the feature be present, that it state a specification version, and that this version be greater than or equal to the requested version. Note that you may not request an exact specification version. This is the preferred form for module dependencies.

Remember, if requesting a dependency on a module which has a release number, you must give that release number as part of the feature name, whether or not you also ask for a specification version.

FEATURENAME = IMPLVERSION
Requests that the feature be present, that it state an implementation version, and that this version exactly match the requested one. Note that you may not request a comparison on implementation versions, as they are generally non-numeric and not comparable.

In summary, here is an (unlikely) example that specifies and requests all sorts of versions:

OpenIDE-Module: com.mycom.mymodule/1
OpenIDE-Module-Specification-Version: 1.0.1
OpenIDE-Module-Implementation-Version: build99
OpenIDE-Module-Module-Dependencies:
 com.mycom.mysistermodule/1 = build99,
 com.othercom.anothermodule > 2.1,
 org.netbeans.modules.applet/1 > 1.0
OpenIDE-Module-Package-Dependencies: javax.television > 0.9
OpenIDE-Module-Java-Dependencies: Java = 1.2.1b4, VM > 1.0
OpenIDE-Module-IDE-Dependencies: IDE/1 > 1.0

There is further special treatment for handling of package dependencies for several reasons:

  1. In practice, the Java Versioning Specification is not all that widely followed, and many needed extensions will not list this information in their manifest.
  2. Classloaders only define packages when the first class from that package is actually loaded. This makes it more difficult to verify whether a package is really there or not.
  3. The standard manifest attribute Class-Path must be recognized and used; typically this attribute is used to actually add extensions to the module classloader, while the Modules API attributes are used to ensure that specific versions are available.

You may specify a sample class name in square brackets immediately after the package name (or indeed instead of it). Giving a class name together with a package name requests that the IDE first try to load that class (it must be able to, so choose a class in the extension you know will be there); then the versioning information is checked. (If the sample class is simply in the desired package, you may omit the package qualification as a shortcut.) This addresses the second problem. Giving just a class name in square brackets with no preceding package name indicates that the IDE should only ensure that the named class is loadable, bypassing the Versioning specification mechanisms. This addresses the first problem.

Here then is a sample manifest which depends on an extension called again javax.television, where it is known that a class javax.television.TunerProvider is part of the package. The extension is stored in modules/ext/television.jar relative to the IDE's installation directory:

OpenIDE-Module: com.mycom.mymodule/1
OpenIDE-Module-Package-Dependencies: javax.television[TunerProvider] > 0.9
Class-Path: ext/television.jar

Where can you get the version information to depend upon?

  1. For installed modules, this information is listed in the module's node in the Modules area under Session Settings.
  2. For packages, use the Java Package class, or examine the manifest of the JAR you depend upon.
  3. For the Java platform, use the system properties java.specification.version and java.version; for the VM, java.vm.specification.version and java.vm.version.
  4. For the IDE, this information is printed to the console when the IDE starts up, and also appears in netbeans.log.

Important! If your module has compile-time references to classes in another module, you must specify a dependency on that module. This ensures that the modules will be installed in the correct order; otherwise a NoClassDefFoundError or similar problem could result.

Standard Module Sections

Standard module sections permit the use of common extensions to be simplified, as the use of an explicit module main class is not required for these. In each case, the class file implementing the extension must have a manifest entry containing the OpenIDE-Module-Class attribute, giving a code for the extension type (see below); and may also have additional entries for further customization.

Action

The class Action installs a system action into the IDE. Refer to the Actions API for details on how to create an action. The class must implement SystemAction.

By using this manifest section, your action will be available as a "tool", i.e. it will appear (when enabled) in a list of all extension tools in any popup menu (etc.) using ToolsAction. Note that you do not need to use ToolsAction directly for this purpose: it should already be attached as a popup to all interesting nodes, as a menu item in the Tools menu, etc. Just using the manifest tag ensures that your action will be shown under such submenus.

Note: Actions may also be installed into fixed places in the Main Window's menus and toolbars (this can also include special components like palettes and so on), to keyboard shortcuts, or to the default popup menu of a data loader you did not write. The Actions API describes how to do all of these things - they do not require anything special in the manifest (besides use of a module install class).

Some types of actions make sense only for an object under your direct control. For example, if you are writing a data loader, you should specify which actions will be provided by default in its data objects' popups. This type of use does not require global installation; it can include standard IDE actions, as well as your own. Please see the Actions API for details on attaching an action to your own object.

Also, as mentioned in the Actions API, it is good practice for all actions implemented by modules to be copied into logical subfolders of Actions/ on the system file system (for example using a module layer), since this folder is presented to the user as a read-only "actions pool" they can use as a source of all actions to customize their menus, toolbars, data object popup menus, keyboard shortcuts, and so on.

Option

The class Option installs this system option into the IDE's control panel, so that the module may have customizable settings which will persist across sessions. See the Options API for details on how to write an option.

Installation of options from manifest is now deprecated. See the Services API instead.

Loader

The class Loader installs a data loader into the system loader pool. Refer to the DataSystems API for instructions on how to create a loader.

There are a couple of options which may be used to specify the relative precedence of data loaders, so that a more specific loader may take precedence over a more general one:

Install-Before: classname
Instructs the IDE to try to install this data loader before another, so as to give it higher precedence: it will be given the opportunity to scan over potential data objects first. The classname should specify the representation class of the other loader's data object type; see the DataLoader constructor for an explanation of how to use representation classes. You may supply multiple class names separated by commas.
Install-After: classname
Exactly like Install-Before, except that the IDE will attempt to install this loader after another, so as to give it lower precedence.
Either of Install-Before and Install-After tags (if present) may fail to be honored if there is no such registered data loader to compare to, or if the tags are contradictory (imply a cyclic dependency); if so, the loader will still be installed. By default a loader is placed at the end of the pool (i.e. lowest priority). Note that these tags have no effect on module install order or dependencies - for one thing, loaders will be installed in the correct order regardless of which order their providing modules are installed in.

You need not specify module dependencies on other modules simply because you use the names of data objects in those modules in your install before and after tags; the ordering hints will simply be ignored unless and until the other module is installed, at which time they will take effect.

Debugger

The class Debugger installs a new system debugger, replacing the previous one. Refer to the Debugger API for details on how to write a debugger.

Installation of debuggers from manifest is now deprecated. See the Services API instead.

Service

The class Service installs a new service type, of which there may be many registered at once. Refer to the Services API for details on how to write a service type.

Installation of services from manifest is now deprecated. See the Services API instead.

File system

The class Filesystem installs a new file system type into the IDE, which may subsequently be instantiated by the user and added to the system repository. Refer to the FileSystems API for details on creating a custom file system.

You may specify a couple of options for a file system:

Display-Name: some text
This sets the display name of the file system type, which will be used (for example) when creating an "Add some text" menu item under the Tools menu, and a corresponding popup on the Repository node. Localized variants of the name may be provided, e.g. by adding an attribute Display-Name_de (in this case, for a German locale).
Help: help ID
Provides context help for this type of file system, in case some of its configuration or use is not apparent. Should be a JavaHelp help ID, as described in the section on context help.

Note that this help ID is provided in the manifest, rather than on the file system itself, since it should not be necessary to actually create a file system instance to get it (which could have undesirable side effects), and it may be used in (e.g.) popup menus on the Repository permitting the user to add a new file system.

Rather than installing a filesystem type via manifest, however, you are encouraged to instead provide a service template according to the FileSystems API.

Node

The class Node installs a new node into some standard area of the IDE. You should also specify where to place it with an attribute Type, which currently may be one of:

Refer to the Nodes API for details on constructing an appropriate node.

Clipboard convertor

The class ClipboardConvertor installs a new clipboard convertor into the system, that may be used to perform transformations on the contents of the system clipboard to data flavors that are not otherwise supported by the transferable. Please see the DataSystems API for additional details.

Installation of convertors from manifest is now deprecated. See the Services API instead.

JavaHelp

It is possible to add JavaHelp and context help capabilities to modules.

JavaHelp via layer

As of APIs 1.6

The most flexible way to add JavaHelp is via your XML layer. First you need to add the helpset to the IDE. This has several effects: it ensures that context help can use your helpset; it enabled help links to use help IDs defined in this helpset; and assuming the helpset is marked to be merged into the master helpset, this means that the menu item Help | Contents will show any navigators you define in your helpset, merged into others.

To add a helpset, you must register an object implementing javax.swing.HelpSet into the IDE's lookup system. The normal way to do this is to create an XML file with a special DTD which you place into the Services/JavaHelp/ folder of the system file system via layer. This file specifies the URL to the help set, for example:

<?xml?>
<!DOCTYPE helpsetref PUBLIC
          "-//NetBeans//DTD JavaHelp Help Set Reference 1.0//EN"
          "http://www.netbeans.org/dtds/helpsetref-1_0.dtd">
<helpsetref url="nbresloc:/org/netbeans/modules/foo/help/HelpSet.hs"/>

By default such a helpset will be merged into the IDE's master help navigators. You may opt out of this by specifying merge="false" on the <helpsetref/>. If providing a custom help set instance via lookup, in order to turn off merging you should set the "key data" on the helpset with the context OpenIDE and key mergeIntoMaster to Boolean.FALSE.

Using nbresloc implies that the helpset can be found in the module's classloader, for example in its main JAR or in an extension ZIP or JAR referenced via the Class-Path manifest attribute. There is a URL protocol nbdocs which is similar to nbresloc, but the current NetBeans core implementation recognizes this URL and permits the help set and associated files to reside in a package hierarchy underneath the docs/ folder (of the IDE installation or user directory), if not in the module classloader.

To add a shortcut to a specific help ID in your help set, there is a different XML DTD you can use. Normally such shortcuts would go into the folder Menu/Help/HelpShortcuts/ so as to appear in Help | Help Sets. The shortcut XML specifies the help ID to display (which must be present in some registered help set of course), for example:

<?xml?>
<!DOCTYPE helpctx PUBLIC
          "-//NetBeans//DTD Help Context 1.0//EN"
          "http://www.netbeans.org/dtds/helpcontext-1_0.dtd">
<helpctx id="org.netbeans.modules.foo.some-topic"/>

By default such a link will display a help window containing navigators only for the help set from which it was taken. If you prefer to display navigators for all merged help sets, specify showmaster="true" on the <helpctx/>.

This XML file's only use is that it has a menu/toolbar presenter which opens the specified help page. (You can customize its display name and icon using the normal means available to templates etc.) You could provide an alternate means of displaying help shortcuts, however the functionality of showing the master navigators is only accessible via the presenters of this kind of XML file.

JavaHelp via manifest

If you are using a version of the APIs older than 1.6, or simply prefer the default mode of displaying JavaHelp and do not require any customization, you may also install JavaHelp via module manifest.

The tag OpenIDE-Module-Description specifies a help-set file according to the standard JavaHelp specification. The tag value gives the location of the help set within the module JAR, and supports localization; for example, the description com.mycom.mymodule.docs.TheHelpSet might look for JAR entries such as:

  1. /com/mycom/mymodule/docs/TheHelpSet_en_US.hs
  2. /com/mycom/mymodule/docs/TheHelpSet_en.hs
  3. /com/mycom/mymodule/docs/TheHelpSet.hs
(This is the standard order used by property bundle searches, etc.)

First of all, providing a help set means the IDE will permit users to see it via the normal JavaHelp viewer, and employ standard merging of navigational views. If you provide a home ID, a menu item linking to that home page will be added to the Help menu automatically, named according to the help set title; if it ends with an asterisk (*) it will be treated as a distinguished help set covering a prominent area of documentation. If you provide various navigational views, they will appear in merged form. Modules are strongly encouraged to provide a home ID, and views in the table of contents, index, and full-text search modes, making for a well-integrated help system.

Context Help

When the help set is specified in the module, this permits the IDE to associate context help with various features associated with the module (or, technically, other modules - if you know their help IDs and they do not themselves bind them). There are several ways to associate help IDs with module features so that they may be used by the IDE, mostly revolving around the API class HelpCtx.

This class has several constructors for different purposes; the constructor which takes a Class is convenient for some purposes, or the one taking a String is also fine. The constructor with a string directly uses that help ID, which may be any string uniquely identifying the feature, and associated with an HTML page using the map file; when passing in a class object, this should typically be the class returning the help object, and the ID will be the fully-qualified class name, which avoids the need to separately choose a good help ID.

For example, a system action might include a method implementation such as:

public HelpCtx getHelpCtx () {
  return new HelpCtx (ThisAction.class);
}
Now the map file should include a line such as:
<mapID target="com.mycom.mymodule.ThisAction" url="this-action.html" />

There are a number of different points in the APIs where you may specify a HelpCtx in this fashion:

There are some other points where you may specify a help ID directly:

The proper help page for the selected component, if there is one, is displayed by the IDE by default using F1, which calls HelpAction. This applies to various visual components (when they have focus), the current TopComponent, and nodes (or the objects they represent) when the node is selected in an Explorer window (or custom windows based on ExplorerPanel). Programmatically, you may also display help by calling TopManager.showHelp(HelpCtx).

Deployment of Modules

This section describes how a module may be deployed to a user's IDE.

Downloading

One scenario is that the user will just download the module from the Web somewhere as a JAR file. The module may be stored to disk, and either dropped into the IDE's modules/ directory (where it will be automatically installed upon the next IDE restart) or manually installed from an arbitrary location using the context menu on Global Settings | Modules. Children of this node represent available modules and permit them to be dynamically uninstalled or reinstalled.

More user-friendly is the Update Center, a feature specific to the NetBeans core implementation (i.e. its mechanism is not specified by the APIs), which presents guided dialogs prompting users to automatically upgrade modules or install new ones from an update server. This functionality is provided by a module.

Upgrades

Upgrades of modules can be customized by calling ModuleInstall.updated(). This permits you to manually remove obsoleted actions, delete old templates, etc. - remove anything not handled by the module manifest which is nevertheless part of the IDE's persistent state. With the advent of XML layers, most of these reasons for using updated() have disappeared.

A proper module will store all of its configurable state using options (excepting service types, module externalization, etc.), and the contents of these will be automatically carried over, one option at a time (according to option name and property name) into the new module version using Externalization - so as long as your option values can be externalized and reread into the new version, you need not worry. Similarly, if the ModuleInstall class is externalizable and there is non-project state being stored this way, you must ensure as usual that your externalization is forward-compatible for upgrading to proceed smoothly.

Module install order

It may be possible for multiple modules to be installed at once, or for a module to specify a dependency which is not satisfied. How does the IDE handle such cases?

First of all, when the IDE starts up, all previously-installed modules are restored, calling the restored methods on their install classes if present. (Sections are installed before this method is called.) If multiple modules are being restored (which is normally the case), the IDE installs them in an arbitrary order. However, if some of them specify dependencies on other modules, the order may be adjusted so as to make sure that the one specifying the dependency is installed after the one it depends on.

Now, when the IDE is running, modules may be installed into it, possibly a cluster of them at once. In such a case, the IDE first checks to make sure that all the prospective modules satisfy their dependencies, either on system or Java features; already-installed modules; or other modules in the prospective cluster. If any modules fail their dependencies (or had syntactical errors in their manifest files, etc.), they are removed from the list and the user is given an explanation of what is missing. The remainder are then ordered according to dependencies as above, and then installed in that order.

Caution: it is forbidden to install modules which have cyclic dependencies on one another, as the IDE would be unable to determine which order to install them in! In fact, it will signal an error and not install such modules. Generally such a situation means that you should "factor out" the common required functionality into a new module which the original ones will then specify legal dependencies on.

List of all modules

Though a module should not generally need to know or care what other modules are installed - anything it needs should be a dependency, and anything which can use it, it should not know about - in a few cases it is desirable to obtain the list of all modules in the system. This can be easily accomplished as follows:
Lookup.Template templ = new Lookup.Template(ModuleInfo.class);
Lookup.Result result = Lookup.getDefault().lookup(templ);
Collection modules = result.allInstances(); // Collection<ModuleInfo>
The resulting ModuleInfo objects give basic information about known modules (both enabled and disabled), such as their names and versioning information, and current enablement status. This API is strictly read-only and informational.

A limited ability to modify the installation status of modules is provided by the APIs as of version 1.31. In the folder Modules/ on the system file system, there will be XML files corresponding to all known modules. In each case the name of the XML file is derived from the code name base of the module with . replaced by -, and followed by .xml; for example, org-netbeans-modules-properties.xml. The contents of the XML file are given by a fixed DTD.

For purposes of the API, only parts of these files are defined. The root element must be <module> and its name attribute must be the code name base of the module. Inside the root element are various <param> elements, each with a name and some textual contents. The APIs define only one parameter, enabled, which if present must be either true or false.

The APIs do not currently permit arbitrary changes to these files. Specifically, addition or deletion of these files, or modification of other files that may be in the same folder, is not permitted. Modification of these files is permitted in only one way: if a module XML file already contains the parameter enabled, you may rewrite the file to be identical except with the opposite value of this parameter. The module system will then make a best effort to enable or disable the module accordingly (subject to dependencies and other constraints).

Doing this is not recommended except in unusual circumstances, and is intended primarily for session support to be able to enable and disable modules via layer. If your module just needs to prevent itself from being turned on under some circumstances (for example if it is missing a valid license key), simply use ModuleInstall.validate.

When rewriting the module XML be sure to use FileSystem.runAtomic(FileSystem.AtomicAction) to wrap the reading of the old XML and the writing of the new, to prevent file changes from being fired halfway through.

Security

In the current API there is no particular provision for security in modules: all modules are assumed to be trusted, and have access to all of the IDE's features (including the abilities of the Java VM running as an application) if they require them. So, users should not install arbitrary modules from potentially dangerous sources. Given the density of callbacks and the fine-grained object model of the APIs, providing a thread-based security model for API code is not feasible.

Code which creates new classloaders and loads other code indirectly should, however, be aware that the created classloaders are generally subject to regular security restrictions, unless code is loaded from within the IDE installation, or certain forms of NbClassLoader are used. When in doubt, explicitly define the protection domains for classloaders you create.

The IDE currently loads each module in its own classloader, which means illegal dependencies between modules will result in errors.

The Update Center supports certificate-based signing of downloaded modules, so that the user can be sure that the contents have not been tampered with or accidentally corrupted since their creation.

JNI

There is now (February 2001) unsupported provision for running modules making use of JNI native implementations inside the NetBeans IDE. This was possible before, but there was no fixed place to put the native libraries. Now you may create a directory modules/bin/ (in either the installation or user directories) and place native libraries (DLL or shared-object) there; it will be added to the search path for calls to System.loadLibrary made from module code. This avoids the need to explicitly add some directory to the binary load path. The module is entirely responsible for distinguishing between various platforms and operating systems and requesting a library name appropriate to the current one, however. Use of JNI is of course not recommended and should be restricted to cases where it is unavoidable.

Warning: since the JVM cannot load the same native library twice even in different classloaders, a module making use of this feature cannot be enabled more than once in a single VM session. JARs loaded from the classpath (application classloader) are never reloaded, so it may be possible to include the native-dependent classes in such a JAR and make use of it from a module JAR.

UML Diagrams

Modules API general structure class diagram

UML diagram for Modules API

Built on December 12 2001.  |  Portions Copyright 1997-2001 Sun Microsystems, Inc. All rights reserved.