站内搜索: 请输入搜索关键词
当前页面: 在线文档首页 > NetBeans API Javadoc 4.1.0

Upgrade Guide - NetBeans API Javadoc 4.1.0

Contents

Introduction

Module developers who have stable and tested modules developed against an earlier NetBeans release should in most cases be able to use them unmodified in a newer NetBeans release due to API compatibility. However improvements are often made, which module developers are encouraged to take advantage of to ensure the best integration into the new release. This document is an attempt to point out the most significant changes and how a typical module should be modified to take advantage of them, including some examples and tutorials. The best sources of examples are of course those modules shipped as part of NetBeans as in most cases these have already been upgraded to match the new APIs.

These recommendations are cumulative: for example, if you are upgrading from 3.2 straight to 3.4, check both the 3.2 -> 3.3 upgrade section and the 3.3 -> 3.4 upgrade section.

Minor local changes to APIs are not listed; interested developers can read through the complete change list if necessary (see next section).

The API Changes List

The definitive source of information is the API changes list. This summarizes all known Open APIs changes, big and small. For the most part you as a module developer do not need to read all this, because many of the changes simply will not be of interest to you.

Two things are important. One, if you are in fact using a new API, this document will tell you which version of the APIs to depend on in the list of changes by version. For the Open APIs, you must use OpenIDE-Module-IDE-Dependencies when making use of a newly added API call.

Two, there are a handful of incompatible changes which everyone should be aware of. See the list of incompatible changes.

Changes in Implementation Behavior and in Module APIs

This guide may be used to list changes in implementation behavior which are likely to be of interest to module developers, even while the official API specification has not changed. Always consult the official documentation for details on what the API really guarantees.

Useful information in the Open APIs documentation which is not guaranteed by the specification is shown with a blue bar in the left margin like this.

Modules can also provide their own APIs, including reference documentation and API change lists. Significant changes to module APIs may be listed here if they are likely to affect many developers. Visit a module's web site for more information.


3.6 -> 4.0 Upgrade Guide

This upgrade guide outlines notable API changes between NetBeans 3.6 and 4.0.

PENDING: need to link to documentation of 4.0 build system. When ready will be http://projects.netbeans.org/buildsys/howto.html Also to summaries of how to use JMI in place of org.openide.src. And info on new debugger APIs. Also http://openide.netbeans.org/proposals/arch/installation.html Also no more mounting, etc.

Notable Incompatibilities and Required Changes

Check for changes here which might cause problems for your module if left uncorrected.

Module JARs no longer autoscanned

In NetBeans 3.x, the module system would always check during startup for new JAR files in the modules/ subdirectory of the installation (also modules/eager/ and modules/autoload/). At the same time, the actual module configuration was controlled by XML files in the Modules/ subdirectory of the system file system (e.g. system/ subdirectory of the NetBeans installation or user directory), as mentioned in the Modules API. If the module system found a new JAR file in an autoscanned directory with no existing config file, it would create a fresh config file for it (trying to enable it), and run ModuleInstall.installed(). However module authors were strongly encouraged to create the proper config files in advance, so that the module configuration would already be correct and autoscanning would normally be a no-op, except after adding JARs from Auto Update or by manual copying.

As of NetBeans 4.0, this autoscanning functionality has been removed, which simplifies the module system, saves a bit of time during startup, and gives more flexibility to product assemblers since they are in full control of the module configuration. However this means that including a config file with your module is now mandatory. A config file can be written by hand, or created by the <createmodulexml> task (as is done automatically for projectized modules). The config file should be included in your product's installation, if you are shipping a complete product; or included in your module's NBM file, if you are distributing just an NBM. (To preserve some backward compatibility, the AutoUpdate client may attempt to create a config file for an NBM which does not include one, but this is not reliable - always supply one explicitly.)

Here is an example of a configuration file for the "beans" module, located in ide5/config/Modules/org-netbeans-modules-beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//NetBeans//DTD Module Status 1.0//EN"
                        "http://www.netbeans.org/dtds/module-status-1_0.dtd">
<module name="org.netbeans.modules.beans">
    <param name="autoload">false</param>
    <param name="eager">false</param>
    <param name="enabled">true</param>
    <param name="jar">modules/org-netbeans-modules-beans.jar</param>
    <param name="release">1</param>
    <param name="reloadable">false</param>
    <param name="specversion">1.14</param>
</module>

This module is declared to be a regular (not autoload or eager) module, and asked to be enabled. The path to the JAR file is given (within the ide5 module cluster), and some information about the module version is given. The above file would have been created by an Ant target similar to this:

<project default="run">
    <property name="nbantext.jar" location="..../nbbuild/nbantext.jar"/>
    <property name="cluster.dir" location="..../nbbuild/netbeans/ide5"/>
    <taskdef name="createmodulexml" classname="org.netbeans.nbbuild.CreateModuleXML">
        <classpath>
            <pathelement location="${nbantext.jar}"/>
        </classpath>
    </taskdef>
    <target name="run">
        <createmodulexml xmldir="${cluster.dir}/config/Modules">
            <enabled dir="${cluster.dir}">
                <include name="modules/org-netbeans-modules-beans.jar"/>
            </enabled>
        </createmodulexml>
    </target>
</project>

Execution API split in half

As part of the move to an Ant-based build system in NetBeans 4.0, much of the 3.6 Execution API is no longer necessary. The deprecated portion was moved to a new module (openide/execution/deprecated in CVS). Modules with a dependency on the non-deprecated classes need to upgrade their dependency declaration to

OpenIDE-Module-Module-Dependencies: org.openide.execution > 1.2

(or later). The deprecated API is still available as

OpenIDE-Module-Module-Dependencies: org.openide.execution.deprecated > 1.0

but this module is not shipped with the standard NetBeans IDE or platform.

Non-deprecated classes still available and potentially useful in 4.0 (with some additions and deprecations) include:

Deprecated classes include:

More information:

Java Comparators separation

Skip if your module does not depend on the java module (org.netbeans.modules.java).

The Comparators API (org.netbeans.api.java.comparators) was moved from the java module to the org.openide.src module. So modules that do not use it and depend on the java module are required to update their dependency on the java module to specification version 1.17 to prevent warnings in console.

Modules that use comparators have to declare their dependency on the org.openide.src module with spec version 1.3 and remove dependency on the java module or update it as was mentioned above.

Desirable Changes in Modules

You should look through changes in this section and see if they might apply to your module. If so, your module will be a better citizen of NetBeans 4.0 by making the recommended changes.


3.5 -> 3.6 Upgrade Guide

This upgrade guide outlines notable API changes between NetBeans 3.5 and 3.6.

Notable Incompatibilities and Required Changes

Check for changes here which might cause problems for your module if left uncorrected.

Window system reimplementation

The window system has been completely reimplemented. The old API remains binary compatible, but the significant UI changes required some changes in the window system APIs. There are a fair number of the APIs which have been deprecated, and new ones and replacements added; the retained APIs may not behave exactly like their older counterparts. Of particular importance is that access to almost all of the window system APIs must now be performed only on the AWT event dispatch thread. See the summary of the API changes in changes document and also especially XML layer declarations changes which is supposed to be the only (must-have) required change, it is found in the same doc.

Use regular expressions from the JRE

J2SE 1.4 includes its own regular expression support in the java.util.regex package. You should use this instead of the Apache Regexp library. Making the transition is not generally difficult:

  1. Remove any package dependencies on org.apache.regexp, or module dependencies on org.netbeans.libs.regexp, from your module's manifest. Remove regexp.jar from your compile-time classpath.

  2. Rather than RE, use Pattern.

  3. Rather than RECompiler and REProgram, just use Pattern.compile. PatternSyntaxException should be caught if the regexp was entered by the user or otherwise might be erroneous. You can pass flags (such as case-insensitive) to compile, or embed them in the regexp itself.

  4. Rather than RE.match(String), use Pattern.matcher(String).find(). Note that Matcher.matches() means that the whole string must match (anchored at both ends).

  5. If you are looking for multiple matches, or wish to retrieve captured groups etc., just make the Matcher and then call find(), start(int), end(int), group(int), etc.

  6. Rather than using StreamCharacterIterator or ReaderCharacterIterator to parse a file, memory-map it into a byte buffer using NIO, make a CharBuffer from that, and match on it.

  7. If you need to parse from an open-ended stream, it is trickier. You may try something like the following: ReaderCharSequence

Ensure the correct PropertyEnv instance controls a custom editor dialog

If you have been using ExPropertyEditor and PropertyEnv to control the OK/Cancel buttons on custom editor dialogs, note that attachEnv may be called more than once with different instances of PropertyEnv. If the custom editor is invoked more than once, ensure that the PropertyEnv the custom editor is setting the state on to control the dialog buttons is the instance passed to attachEnv immediately prior to opening the custom editor, not a cached instance from a previous dialog.

Ensure any listeners are detached from the previous PropertyEnv on subsequent calls to attachEnv().

Loaders threading semantics are now more strict

Constructors of DataObjects should not do too many things. In release36 when a constructor of a data object is running, all other threads are prevented from accessing files in the same folder to prevent calls to not yet constructed data object. This is a fix for a bug that used to be called "500ms problem".

Generally it is suggested to prevent all modifications to files (writing, deleting, renaming, etc.) in data object constructors.

PropertyPanel does not guarantee caching of property editors

PropertyPanel will no longer necessarily cache the PropertyEditor whose contents it is displaying. The method getPropertyEditor() has been deprecated. It will now fetch the property editor from the property it is displaying as needed. To ensure consistent access to the same property editor instance, the Node.Property supplying it must handle any caching necessary. ReaderCharSequence

BookmarksAction and BookmarksNode no longer installed

BookmarksAction and BookmarksNode no longer installed by the Utilities module, also meaning that the Help/Bookmarks menu is gone. If you have added bookmarks to the bookmarks submenu, add them directly to the Help menu instead.

Desirable Changes in Modules

You should look through changes in this section and see if they might apply to your module. If so, your module will be a better citizen of NetBeans 3.6 by making the recommended changes.

Actions should run in the event thread

Any subclasses of CallableSystemAction should override asynchronous:

protected boolean asynchronous() {
    return false;
}

and ensure that performAction is expecting to be run in the event thread. Direct subclasses of SystemAction or Action should expect to be have actionPerformed called in the event thread. The action may explicitly do its work in a separate thread (e.g. using RequestProcessor) if it needs to.

See: issue #35755.

Upgrade to JavaHelp 2.0

NetBeans now bundles with JavaHelp 2.0 software, which enables better merging of the TOC and index. These are the most important things to adjust for:

See the JavaHelp release notes for more information on the JavaHelp 2.0 software release.

Order of Node.Propertys in a Node.PropertySet is significant

As detailed in issue #21065, the order of properties in a property set (formerly property sheet tab, now property sheet category) is now significant; the property sheet will not automatically alphabetize properties for you. If your module produces nodes with more than one property in a given property set, pay attention to the order, and manually adjust it if you prefer.

Window System XML layer declarations changed

As detailed at Changes in XML layer declarations, it is necessary to use the newer version of the XML declaration, especially declaration of modules, to be able achieve the desired layout whitin the new UI hierarchy of new window system implementation.

TopComponent subclasses should override getPersistenceType()

Override TopComponent.getPersistenceType instead of using the deprecated client property "persistenceType". For example if you set client property PersistenceType to "never" (the window system should not store the position and contents of your TopComponent) in NB 3.5, you should override TopComponent.getPersistenceType to return the constant TopComponent.PERSISTENCE_NEVER. This property determines if, and when, the window system will store the state of your TopComponent (where it was docked, etc.).

Even if you did not set any value for the persistence type in the past, it is recommended to add this override to explicitly indicate how your TopComponent should be persisted. For backward compatibility, the default persistence type is TopComponent.PERSISTENCE_ALWAYS. For transient views such as search results or other types of output, this is not the correct behavior.

Even if permanent persistence is the right thing for a TopComponent, it should override this method rather than relying on the default remaining the same in the future.

TopComponent subclasses should override preferredID()

Override TopComponent.preferredID to tell window system ID string that identifies your TopComponent. It's good idea to always override this method to return some String name unique or special to your TopComponent subclass. Good starting point for your preferredID might be actual name of your TopComponent's subclass.

Window system uses preferredID result as part of unique TopComponent instance identification.

TopComponent activation

TopComponent.requestFocus was used to both activate and focus TopComponents in the old window system. As this caused problems, a new method, TopComponent.requestActive, was added. This method must be used to activate TopComponents in window system, causing its parent Modes to indicate activation, and causing context-sensitive actions to be enabled/disabled appropriately. requestActive will cause focus to be transferred to the TopComponent by calling TopComponent.requestFocusInWindow. Do not call TopComponent.requestFocus in isolation - it will focus to the TopComponent but will not activate the TopComponent, leaving the window system in an inconsistent state.

TopComponent focus management

If your TopComponent subclass overrides requestFocus to transfer focus to a subcomponent, it should also override requestFocusInWindow. The window system now uses requestFocusInWindow to focus TopComponent to avoid infinite loops when the WINDOW_ACTIVATED event is handled in SDI mode.

java.awt.Component.requestFocusInWindow is new in JDK 1.4. Generally it is good idea to override requestFocusInWindow in Component subclasses if you override requestFocus. For an example, see org.netbeans.core.NbMainExplorer.ExplorerTab. It is subclass of TopComponent and it overrides both requestFocus and requestFocusInWindow. ExplorerTab uses a JTree as subcomponent which should be the final recipient of focus.

Specific help IDs for Nodes' property sheets

It is now possible to provide help IDs specific to the properties of Nodes, distinct from the help ID for the node itself, via Node.getValue ("propertiesHelpID"). Help IDs may also be specified for individual property sets via PropertySet.getValue("helpID"). Only the first property set displayed is checked for this value. If your module contributes properties to another module's node, and you want to provide help specific to your properties, specify that your properties should be displayed on a different tab in the property sheet by returning a localized tab name from PropertySet.getValue("tabName") for those property sets that you are providing. Returning the same name for multiple property sets will cause them to be grouped together on their own tab in the property sheet.

If no help ID is provided either by the selected property, the first property set displayed, or the node, the help button will be disabled. The help button in the property sheet will never display the help on the node, only property-specific help. This change only affects help button in the description area of the property sheet, the help item on the property sheet's popup menu, and the help ID chosen if the user presses F1 while the property sheet has focus.

Prefer Node.Property to PropertyModel

With the property sheet rewrite, PropertyPanel is now driven primarily by Node.Property objects; use of PropertyModel is discouraged except in the specific circumstance of needing to display a property whose value may unexpectedly change due to events beyond the control of the UI. For uses of DefaultPropertyModel, use PropertySupport.Reflection instead.


3.4 -> 3.5 Upgrade Guide

This upgrade guide outlines notable API changes between NetBeans 3.4 and 3.5.

Notable Incompatibilities and Required Changes

Check for changes here which might cause problems for your module if left uncorrected.

Group functionality moved to separate module

If your module brings .group files you should change dependency of your module. Currently you declare dependency on Utilities modules in module's manifest:

OpenIDE-Module-Module-Dependencies: org.netbeans.modules.utilities/1

Group functionality was moved to separate group.jar module. You should change module's manifest to require dataload.group token:

OpenIDE-Module-Requires: dataloader.group

I.e. remove *-Module-Dependencies and add *-Requires to module's manifest, see RMI's manifest for example.

Open APIs Separation and Deprecation

Cleaning of the Open APIs by separating them into independent modules has progressed in several phases.

Phase I

Many classes in the Open APIs have been deprecated and moved to a separate module, openide-deprecated.jar. The Java Hierarchy API and a few associated classes were moved also to java-src-model.jar. The core implementations of deprecated APIs were moved to core-deprecated.jar.

Module developers who did not use any of the deprecated API classes to begin with, or who are able and willing to clean up their modules to not use them, should request:

OpenIDE-Module-IDE-Dependencies: IDE/1 > 3.14

Otherwise they may compile against openide-deprecated.jar for the time being and declare:

OpenIDE-Module-Module-Dependencies: org.openide.deprecated > 1.0
OpenIDE-Module-Requires: org.openide.TopManager

Modules which wish to use the Java Hierarchy API may compile against java-src-model.jar and declare:

OpenIDE-Module-Module-Dependencies: org.openide.src > 1.0

For compatibility, modules with no IDE/1 dependency, or a dependency older than 3.14, will be automatically given the three dependencies above at runtime. The three new modules are autoloads, so they will only be loaded if at least one module requests them (explicitly or implicitly).

See the Open APIs Changes List for a complete list of classes and packages which were deprecated. The ones which were most commonly used are:

org.openide.TopManager

All methods from this class are deprecated. Most of the getter methods can be replaced by using Lookup.getDefault().lookup(InterfaceClass.class).

Most utility methods, such as showing a URL, displaying status text, etc., have been replaced by separate utility classes with similar semantics, all of which have a getDefault() method:

  • org.openide.DialogDisplayer
  • org.openide.LifecycleManager
  • org.openide.awt.HtmlBrowser.URLDisplayer
  • org.openide.awt.StatusDisplayer
  • org.openide.loaders.RepositoryNodeFactory
  • org.openide.nodes.NodeOperation
  • org.openide.windows.IOProvider

Showing help should be done using the JavaHelp API module.

systemClassLoader() should be replaced with lookup on ClassLoader.class. currentClassLoader has no undeprecated equivalent, since it misuses the Java classpath; for now, use NbClassLoader (you may wish to add AllPermissions using setDefaultPermissions).

For details on replacing TopManager calls, see its Javadoc.

Forcible initialization of the NetBeans core using TopManager.getDefault() (in standalone applications, tests, and scripts) can in some cases be deleted, since many services are now available standalone. If it is necessary to force modules to be loaded, ask lookup for ModuleInfo.class to scan for and install modules, as well as performing other unspecified initialization activities in the core.

org.openide.Places

Everything here is deprecated. To find folders, find them by file path on the system filesystem: Repository.getDefault().getDefaultFileSystem(). Most nodes are no longer made available to modules, as they are details of the GUI implementation, but see RepositoryNodeFactory for repository().

org.openide.debugger.* and related cookies, actions, and org.openide.loaders.ExecSupport

The Debugger API is deprecated, as it never really exposed the actual power of the debuggercore module. As of this writing, a full replacement is still pending. ExecSupport may be replaced by org.openide.loaders.ExecutionSupport which provides just the ability to select an Executor, without referring to debugger types.

org.openide.cookies.ElementCookie and org.openide.loaders.DataObjectFilter

Ancient APIs used only by the old Object Browser. Should be replaced by the Looks API when available.

org.openide.cookies.ProjectCookie and related actions

Will be obsoleted by the new projects system.

Many classes in org.openide.explorer.propertysheet.editors.*, and org.openide.loaders.ExtensionListEditor

These property editor implementations are obsolete. Simply declare your properties to be of the correct type, and often a suitable editor will be found automatically. For cases where more control is needed, hints may be passed to the property editor implementation; see the Explorer API.

org.openide.options.ControlPanel, several classes in org.openide.modules.*, and org.openide.actions.HelpAction

Various classes which have not been used in the APIs or core for some time and can be considered obsolete.

Phase II

The Compiler API is now in a separate module, modules/autoload/openide-compiler.jar at compile time, org.openide.compiler > 1.0 at runtime. Any module wishing to call CompilationEngine.getDefault(), directly or indirectly, must declare a dependency on the token org.openide.compiler.CompilationEngine to ensure that the core-compiler.jar module is loaded.

The Execution API is in modules/autoload/openide-execution.jar at compile time, org.openide.execution > 1.0 at runtime. Again, the token org.openide.execution.ExecutionEngine should be required by a module in order to guarantee that ExecutionEngine.getDefault() will succeed.

InputOutput and related classes dealing with the Output Window are now called the Input/Output API, separate from the Window System API. Use modules/autoload/openide-io.jar, org.openide.io > 1.0, and again org.openide.windows.IOProvider to ensure IOProvider.getDefault() will succeed.

Module authors are encouraged to determine which of the three above APIs they really need to use, and remove gratuitous dependencies. In particular, the Compiler API is likely to be deprecated by new Projects, so frivolous uses of this API should be removed sooner rather than later. If your module really needs all three APIs, your manifest could be updated as follows:

OpenIDE-Module-IDE-Dependencies: IDE/1 > 3.17
OpenIDE-Module-Module-Dependencies: org.openide.compiler > 1.0,
  org.openide.execution > 1.0, org.openide.io > 1.0
OpenIDE-Module-Requires: org.openide.compiler.CompilationEngine,
  org.openide.execution.ExecutionEngine, org.openide.windows.IOProvider

The Terminal Emulator library is now modules/autoload/terminalemulator.jar; use a module dependency on org.netbeans.lib.terminalemulator rather than a package dependency.

To reduce the number of modules needing to depend on the Execution API, some deprecations and additions were introduced:

The new method AbstractCompileAction.prepareJobFor ought to be used when creating a compiler job based on a folder, rather than assuming that DataFolder will have a CompilerCookie.

Executor subclasses need no longer override execute(ExecInfo); ExecInfo has been deprecated (it is not classpath-safe). Executors must override execute(DataObject) instead. Callers should always use the DataObject form, never the ExecInfo form. Similarly, DebuggerType subclasses should override startDebugger(DataObject,boolean) and just implement startDebugger(ExecInfo,boolean) to throw an exception.

See the Open APIs Changes List for further details on the Phase II separation.

As with Phase I, old modules not depending on version 3.17 or later of the Open APIs will be given automatic dependencies on the separated APIs, for binary compatibility.

For more details, see issue #19443.

Phase III

The Debugger API is no longer deprecated in this phase, and is available in the new autoload module debuggercore/oldapi/netbeans/modules/autoload/openide-debugger.jar. This new module contains all of org.openide.debugger.*, plus org.openide.cookies.DebuggerCookie and org.openide.loaders.ExecSupport.

The module does not contain any actions. In the event that you were setting action performers for e.g. ToggleBreakpointAction, please note that the action implementations now reside in the org.netbeans.modules.debugger.support.actions.* package (in debuggerCore.jar); and that you need not refer directly to the action in order to set a performer - rather, modify the action map for your TopComponent (or other component) to bind e.g. "org.netbeans.modules.debugger.support.actions.ToggleBreakpointAction" to a javax.swing.Action of your choice. This is easier and safer than setting and unsetting action performers.

Modules should declare a dependency:

OpenIDE-Module-Module-Dependencies: org.openide.debugger > 1.0

Any modules depending on org.openide.deprecated prior to version 1.2 will receive this dependency automatically.

You may if you wish also:

OpenIDE-Module-Requires: org.openide.debugger.Debugger

in case your module cannot work without a debugger implementation being installed.

For more details, see issue #29914.

Indirect dependencies do not count for a module's "effective classpath"

In NetBeans 3.4, if module A exposes some API packages (either explicitly using OpenIDE-Module-Public-Packages, or implicitly by omitting this attribute entirely), and module B depends on module A, and module C depends on module B, then module C can directly refer to those packages in A. In NetBeans 3.5, this is no longer permitted; if C really wishes to refer to classes in A, it must declare an explicit dependency on A as well as on B.

This change makes the "effective classpath" for a module more apparent: just look at the listed dependencies in its manifest. It also permits B in this example to use A's API as an implementation detail: B may be exposing its own API and only using A to implement it, but not wish to expose this fact to clients. In NetBeans 3.5, B can use any other modules to implement itself without fear of "polluting" its own public API. Its clients could even use a different version of A's packages for their own purposes. If B does wish to include A's Java-level API as part of its own, it should simply state in its documentation that clients may need to declare a dependency on A as well as on B.

Similarly, in NetBeans 3.4, if module A declared some package dependencies on JARs in the startup classpath - for example regexp.jar - not only could A access these classes, but any module depending on A could access them too. In 3.5, modules can only access startup classpath packages they themselves declare an explicit dependency on.

For compatibility reasons, modules with no API dependency declaration, or depending only on APIs version 3.11 or earlier, will see the 3.4 behavior: they will be permitted to access modules they only depend on indirectly, or packages they only depend on via a module dependency. But modules declaring:

OpenIDE-Module-IDE-Dependencies: IDE/1 > 3.12

will see the new 3.5-style behavior.

To check whether your module is correctly declaring every dependency, make sure your bin/ide.cfg does not contain -J-Xverify:none (dev builds are OK, but change this in release builds), then start NetBeans with the flag -J-Dnetbeans.preresolve.classes=true and check the log file for any problems.

For more details, see issue #27853.

Desirable Changes in Modules

You should look through changes in this section and see if they might apply to your module. If so, your module will be a better citizen of NetBeans 3.5 by making the recommended changes.

Use InstalledFileLocator

If your module needs to search for a related file in the NetBeans installation, generally something packed into its NBM, prior to 3.5 there was no supported way of finding such a file. Now there is: org.openide.modules.InstalledFileLocator can find files installed alongside the module. Modules which searched undocumented system properties such as netbeans.home to find associated files should be changed to use the new service.

File f = InstalledFileLocator.getDefault().locate(
    "docs/my-docs/index.html", "org.netbeans.modules.myself", true);
if (f != null) {
    HtmlBrowser.URLDisplayer.getDefault().showURL(f.toURL());
}

If you are looking for the module JAR itself, you may still prefer to use this idiom:

File jar = new File(ThisModuleClass.class.getProtectionDomain().
                    getCodeSource().getLocation().getFile());

When using this new service, be sure to depend on it:

OpenIDE-Module-IDE-Dependencies: IDE/1 > 3.21
OpenIDE-Module-Requires: org.openide.modules.InstalledFileLocator

For more details, see issue #28683.

Use EditorCookie.Observable

It was not possible to listen on changes in state of document before the NetBeans 3.5. Typical workaround was to cast object implementing EditorCookie to CloneableEditorSupport and use its state change listener. This however did not work in case when implementation of EditorCookie was based on EditorSupport.

The newly added org.openide.cookies.EditorCookie.Observable should eliminate this deficiency. It extends org.openide.cookies.EditorCookie and adds possibility to listen on changes of several document states:

Recommended change for authors of editors is to implement directly EditorCookie.Observable instead of EditorCookie only. The CloneableEditorSupport (and also old deprecated EditorSupport) was extended to provide all necessary implementation.

Recommended change for clients which need to listen on document state changes and which were using CloneableEditorSupport till now is to replace the code and use EditorCookie.Observable. All NetBeans editors were modified to implement it and provide this cookie.

When using this new class, be sure to depend on correct version:

OpenIDE-Module-IDE-Dependencies: IDE/1 > 3.40

For more details, see issue #31101.


3.3 -> 3.4 Upgrade Guide

This upgrade guide outlines notable API changes between NetBeans 3.3 and 3.4.

Notable Incompatibilities

Check for changes here which might cause problems for your module if left uncorrected.

XML MIME types

While not strictly speaking a compatibility issue, as it does not involve behavior specified by the Open APIs, developers whose modules operate on XML files should take note of a change in the NetBeans XML Core module from 3.3 to 3.4. Previously this module was configured to recognize all *.xml files (and a few other extensions) by default, as well as any file object marked with the MIME type text/xml.

In NetBeans 3.4, the module will also recognize application/xml and in fact any MIME type matching the pattern */*+xml. Therefore, other modules which recognize certain subtypes of XML file might find the 3.4 data loader picking up the files instead. This is manifested by the files appearing with the generic XML icon: XML data object icon

To correct the problem, you should just need to ensure that your manifest explicitly asks for your loader to take priority:

Name: my/module/MyDataLoader.class
OpenIDE-Module-Class: Loader
Install-Before: org.openide.loaders.XMLDataObject, org.netbeans.modules.xml.core.XMLDataObject

Even without changes to the module, end users can manually work around the problem if it occurs by reordering Object Types in the Options window to ensure that your object type appears above XML Files.

For more details, see issue #25163.

Use of libraries from classpath

Previous versions of NetBeans permitted modules to freely use any libraries found in the startup classpath without declaring a dependency. This was dangerous, since the dependency declaration encapsulates information about what libraries the module uses. When a dependency fails, the failure can be presented politely to the user, rather than throwing a NoClassDefFoundError at runtime.

To enforce the rule, NetBeans 3.4 will refuse to serve certain classpath libraries to modules which do not declare a proper dependency. Additionally, the JavaHelp library was moved into a module in NetBeans 3.4; previously it was accessible on the classpath.

One benefit besides safety is that modules which do not declare dependencies on the classpath versions of these packages are free to use specific other versions simply by adding suitable JARs to their Class-Path manifest attribute.

In particular, here are the proper declarations you will need for each package:

Required Dependency Declarations in NetBeans 3.4
Package(s) Dependency Notes
org.netbeans.core.**, org.netbeans.beaninfo.** OpenIDE-Module-Module-Dependencies: org.netbeans.core/1 Using any such packages is very dangerous and compatibility for your module in future releases is not assured.
org.netbeans.lib.terminalemulator.* OpenIDE-Module-Package-Dependencies: [org.netbeans.lib.terminalemulator.TermListener]
org.apache.regexp.* OpenIDE-Module-Package-Dependencies: [org.apache.regexp.RE] Use the Regexp autoload module or JDK 1.4's built-in regexps in NB 3.6.
org.apache.crimson.subpackage.* OpenIDE-Module-Package-Dependencies: org.apache.crimson.subpackage[SampleClass] > 1.1 Declare the particular package(s) you are using, e.g. org.apache.crimson.tree. You are living dangerously if you depend on a particular XML parser.
org.apache.xerces.** OpenIDE-Module-Package-Dependencies: [org.apache.xerces.SampleClass] Again, using such a dependency is strongly discouraged. Consider using the Xerces parser autoload module instead. (Note: not available in NetBeans 3.4; added to the build for subsequent releases.)
org.apache.xml.serialize.* OpenIDE-Module-Package-Dependencies: [org.apache.xml.serialize.EncodingInfo] This is safer than depending on the Xerces parser proper, but it is still best to avoid such a dependency if possible. Consider using XMLUtil.write(...) instead, assuming you do not need two-way editing (preservation of whitespace).
javax.help.** OpenIDE-Module-Module-Dependencies: org.netbeans.modules.javahelp/1 > 1.0 Note that this dependency will not be satisfiable in NetBeans 3.3. More information
org.w3c.dom.**, org.xml.sax.**, javax.xml.** (no dependencies required) These API classes are available to all modules automatically. There is no guarantee that implementations of full JAXP will be available, however; but you may request this: see JAXP information for more details.

For more details, see issue #19622.

Desirable Changes in Modules

You should look through changes in this section and see if they might apply to your module. If so, your module will be a better citizen of NetBeans 3.4 by making the recommended changes.

Declare public packages

As of NetBeans 3.4, it is possible to declare Java packages in your module which provide a public API (or SPI - Service Provider Interface) to other modules. By default, as in previous releases, all Java packages in your module are considered fair game to a module depending on your module, subject to Java access restrictions.

Now you can declare that only certain packages are to be considered part of the API of your module and accessible to others. Classes in other packages will be inaccessible outside your module, even if they are public. This declaration helps enforce separation of interface from implementation. Consult the Modules API for details; an example:

OpenIDE-Module-Public-Packages: org.netbeans.api.myapi.*

Modules with an official, documented, maintained API are encouraged to use this new declaration to enforce it. Modules which intentionally have no API they wish to expose to other modules can also declare this:

OpenIDE-Module-Public-Packages: -

See the Versioning Policy for suggested guidelines on providing an API from a module.

Purify execution classpath

Previous versions of NetBeans tended to include too many items in the classpath used by user applications. Specifically, external compilation and execution would include in the classpath libraries used by NetBeans itself. This was convenient in a few cases but usually undesirable, sometimes a source of great confusion. In NetBeans 3.3, the default classpath for the external Java compiler was purified to include only the JRE's own libraries. In NetBeans 3.4, the change was completed by removing extraneous items from the classpath for the external Java executor.

However the Open APIs code did not change, and only the standard executor was changed. Modules may provide customized executors by subclassing ProcessExecutor, for example. They then typically construct a command parameter to include a -classpath argument. Executors defined by older modules may still include NetBeans' own startup and library paths, or even the JRE boot classpath, as derived from various methods in NbClassPath. For consistency with the new executor style, all these extraneous paths should be removed in the default executor configuration. What should remain:

  1. The filesystems path, from NbClassPath.createRepositoryPath(FileSystemCapability.EXECUTE). This can be configured by users simply by mounting or unmounting filesystems.

  2. Optionally: a custom additional Classpath, by default blank, which will be appended to the filesystems path. While the user could just directly edit the External Process command template, it is more convenient to leave it untouched and use the custom property editor dialog for NbClassPath.

Compiler and debugger type classpaths should also be checked to ensure they are by default free of any libraries used by the NetBeans IDE.

If you have a library shipped as part of your module's NBM which can be used by user applications and which you feel is important to average users of your module, you may include it in the default classpaths used for execution, compilation, and debugging. Just create a declarative mount: see Move settings to layers: Specification filesystem instances for an explanation. Users may simply unmount this library if they are not interested in using it.

Require a JavaHelp implementation

In NetBeans 3.3, the Modules API specified that you could install help sets conforming to the JavaHelp specification. In NetBeans 3.4, in the interests of greater modularity, JavaHelp is handled by an independent module. In order to ensure that this module is enabled when it is needed (it is an autoload), all modules with help sets should declare in their manifest:

OpenIDE-Module-Requires: org.netbeans.api.javahelp.Help

For compatibility, older modules will automatically be considered to require JavaHelp. But newer modules (distinguished by requesting a post-2.2 specification version using OpenIDE-Module-IDE-Dependencies) must explicitly ask for JavaHelp support if they want it.

Any module referring directly to the JavaHelp Java-level APIs in javax.help.*, rather than simply installing a help set, must declare:

OpenIDE-Module-Module-Dependencies: org.netbeans.modules.javahelp/1 > 1.0

This should be given in addition to or instead of the OpenIDE-Module-Requires above.

For more information, see issue #19620 and issue #27776.

Move manifest nodes to layers

Even more types of module-provided settings can in 3.4 be registered via XML layer rather than in manifest. Layers are more flexible and brandable.

The basic definition of how settings in layers work is given in the Services API.

Here is list of types of nodes that might formerly have been present in a manifest which should be moved:

RequestProcessor API changes

In NetBeans 3.3, performance and thread analysis revealed two problems with RequestProcessor usage. One was that the default post(Runnable) call forced serialization of requests, even when that was not the explicit intent, which often produced deadlocks between two requests. Another problem was that users of private RequestProcessor instances (a common way to avoid the abovementioned deadlocks) were inadvertently getting a dedicated thread each, which was a lot of overhead.

For NetBeans 3.4, newer calls make it easier to post tasks exactly how you want to post them, and still avoid unnecessary thread creation. The most common change module authors should do is to eliminate unnecessary serialization.

// OLD WAY:
public void somethingChanged(TheEvent ev) {
    RequestProcessor.postRequest(new Runnable() {
        public void run() {
            // Do this asynchronously, i.e. do not run in the listener callback.
            // But no one cares exactly when you do it, just soon.
            reactToTheChange();
        }
    });
}
// CAN OFTEN BE REPLACED BY:
public void somethingChanged(TheEvent ev) {
    RequestProcessor.getDefault().post(new Runnable() {
        public void run() {
            // Will still be run asynchronously, but won't deadlock if
            // it happens to waitFinished() on some other RP task.
            reactToTheChange();
        }
    });
}

For details on the various ways to start an asynchronous task and an explanation of when you would want to use one style over the other, see the RequestProcessor class Javadoc.

Enable auto update of module without restart

There is new feature in the Auto Update client, allowing installation of many modules without restarting the IDE. This quick installation can be applied only to a module NBM which:

If your module has no particular reason to require a restart of NetBeans before it can be used, you are encouraged to set this flag to false: the default is true for compatibility reasons.

Add a description for the selected node in the Explorer

Each node should have a description to characterize the node's purpose and use in the IDE for the end user. This description is shown in the "mini status bar" in the Explorer panel in the IDE. Each module should add a description for each type of node it provides.

There are two ways to make such a description appear.

  1. (Preferred) If a node has a short description (Node.getShortDescription()) then this is used in the mini status bar as well as in the node's tool tip. So for most nodes, you can just override getShortDescription() to return the desired localized text. If the node already had a tool tip and it is acceptable, no change is needed.

  2. Sometimes a node will already have a short description used in the tooltip, but it is not appropriate for the mini status bar. Or no tool tip is desired at all. In such cases it is possible to provide a distinct status bar message. Just use the nodeDescription attribute:

    public MyNode() {
        super(Children.LEAF);
        setValue("nodeDescription",
                 NbBundle.getMessage(MyNode.class, "DESC_MyNode"));
    }
    

For a quick summary of which nodes are still missing descriptions, run NetBeans with the command-line switch -J-Dnetbeans.log.node.description=true, exercise relevant features of the IDE, and watch the log file.

More information can be found in issue #9940.

JAR Packager - registration of ArchiveBuilders, extended properties moved to layer

Relevant to modules which programmatically extend the capabilities of the JAR Packager. For details see issue #21323.

Change in format of DataShadows

Before 3.4 the only way to create DataShadows (symbolic links) was to create a file with the extension .shadow containing two lines of text. The first line contains the name of the linked-to file. The second line contains the (system) name of the filesystem in which the linked-to file resides. If the second line is omitted all filesystems are searched for the linked file.

For 3.4 a new preferred format was introduced. The *.shadow file is allowed to be empty (byte length zero). If so then the FileObject should have the attribute originalFile and optionally also originalFileSystem, both of value type String. The second attribute may be omitted if the filesystem on which the *.shadow file resides is to be searched for the linked file, which is the commonest case.

The change was designed primarily for XML layers forming the system filesystem. It is no longer necessary to use a CDATA section for the shadow contents, which is a bit cleaner to write and more memory-efficient.

The old style:

<folder name="FooFolder">
            <file name="the.shadow">                
                <![CDATA[BarFolder/original.file
SystemFileSystem
]]>
            </file>
        </folder>

The new preferred style:

<folder name="FooFolder">
    <file name="the.shadow">
        <attr name="originalFile" stringvalue="BarFolder/original.file"/>
        <!-- optional: -->
        <attr name="originalFileSystem" stringvalue="SystemFileSystem"/>               
    </file>
</folder>

Eager modules

NetBeans 3.3 introduced autoload modules which are suitable for situations where the module provides no inherent functionality but is just used as a library for the benefit of other modules. In NetBeans 3.4 you may also have eager modules, or "bridges". These modules cannot be turned off by the user, unless they depend on normal modules which are turned off. This is appropriate for small modules which simply provide bridging functionality between two or more major modules.

For example, i18n-form.jar is turned on only when i18n.jar and form.jar are both turned on; it lets the I18N support understand how to work with Form Editor files and guard blocks. The user might want to disable I18N support, or might want to disable GUI form support, but if both of these are enabled then they are expected to cooperate. The existence of a separate module to bridge them is only an implementation detail: it permits the two major modules to be enabled independently of one another, by separating out the dependency. Therefore it is made eager.

You can easily make a module eager. Just place it in the modules/eager/ folder rather than modules/.

Use new open/close/show/hide methods for TopComponent

There are several new protected notification methods in TopComponent:

When added to the existing componentActivated and componentDeactivated methods, this means that if you have a custom component, you can more precisely control its resource management than before.

For example, your component might need to perform heavyweight initialization when it is displayed - opening files, parsing them, and so on. In NetBeans 3.3, if your component was open but in not showing in a multitab container after a restart of NetBeans, it would need to perform all this work when the window system was opened - even though the user was not actually looking at your component at all. Now you can override componentShowing to perform this initialization on demand. If the tab is not selected by the user, or not selected for a while, the initialization can be deferred, improving startup time and perhaps reducing memory use.

Provide final children list for a node

Children of a node are often initialized completely when Children.addNotify is called. But for some cases, the creation of children can be time-consuming or use critical threads, and so it is done asynchronously. For example, objects inside a folder are recognized soon after the folder is expanded, but not synchronously, in case the folder is very large. As another example, Java sources show a Please wait... node until they are parsed.

While desirable in the Explorer GUI, such behavior can be troublesome for other purposes. Scripting and automated testing are made much more complicated if the proper subnodes are not available at once. MenuView (used for some popup menus) also suffered from Swing display problems associated with dynamic changes to menu contents.

So for NetBeans 3.4, there is a new method Children.getNodes(true) which you may override. By default it behaves identically to Children.getNodes(). However, if your node adds subnodes after a delay, you are strongly encouraged to override it. The implementation ought to block, perform whatever calculations it needs, and provide a complete and finalized children list before returning. (The children may still be changed later in response to various events, of course.)

Supply TemplateWizard.Iterator as a cookie

In NetBeans 3.3, custom template wizard iterators (panel sequences for the New wizard) had to be declared using the file attribute templateWizardIterator on the template's primary file. This was generally written into the layer. There were several problems with this when used for wizards applicable generally to any template of a given object type, such as the several panels used on Java classes:

For NetBeans 3.4, you may instead supply TemplateWizard.Iterator as a cookie from a DataObject subclass you create. Then the wizard will by default be available to all templates of that type. No file attribute is needed.

Use of the file attribute is still appropriate for cases where the wizard iterator is only applicable to a certain special template designed to launch that wizard, and not to all templates of that data object type. You can also override the cookie with the file attribute if you need a special wizard for just a few templates.

Map file objects to URLs

Before NetBeans 3.4, it was hard to find a good URL corresponding to a given FileObject. nbfs: protocol was available for all file objects, but this protocol is useless outside NetBeans (e.g. for displaying to an external web browser). file: and jar: URLs could be constructed for some file objects manually, but not very reliably.

In 3.4, the new class org.openide.filesystems.URLMapper may be used to declare helpful URL equivalents for file objects. For example, an HTTP-based filesystem could register a mapper producing http: URLs for its files. You can use static methods to find the best URL for a given file object.

Code which constructed URLs in an ad-hoc manner should be upgraded to use the new utility methods. Implementors of custom FileSystems are encouraged to supply mappers meaningful to files residing in filesystems of that type.

Adjust New wizard template folder order

The folders in the New wizard were rearranged for NetBeans 3.4 in various ways. If you supply your own template folder beneath Templates/ in your layer, check the resulting order in NetBeans 3.4 and adjust it to look better if necessary.

Stop using FileSystemCapabilities

The next major version of NetBeans will ruin the assumption that roots of classpath are in filesystem roots; roots of classpath will be defined explicitly by settings. To ease migration a new ClassPath API was introduced in NetBeans 3.4, which we commited to make work even with the new project infrastructure.

If you are using FileSystemCapabilities.{COMPILE|DEBUG|EXECUTE} to search for resources, or to produce FileObjects you can construct classpath information from, you should consider using corresponding calls to ClassPath API. The old approach still works in NetBeans 3.4 and 3.5, but will break in NetBeans 4.0. See ClassPath API documentation online for more information.


3.2 -> 3.3 Upgrade Guide

This upgrade guide outlines notable API changes between NetBeans 3.2 and 3.3.

Notable Incompatibilities

There is only one incompatibility which is likely to be relevant.

MultiDataObject.cookieSet (getter and setter) are now protected rather than public, affecting compilability of sources (though binary compatibility is retained). See the change summary for the rationale for this change and suggested workarounds.

A similar change was also made to AbstractNode.cookieSet which is likely to have a smaller impact.

Desirable Changes in Modules

This section lists changes desired when upgrading to 3.3 from 3.2, in addition to older changes.

Move settings to layers

Many types of module-provided settings can in 3.3 be registered via XML layer rather than in manifest. Besides permitting greater flexibility, there are performance benefits to doing this.

The basic definition of how settings in layers work is given in the Services API.

Here are the things that might formerly have been present in a manifest which should be moved:

For most of these cases (especially system options, services, filesystems, and JavaHelp) there are copious examples to be found in the layers of standard NetBeans modules in development sources. Do not forget to also look at the localizing bundles referred to by the layers, and the settings files themselves which are normally linked to with the url="..." attribute of <file> for performance reasons (inline file contents are a bit slower to parse). For example, ant/src/org/apache/tools/ant/module/resources/AntModuleLayer.xml has these relevant entries:
<!-- Not the complete layer, just interesting pieces: -->
<folder name="Services">
    <!-- Register two compiler types and an executor into the default service pool. -->
    <folder name="CompilerType">
        <!-- See ant/src/org/apache/tools/ant/module/resources/AntCompiler.settings for contents. -->
        <file name="org-apache-tools-ant-module-AntCompiler.settings" url="AntCompiler.settings">
            <!-- Make any changes specific to the project. -->
            <attr name="SystemFileSystem.layer" stringvalue="project"/>
            <!-- Give it a nice display name; otherwise the service has to be actually loaded in order -->
            <!-- to display its icon. See ant/src/org/apache/tools/ant/module/run/Bundle.properties. -->
            <!-- The key will be named "Services/CompilerType/org-apache-tools-ant-module-AntCompiler.settings". -->
            <attr name="SystemFileSystem.localizingBundle" stringvalue="org.apache.tools.ant.module.run.Bundle"/>
            <!-- Give it a nice icon, as above. -->
            <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/apache/tools/ant/module/resources/AntIcon.gif"/>
        </file>
        <file name="org-apache-tools-ant-module-IndirectAntCompiler.settings" url="IndirectAntCompiler.settings">
            <attr name="SystemFileSystem.layer" stringvalue="project"/>
            <attr name="SystemFileSystem.localizingBundle" stringvalue="org.apache.tools.ant.module.run.Bundle"/>
            <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/apache/tools/ant/module/resources/AntIcon.gif"/>
        </file>
    </folder>
    <folder name="Executor">
        <file name="org-apache-tools-ant-module-AntExecutor.settings" url="AntExecutor.settings">
            <attr name="SystemFileSystem.localizingBundle" stringvalue="org.apache.tools.ant.module.run.Bundle"/>
            <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/apache/tools/ant/module/resources/AntIcon.gif"/>
            <attr name="SystemFileSystem.layer" stringvalue="project"/>
        </file>
    </folder>
    <!-- A system option. -->
    <file name="org-apache-tools-ant-module-AntSettings.settings" url="AntSettings.settings">
        <attr name="SystemFileSystem.localizingBundle" stringvalue="org.apache.tools.ant.module.Bundle"/>
        <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/apache/tools/ant/module/resources/AntIcon.gif"/>
    </file>
</folder>
<folder name="Templates">
    <folder name="Services">
        <!-- Make it possible for the user to create new services too. -->
        <folder name="CompilerType">
            <!-- Use the same initial settings as for the default instance in the pool. -->
            <file name="org-apache-tools-ant-module-AntCompiler.settings" url="AntCompiler.settings">
                <!-- They have to be marked as templates, or they will not be included in the wizard. -->
                <attr boolvalue="true" name="template"/>
                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.apache.tools.ant.module.run.Bundle"/>
                <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/apache/tools/ant/module/resources/AntIcon.gif"/>
            </file>
            <file name="org-apache-tools-ant-module-IndirectAntCompiler.settings" url="IndirectAntCompiler.settings">
                <attr boolvalue="true" name="template"/>
                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.apache.tools.ant.module.run.Bundle"/>
                <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/apache/tools/ant/module/resources/AntIcon.gif"/>
            </file>
        </folder>
        <folder name="Executor">
            <file name="org-apache-tools-ant-module-AntExecutor.settings" url="AntExecutor.settings">
                <attr boolvalue="true" name="template"/>
                <attr name="SystemFileSystem.localizingBundle" stringvalue="org.apache.tools.ant.module.run.Bundle"/>
                <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/apache/tools/ant/module/resources/AntIcon.gif"/>
            </file>
        </folder>
    </folder>
</folder>
<folder name="UI">
    <folder name="Services">
        <!-- Display the system option under the "Building" category in Options. -->
        <folder name="Building">
            <!-- Point to the real settings file (always on the system filesystem). -->
            <file name="org-apache-tools-ant-module-AntSettings.shadow">
                <![CDATA[Services/org-apache-tools-ant-module-AntSettings.settings
SystemFileSystem
]]>
            </file>
        </folder>
    </folder>
</folder>
<folder name="Mount">
    <!-- Add a Javadoc default mount. The user can unmount it but not customize it. -->
    <folder name="Javadoc">
        <!-- The file name does not matter but should be unique. -->
        <file name="org-apache-tools-ant-module-javadoc.xml">
            <!-- The contents could also be given in a separate file. -->
            <![CDATA[<?xml version="1.0"?>
<!-- The DOCTYPE's public ID is mandatory for the document to be processed. -->
<!DOCTYPE Javadoc PUBLIC
          "-//NetBeans IDE//DTD JavadocLibrary//EN"
          "http://www.netbeans.org/dtds/JavadocLibrary-1_0.dtd">
<Javadoc>
  <!-- This is a path inside the NetBeans installation directory. -->
  <!-- See the ant/release/ folder in sources. -->
  <Archive name="docs/ant-api.zip"/>
</Javadoc>
]]>
            <!-- Nice name and icon to show in Filesystems Settings. -->
            <attr name="SystemFileSystem.localizingBundle" stringvalue="org.apache.tools.ant.module.resources.Bundle"/>
            <attr name="SystemFileSystem.icon" urlvalue="nbresloc:/org/apache/tools/ant/module/resources/AntIcon.gif"/>
        </file>
    </folder>
</folder>

The following cannot be moved from a layer:

Define workspaces and windows in layers

If your module for 3.2 defined its own workspaces or modes, or otherwise performed some sort of setup of the window system from the module installer, it should be converted in 3.3 to define these things using an XML layer. There are several benefits to doing so:

There is a migration guide available to help you convert old procedural code to the new XML style. Full details of the XML format and behavior are given in the Window System API.

Avoid persisting closed TopComponents

Before NetBeans 3.3, it was normal for all top components to be persistent in the user's project, even closed ones. This was not always desirable since some components cannot be properly serialized; and others can but there is no need to store them all the time, they just add clutter to user settings. In the new window system, modules have an option to disable persistence of their top components after project switch or IDE shutdown. (The default is still to persist all components, for compatibility.)

If your top component either cannot be properly serialized and deserialized because it includes critical transient information, or for UI reasons it is undesirable to ever keep it open after a switch in the window system, you can add a Swing client property to it giving the window system this hint:

putClientProperty("PersistenceType", "Never"); // NOI18N

If you wish for it to be saved when it is open (at the time of project switch or IDE shutdown) but no information about it should be kept after it is closed, use:

putClientProperty("PersistenceType", "OnlyOpened"); // NOI18N

For efficiency during IDE or project open and in the interests of avoiding clutter in the user directory, it is recommended that most top components use one of these hints (normally OnlyOpened).

Warning: this is only a hint which may not be respected in future releases; so for safety TopComponents which cannot be safely deserialized at all should still writeReplace to null, so as to avoid problems in a future release.

Localize manifests with bundles

In 3.2, all metainformation about a module such as its display name needed to be kept in its manifest. There was a facility for localizing human-visible strings, using variants on attribute keys; unfortunately the JRE does not current support non-ASCII characters in manifests, so this was not very useful. For 3.3, all localizable strings formerly in manifests ought to be moved to resource bundles, where they can be properly localized. This feature is turned on by a new manifest attribute pointing to the bundle.

There is an existing document describing how to do this conversion: Manifest Localization Guide

Install JavaHelp help sets with layers

Before 3.3, there was only one way to add JavaHelp documentation for a module, using the Modules API. You included a helpset in the module and then used the OpenIDE-Module-Description attribute to refer to it. For NetBeans 3.3 JavaHelp functionality can be registered via layer instead of manifest, with several advantages:

To use this new system effectively, first remove the obsolete manifest declaration. Now in your layer you need to add three items:

  1. An instance to the help set itself, conventionally done with a special DTD used for this purpose and underneath Services/JavaHelp/. This registers the existence of the help set, and controls its merging into the master help set.

  2. A shortcut to the help set (typically to its home ID), conventionally using another special DTD and placed under Menu/Help/HelpShortcuts/. This registers the menu item including its name and icon, and controls ordering.

  3. The same shortcut in the actions pool, conventionally under Actions/Help/, permitting users to copy the help set reference elsewhere and safely customize their shortcuts menu knowing it will still be available here.

Here is an excerpt from the API Support module's layer demonstrating these things. Note that the usersguide module (Online Help) provides a few marker files in its layers which help sort other help sets and menu items into the "general zone": the IDE's main Online Help itself comes first, then a separator (in the menu), then module help sets (or menu items), then another separator, then module help sets (or menu items) which represent "third-party" or bundled documentation (such as the Ant User Manual included in the Ant support module).

<filesystem>
    <folder name="Services">
        <!-- Register the HelpSet in lookup: -->
        <folder name="JavaHelp">
            <!-- Merge it after the main online help: -->
            <attr name="org-netbeans-modules-usersguide-above-regular.txt/org-netbeans-modules-apisupport-helpset.xml"
                  boolvalue="true"/>
            <!-- This DTD gives an instance of HelpSet: -->
            <file name="org-netbeans-modules-apisupport-helpset.xml">
                <![CDATA[<?xml version="1.0"?>
<!DOCTYPE helpsetref PUBLIC
          "-//NetBeans//DTD JavaHelp Help Set Reference 1.0//EN"
          "http://www.netbeans.org/dtds/helpsetref-1_0.dtd">
<!-- By default it is set to merge. nbdocs: looks in the module JAR(s) and docs/ folder. -->
<helpsetref url="nbdocs:/org/netbeans/modules/apisupport/help/HelpSet.hs"/>
]]>
            </file>
            <!-- And merge before additional helpsets such as the Ant Manual: -->
            <attr name="org-netbeans-modules-apisupport-helpset.xml/org-netbeans-modules-usersguide-below-regular.txt"
                  boolvalue="true"/>
        </folder>
    </folder>
    <folder name="Menu">
        <folder name="Help">
            <!-- Add a menu item in Help | Help Sets: -->
            <folder name="HelpShortcuts">
                <!-- Again some ordering: -->
                <attr name="org-netbeans-modules-usersguide-sep.instance/org-netbeans-modules-apisupport-mainpage.xml"
                      boolvalue="true"/>
                <!-- Instance of a menu item: -->
                <file name="org-netbeans-modules-apisupport-mainpage.xml">
                    <![CDATA[<?xml version="1.0"?>
<!DOCTYPE helpctx PUBLIC
          "-//NetBeans//DTD Help Context 1.0//EN"
          "http://www.netbeans.org/dtds/helpcontext-1_0.dtd">
<!-- Give the help ID to open. By default shows master help set, you can instead -->
<!-- show just this help set if you prefer: -->
<helpctx id="org.netbeans.modules.apisupport.HOMEID"/>
]]>
                    <!-- Make it appear with a nice name and icon. -->
                    <attr name="SystemFileSystem.localizingBundle"
                          stringvalue="org.netbeans.modules.apisupport.resources.TemplateNames"/>
                    <attr name="SystemFileSystem.icon"
                          urlvalue="nbresloc:/org/netbeans/modules/apisupport/resources/ShowAPIJavadocIcon.gif"/>
                </file>
                <attr name="org-netbeans-modules-apisupport-mainpage.xml/org-netbeans-modules-usersguide-lower-sep.instance"
                      boolvalue="true"/>
            </folder>
        </folder>
    </folder>
    <folder name="Actions">
        <!-- Add to actions pool too: -->
        <folder name="Help">
            <!-- No need for any special ordering here. -->
            <file name="org-netbeans-modules-apisupport-mainpage.xml">
                <![CDATA[<?xml version="1.0"?>
<!DOCTYPE helpctx PUBLIC
          "-//NetBeans//DTD Help Context 1.0//EN"
          "http://www.netbeans.org/dtds/helpcontext-1_0.dtd">
<helpctx id="org.netbeans.modules.apisupport.HOMEID"/>
]]>
                <attr name="SystemFileSystem.localizingBundle"
                      stringvalue="org.netbeans.modules.apisupport.resources.TemplateNames"/>
                <attr name="SystemFileSystem.icon"
                      urlvalue="nbresloc:/org/netbeans/modules/apisupport/resources/ShowAPIJavadocIcon.gif"/>
            </file>
        </folder>
    </folder>
</filesystem>

(In your module it would be better to use url="..." to include these XML files separately rather than inline, which is a little faster.)

Do not forget also that the localized filenames need bundle entries, here from apisupport/src/org/netbeans/modules/apisupport/resources/TemplateNames.properties:

Menu/Help/HelpShortcuts/org-netbeans-modules-apisupport-mainpage.xml=OpenAPIs Support
Actions/Help/org-netbeans-modules-apisupport-mainpage.xml=OpenAPIs Support

The helpset itself must be accessible with the nbdocs protocol. While it still works to copy helpsets unpacked to the docs/ directory of the IDE installation, this practice is discouraged. In most cases using a JavaHelp helpset off-line is not important to users anyway. The proper thing to do is to keep the helpset available from the module classloader, i.e. its JAR, or more cleanly in an extension JAR (or ZIP). Conventionally these go in the modules/docs/ directory. So for example include this in your module manifest:

Class-Path: docs/apisupport-manual.jar

where modules/docs/apisupport-manual.jar contains the complete helpset, starting from the resource org/netbeans/modules/apisupport/help/HelpSet.hs. In such a case you can use the nbresloc rather than nbdocs protocol in the helpset registration URL.

Details of JavaHelp installation are given in the JavaHelp Integration API.

Ability to turn modules on and off, list all modules

In NetBeans 3.2 there was no API-level access to the set of installed modules nor any control over them. In 3.3 there is limited information and control available. Specifically you can:

For purposes of upgrading existing functionality, the last point is the most significant: if in NetBeans 3.2 your module attempted to manually turn off pieces of its GUI upon finding that some critical thing it needed was missing, for 3.3 this check should be done in ModuleInstall.validate().

See the Modules API - Installers for information on using validate, and the Modules API - List of modules for information on the other new features.

Use annotations to control GUI of editor

As of NetBeans 3.3 there is a new API which permits any module to interact with the GUI of the editor, specifically by making marks called annotations in the margin of the editor, highlighting complete lines or fragments of text, displaying tool tips and actions, and several other abilities. This is documented in the Editor API.

If in NetBeans 3.2 you are using the now-deprecated method Line.markError or one of several similar calls to indicate erroneous places in a document, you should for 3.3 create an annotation representing your error and attach it to the line instead. This is much more flexible. A sample implementation of an error annotation including how to attach and detach it at the proper times can be found in ant/src/org/apache/tools/ant/module/run/OutputWriterOutputStream.java. An excerpt from this class showing the essential code follows:

/** This is called when a complete line of process output is collected.
 * It either prints it plain, or prints it with a hyperlink which when
 * clicked opens the offending source and attaches an error annotation.
 * 'writer' is an OutputWriter from a tab in the Output Window.
 */
private void flushLine(String l) throws IOException {
    // ...
    Hyperlink link = /* create one based on erroneous line, or null */;
    if(link != null) {
        writer.println(link.getMessage(), link);
    } else {
        writer.println(l);
    }
}

/** Call this from ModuleInstall.uninstalled() to clean up. */
public static void detachAllAnnotations() {
    synchronized (hyperlinks) {
        Iterator it = hyperlinks.iterator();
        while (it.hasNext()) {
            ((Hyperlink)it.next()).destroy();
        }
    }
}
private static final Set hyperlinks = new WeakSet(); // Set<Hyperlink>

/** This is the annotation itself, which also knows how to behave in the
 * Output Window, and when to attach and detach itself.
 */
private static final class Hyperlink extends Annotation implements OutputListener, PropertyChangeListener {
    private FileObject file; // file to jump to
    private int line1, col1, line2, col2; // line/col number to jump to, 0-based, -1 for unspecified
    private String message; // message it is saying, or null
    private boolean dead = false; // whether it has been destroyed
    /** Create it based on some parsing of the erroneous line, etc. */
    Hyperlink(FileObject file, int line1, int col1, int line2, int col2, String message) {
        this.file = file;
        this.line1 = line1;
        this.col1 = col1;
        this.line2 = line2;
        this.col2 = col2;
        this.message = message;
        synchronized (hyperlinks) {
            hyperlinks.add(this);
        }
    }
    /** See detachAllAnnotations() above. */
    void destroy() {
        doDetach();
        dead = true;
    }
    /** Get a message suitable for printing in the Output Window, based on error, line #, etc. */
    public String getMessage() { /* ... */ }
    // OutputListener methods:
    public void outputLineAction(OutputEvent ev) {
        if (dead) return;
        if (! file.isValid()) {
            Toolkit.getDefaultToolkit().beep();
            return;
        }
        if (message != null) {
            TopManager.getDefault().setStatusText(message);
        }
        try {
            DataObject dob = DataObject.find(file);
            EditorCookie ed = (EditorCookie) dob.getCookie(EditorCookie.class);
            if (ed != null) {
                if (line1 == -1) {
                    // OK, just open it.
                    ed.open();
                } else {
                    // Open the document, add the annotation, jump to the right line.
                    ed.openDocument();
                    Line l = ed.getLineSet().getOriginal(line1);
                    if (! l.isDeleted()) {
                        attachAsNeeded(l);
                        if (col1 == -1) {
                            l.show(Line.SHOW_GOTO);
                        } else {
                            l.show(Line.SHOW_GOTO, col1);
                        }
                    }
                }
            } else {
                Toolkit.getDefaultToolkit().beep();
            }
        } catch (DataObjectNotFoundException donfe) {
            TopManager.getDefault().getErrorManager().notify(ErrorManager.WARNING, donfe);
        } catch (IndexOutOfBoundsException iobe) {
            TopManager.getDefault().getErrorManager().notify(ErrorManager.WARNING, iobe);
        } catch (IOException ioe) {
            TopManager.getDefault().getErrorManager().notify(ErrorManager.WARNING, ioe);
        }
    }
    public void outputLineSelected(OutputEvent ev) {
        // Similar to the above but less aggressive. Do not forcibly open the document.
        if (dead) return;
        if (! file.isValid()) {
            return;
        }
        try {
            DataObject dob = DataObject.find(file);
            EditorCookie ed = (EditorCookie) dob.getCookie(EditorCookie.class);
            if (ed != null) {
                if (ed.getDocument() == null) {
                    // The document is not opened, don't bother with it.
                    return;
                }
                if (line1 != -1) {
                    Line l = ed.getLineSet().getOriginal(line1);
                    if (! l.isDeleted()) {
                        attachAsNeeded(l);
                        if (col1 == -1) {
                            l.show(Line.SHOW_TRY_SHOW);
                        } else {
                            l.show(Line.SHOW_TRY_SHOW, col1);
                        }
                    }
                }
            }
        } catch (DataObjectNotFoundException donfe) {
            TopManager.getDefault().getErrorManager().notify(ErrorManager.WARNING, donfe);
        } catch (IndexOutOfBoundsException iobe) {
            TopManager.getDefault().getErrorManager().notify(ErrorManager.WARNING, iobe);
        }
    }
    public void outputLineCleared(OutputEvent ev) {
        doDetach();
    }
    /** Attach myself to a line if this has not been done already. */
    private synchronized void attachAsNeeded(Line l) {
        if (getAttachedAnnotatable() == null) {
            Annotatable ann;
            // Text of the line, incl. trailing newline.
            String text = l.getText();
            if (text != null && (line2 == -1 || line1 == line2) && col1 != -1) {
                // fits on one line
                if (col2 != -1 && col2 >= col1 && col2 < text.length()) {
                    // one section of the line was specified
                    ann = l.createPart(col1, col2 - col1 + 1);
                } else if (col1 < text.length()) {
                    // some column until end of line
                    ann = l.createPart(col1, text.length() - col1);
                } else {
                    // some problem
                    ann = l;
                }
            } else {
                // multiple lines, something wrong with line, or no column given
                ann = l;
            }
            // actually display the glyph etc.:
            attach(ann);
            // see if this line/fragment is changed or deleted in the future:
            ann.addPropertyChangeListener(this);
        }
    }
    /** Detach from the associated line, if necessary. */
    private synchronized void doDetach() {
        Annotatable ann = getAttachedAnnotatable();
        if (ann != null) {
            ann.removePropertyChangeListener(this);
            detach();
        }
    }
    // PropertyChangeListener method:
    public void propertyChange(PropertyChangeEvent ev) {
        if (dead) return;
        String prop = ev.getPropertyName();
        if (prop == null ||
                prop.equals(Annotatable.PROP_TEXT) ||
                prop.equals(Annotatable.PROP_DELETED)) {
            // Affected line has changed.
            // Assume user has edited & corrected the error.
            doDetach();
        }
    }
    // Annotation:
    public String getAnnotationType() {
        // Unique ID of this kind of annotation:
        return "org-apache-tools-ant-module-error"; // NOI18N
    }
    public String getShortDescription() {
        // This will become the annotation's tool tip text
        return message;
    }
}
The annotation type is registered in the layer:
<filesystem>
    <folder name="Editors">
        <folder name="AnnotationTypes">
            <file name="org-apache-tools-ant-module-error.xml"
                  url="error-annotation.xml"/>
        </folder>
    </folder>
</filesystem>
and the actual definition mainly gives some GUI properties of the glyph:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE type PUBLIC
          "-//NetBeans//DTD annotation type 1.0//EN"
          "http://www.netbeans.org/dtds/annotation-type-1_0.dtd">
<type name="org-apache-tools-ant-module-error"
      description_key="LBL_error_annotation"
      localizing_bundle="org.apache.tools.ant.module.resources.Bundle"
      visible="true"
      glyph="nbresloc:/org/apache/tools/ant/module/resources/error-glyph.gif"
      highlight="#FF8080"
      type="linepart"
/>

Also see issue #15920 for a suggestion on simplifying this whole process.

Define file types using MIME resolvers

The MIME resolver facility is actually present in NetBeans 3.2 though it was essentially unused at the time. It provides a way for modules to register knowledge of what the MIME type of a file is. You can add a MIMEResolver implementation to lookup and filesystems will ask it for information of file object MIME type; typically it might scan initial bytes of a file for magic headers and so on. The system can cache the opening bytes of the file, making this reasonably efficient.

For the first time in NetBeans 3.3 it is possible to add a MIME resolver without any coding: a new XML format is defined which makes the most common tasks (decision trees) available. For example, you can ask that all XML files with a certain DTD be given a special MIME type. Or that all files beginning with a certain "magic" byte sequence be recognized as some MIME type.

How are these MIME types supposed to be used? There are a few different ways in which it can be helpful to directly associate proper MIME types to file objects, but the major reason to use them is to make DataLoaders simpler. If a MIME resolver, especially an XML one, can fully define what your file type is, then you may simply use a UniFileLoader with one item in its extension list - that MIME type. There are potential performance benefits here because the system can pool resolvers and apply them in batch.

Which modules should look at these? Ultimately probably all loaders should work this way. For the shorter term, consider switching complex data loader recognition logic to simplified loader logic paired with MIME recognition if you:

  1. Recognize XML files by DTD, root element, or other common mechanism. Adding a new MIME resolver will make your module add very little overhead to the XML recognition process; keeping custom parsing tricks in the data loader adds a significant amount of overhead and prevents back-end optimizations by the IDE. Additionally your code will be much simpler.

  2. Recognize any kind of file based on its contents, especially some distinctive marker in its header. Opening the file from a data loader is a significant source of overhead; the resolver pool caches these headers. Your code will also be simpler here if you can use a declarative resolver.

  3. Need to have a MIME type associated with the actual FileObject anyway. For example, if it should be given some type when served from the internal web server.

The system of MIME resolution using declarative resolvers is documented in the Filesystems API. An example can be found in the Ant module (recognizing XML files based on root element). Consult the layer and data loader.

Lazy instances with InstanceCookie.Of

If you were registering any kind of instance to lookup in NetBeans 3.2, or are considering doing it for the first time in NetBeans 3.3, make sure you understand how to prevent eager classloading from happening. Essentially: if you have some service implementation which you wish to register, and it is not actually used in the course of an IDE session, its class should never be loaded (the interface class probably will be).

This is done technically using InstanceCookie.Of, if you are providing the raw cookie. When using standard means of providing instances, there are matching ways to ensure you split the interface and implementation classes clearly enough to be lazy: the instanceOf attribute on *.instance files, and the <instanceof/> element in XML settings. See the Services API for more information.

Enhanced lookup facilities supersede service types

There are many new features in the lookup system for 3.3. It would not be helpful to enumerate all of them because if you were not using lookup extensively in 3.2, you probably do not need to do much for 3.3 (except for things like moving services to layers, as described elsewhere in this document). If you were considering using ServiceType to define a custom kind of pluggable service, be aware that creating new subclasses of ServiceType is essentially deprecated - you can now use any superclass (or perhaps interface) you like, and lookup will handle it. Existing subclasses such as Executor and so on of course remain for compatibility reasons.

XML entity resolution and processors installed via layer

For 3.3, there is a more efficient and powerful way to add both entity resolution and cookie processors for XML files based on public ID.

Autoload modules

A new feature introduced into the NetBeans 3.3 core module system (though not officially covered by the Modules API) is that of autoload modules. It was observed that a number of modules simply function as libraries with no intrinsic value to the user, but are needed by "real" modules. Using module extensions (modules/ext/ and referencing via Class-Path) is not suitable: as of 3.2, modules do not automatically share extensions even when the file name is the same, meaning the JAR would be loaded twice; and extensions are not full modules, so they cannot register basic services in a layer, and so on.

So autoloads were introduced. An autoload module behaves exactly like ordinary modules except where enablement is concerned. It has no permanent enablement status; it is turned on automatically when the first non-autoload module to depend on it is turned on, and turned off automatically when the last such real module is turn off. Neither the user nor code can turn on or off just the autoload. It will be listed in a segregated category of the Modules node.

If your module built for 3.2 matches these criteria it may be better used as an autoload:

  1. Turning it on by itself, without something else to use it, would be pointless for the user.

  2. You have made it into an extension, or considered doing so, in order to be able to share it among several first-class modules.

  3. It has no independent user interface, or it does but this UI is just a container to be filled by other modules.

  4. Most of the module is an API of some sort.

To make it an autoload is easy: place it in the modules/autoload/ folder of the IDE installation. It may have extensions and so on like regular modules, e.g. modules/autoload/ext/.

Use hints to control property editors

The entire org.openide.explorer.propertysheet.editors package is deprecated, save perhaps generic interfaces such as XMLPropertyEditor, EnhancedPropertyEditor, and EnhancedCustomPropertyEditor. Any code which was using a specific property editor implementation from this package (such as FileEditor or one of its subclasses) should stop doing so for 3.3, and new code should not be written using this package.

There is a cleaner system in place for controlling the UI of property editors. Rather than explicitly creating a PropertyEditor implementation, you rely on the core and the search path to provide one according to property type. Controlling aspects of the editor is done using hints, name/value pairs which may be applied either to PropertyDescriptors in a BeanInfo, or to Node.Propertys when using the Nodes API. A number of hints for common property editors are already defined, and more will be defined as they are needed.

See the Explorer API for details on how to use these.

Here is an example of replacing the deprecated DirectoryOnlyEditor with the default editor for java.io.File plus a hint to use directories. It is similar to that used in the bean info for LocalFileSystem:

PropertyDescriptor rootDirectory = new PropertyDescriptor("rootDirectory", LocalFileSystem.class);
// These are documented in the Explorer API under java.io.File:
rootDirectory.setValue("directories", Boolean.TRUE);
rootDirectory.setValue("files", Boolean.FALSE);
rootDirectory.setDisplayName(/* ... */);
rootDirectory.setShortDescription(/* ... */);

j2eeserver/src/org/netbeans/modules/j2ee/impl/ServerExecSupport.java also demonstrates creating a Node.Property editing DataObjects with a given cookie. (It could probably also use the cookies rather than dataFilter hint.) ant/src/org/apache/tools/ant/module/nodes/DataTypeNode.java in FileProperty demonstrates creating a java.io.File again, but this time specifying a base directory to resolve filenames from. The Services API includes an example of customizing the editor for java.lang.Object.

Also if you are displaying a custom GUI component in which you would like to embed something akin to the Property Sheet or the GUI customizer for a property, avoid directly creating the embedded editor, if it is even available from the APIs. You want to take advantage of future improvements in the editor, and pick up whatever the core search path has to offer. So use PropertyPanel which is a neat way to reuse the property sheet code for editing a property.


Performance Optimizations

NetBeans 3.3 release is targeted mainly on performance and stability. For performance improvements, some compatible changes in API were done to allow modules to be less aggressive during the IDE startup. The standard NetBeans modules were changed accordingly to these changes but compatibility was ensured for other modules. The other modules work in NetBeans 3.3 without modifications but they are not so kind to the user's computer resources so it is desirable to change them to use the new APIs as well. The following paragraphs show the key areas which should be changed, and the methods for checking modules performance.

DataLoader changes

DataLoaders needs to be loaded into the IDE and initialized during the startup. They are necessary for the IDE functionality and may be considered entry point for file-type oriented modules. But there are things tightly coupled with DataLoaders which are not necessary until the DataLoader spot its file.
The first thing is DataObject for given DataLoader. Resolving DataObject too early can bring a lot of unneeded stuff into the IDE, but it was a common use-case of the DataLoader because the DataLoader have to pass its DataObject Class to the super constructor. As of OpenAPIs 1.10, it is possible to pass DataObject Class's name instead of the DataObject itself, so you should change your DataLoader constructor from
public TXTDataLoader() {
    super(TXTDataObject.class);
}
to
public TXTDataLoader() {
    super("org.netbeans.modules.text.TXTDataObject"); // NOI18N
}
See also API changes

DataLoaders also don't have to set actions and display name during the initialization. For this purpose, two methods were added some time ago, even before NetBeans 3.2 release but they were not much used before. These are defaultActions() and defaultDisplayName(). You should remove calls to setActions() and setDisplayName() and override the above mentioned methods instead.
Example:
protected void initialize() {
    setExtensions(...);
    setDisplayName(NbBundle.getMessage(TXTDataLoader.class, "PROP_TXTLoader_Name"));
    setActions(new SystemAction[] {
                        SystemAction.get(OpenAction.class),
                        SystemAction.get(FileSystemAction.class),
                        null,
                        SystemAction.get(CutAction.class),
                        SystemAction.get(CopyAction.class),
                        SystemAction.get(PasteAction.class),
                        null,
                        SystemAction.get(DeleteAction.class),
                        SystemAction.get(RenameAction.class),
                        null,
                        SystemAction.get(SaveAsTemplateAction.class),
                        null,
                        SystemAction.get(ToolsAction.class),
                        SystemAction.get(PropertiesAction.class),
    });
}
should be changed to
protected void initialize() {
    setExtensions(...);
}

protected String defaultDisplayName() {
    return NbBundle.getMessage(TXTDataLoader.class, "PROP_TXTLoader_Name");
}

protected SystemAction[] defaultActions() {
    return new SystemAction[] {
                        SystemAction.get(OpenAction.class),
                        SystemAction.get(FileSystemAction.class),
                        null,
                        SystemAction.get(CutAction.class),
                        SystemAction.get(CopyAction.class),
                        SystemAction.get(PasteAction.class),
                        null,
                        SystemAction.get(DeleteAction.class),
                        SystemAction.get(RenameAction.class),
                        null,
                        SystemAction.get(SaveAsTemplateAction.class),
                        null,
                        SystemAction.get(ToolsAction.class),
                        SystemAction.get(PropertiesAction.class),
    };
}
See also API changes

SystemOption usage

Modules often have persistent configuration and it is often implemented as a SystemOption subclass. The SystemOptions singletons were previously mentioned in module's manifest as OpenIDE-Module-Class: Option and their serialized state was loaded during the IDE startup, although it was not necessary in most cases. Now, you can move the option declaration into the layer and it will be deserialized the first time you'll ask for it. This also means you have to be careful to not reference the option too early. Common mistake is to place static reference to the option instance somewhere where it would be touched too early, like
    static MyOptions OPTIONS = (MyOptions)SharedClassObject.findObject(MyOptions.class, true);
inside a MyNode class or something like this.
In short, you should isolate the access to the SystemOption instances by lazy getters or lazy loaded classes. It you believe you can initialize your module without SystemOption and your SystemOption is still loaded, place a Thread.dumpStack() in a static initializer of your SystemOption, it will tell you who references it.

Services declaration

The similar issue as with SystemOptions is with ServiceTypes. ServiceTypes hold their configuration serialized and were used to be deserialized/initialized during the IDE startup. You should move all services from the module's manifest to layer, it will prevent the services from eager initialization.

WindowSystem changes

Modules which define their own Workspace or Mode, or starting with some TopComponents open were used to do this programmatically from their ModuleInstall. It is wrong from two perspectives: Why should the IDE create the window with its content when it will be hidden for most of the time, and why this thing have to be performed on every IDE startup (common case). This is solved by the new WindowSystem implementation which allows declarative specification of Workspaces, Modes and TopComponents allowing the module author to specify more docking constraints for the modes and TopComponents.
You should replace the layout creation code in ModuleInstall by XML documents placed at proper locations in the module's layer.
See the Window System API.

ModuleInstall cleanup

There is a generic guide why to cleanup ModuleInstalls.
After all of the above, your ModuleInstall.restored() should be empty in a lot of cases. If it is not the case, you should try to come up with some replacement solution. Be creative. Just think about what you're doing in the ModuleInstall, what is the impact on the IDE startup time, number of classes loaded on the startup and the memory footprint of the IDE. Then think whether it is necessary to do it during startup, how can you get it out of ModuleInstall and so on. There it one thing to think about: Your module will NOT be really used by most of the users of the IDE with your module enabled.

We can't cover all the possibilities in this guide, so I'm placing here an example for removing hooking into other modules: The cases where you're hooking some functionality in other module code can be replaced by lookup, but it requires cooperation with the other module's author. This was the case with Beans and Javadoc modules hooking their NodeFactorys into Java module's Nodes. Now they only place instance files into their layers and Java module will pick up the instances (using a FolderInstance over some negotiated folders, namely NodeFactories/java/explorer and NodeFactories/java/objectbrowser) as soon as it will need them, which means way later than with direct registration. This kind of communication can be developed between other modules as well, but there is no generic one size fits all recommendation.

BeanInfo implementations

This is not an upgrade issue but surely thing you should care of.
There are some BeanInfo classes in nearly all modules. The problem with their implementation is that they are "over-cached" most of the times. The BeanInfo is a class that is brought inside the VM mostly for one-time usage like deserialization or IDE configuration so it should be optimized for size, not the speed. Holding of tons of reflective information in static fields is not an option for such a class. See for example issue #14938 for more on this topic. You could look for rewrite example here
In future, we plan to replace standard BeanInfo lookup mechanism by a XML based bean descriptions but it is not fully specified yet.

PropertySupport.Reflection

This is another thing that should be considered while rewriting the module. There is a lot of subclasses of PropertySupport.ReadWrite/ReadOnly. In most cases they can be replaced by using PropertySupport.Reflection. The reason is simple: the less classes are loaded in the VM the better.

Static resource bundles

You should avoid keeping static references to resource bundles. org.openide.util.NbBundle is intended to centrally manage the bundle cache anyway.

// UNDESIRABLE:
private static ResourceBundle bundle;
private synchronized static getText(String key) {
    if (bundle == null) {
        bundle = NbBundle.getBundle(ThisClass.class);
    }
    return bundle.getString(key);
}
public getSomeText(String something) {
    return MessageFormat.format(getText("FMT_foo"), new Object[] {something});
}

// BETTER AND EASIER:
public getSomeText(String something) {
    return NbBundle.getMessage(ThisClass.class, "FMT_foo", something);
}

Pre-3.3 Changes

There are a few older changes which were available already in 3.2 but sufficiently young that many modules may not have picked up on them, so a few of these are listed for completeness.

Desirable Changes in Modules

Consider making these changes.

Image caching and badging

Most loading of images, such as from BeanInfo.getImage(), loads an icon from a module resource path. The JRE provides a few caching techniques, such as those in Toolkit and SimpleBeanInfo, but they are not great with memory management. Please use the NetBeans standard cache manager: Utilities.loadImage(String).

Your module should never load an image in the static initializer of some class, just load it on demand using the cache:

// This is bad:
static Image myicon = ...;
public Image method() {
    return myicon;
}
// This is better but never collects the image:
static Image myicon = null;
public Image method() {
    if (myicon == null) {
        myicon = ...;
    }
    return myicon;
}
// Better yet, though JRE cache manager is not the best:
public Image method() {
    return Toolkit.getDefault().getImage(...);
    // similar for SimpleBeanInfo.loadImage(...)
}
// Best:
public Image method() {
    return Utilities.loadImage("my/pkg/resources/something.gif");
}
If you are doing icon badging, that is placing special overlays on a basic icon to indicate statuses such as "uncompiled", "in error", "needs checkout", and so on, then you can do this best using the badge management API, which is easy to use and works well with the image cache (both basic images and generated composites are cached sanely): Utilities.mergeImages(...)

Short and long descriptions and categories for modules

All modules ought to have in addition to the display name a short description (tool-tip-length); long description (paragraph-length); and a display category (phrase selected from among some known categories). This will help the user understand what the module does and whether it is important; such information is displayed under the Modules node as well as in the module configuration panel of the Setup Wizard.

For 3.3, such information is all localizable and should go into a bundle, so for example:

OpenIDE-Module-Name=My Module
OpenIDE-Module-Short-Description=This module performs one simple task relating to something.
OpenIDE-Module-Long-Description=\
        This module is a complete solution for developing something objects. \
        It can be used in two modes, one to generate things, and one to display them. \
        You may want to use it in conjunction with the Power Something module.
OpenIDE-Module-Display-Category=Data Types

Nicer-looking wizards

Wizards got a face lift - you can customize a number of aspects of wizard GUI, such as creating a list of steps to be performed in the left margin of the wizard. All wizards used in 3.3 should try to look pleasant to the user, especially in the list of steps. Pending official documentation, see the UI website.

Use ErrorManager for logging

Do not use System.err.println for keeping a trace of what your module is doing. There is a better way:
static final ErrorManager err = TopManager.getDefault().getErrorManager().
    getInstance("org.netbeans.modules.mymodule"); // NOI18N
// ...
if (somethingWeirdHappened) {
    err.log("something weird happened here");
    // continue with it
}
if (isStrange(result)) {
    if (err.isLoggable(ErrorManager.UNKNOWN)) {
        err.log("strange result: " + formatForDebugging(result));
    }
    // deal with it
}
You can log messages to the system log file (and also check whether one will be logged before doing some expensive calculation). To turn on logging for your module, just add this to your command line or ide.cfg:
-J-Dorg.netbeans.modules.mymodule=0
You should see in system/ide.log e.g.:
[org.netbeans.modules.mymodule] something weird happened here
[org.netbeans.modules.mymodule] strange result: Result<....>
You can also set the level to -1 to get output on the console too.

Regardless of whether you use it for logging, ErrorManager should be used by all modules for several purposes:

  1. Adding information to an exception. Use annotate. You can add unlocalized information to help debug it, or localized information (preferably with a severity level) to present it comfortably to the user, if it is expected that a user action could cause it.

  2. Changing an exception type. Use annotate or copyAnnotation before rethrowing to preserve the original stack trace.

  3. Reporting an exception you have caught. Use notify to include all information available - never printStackTrace which bypasses the manager and can lose information.

See the documentation for ErrorManager for more details.


Helpful Performance Tools

These tricks can help you find areas of your module that need performance work.

Startup performance logging

If you'd like to see how does your module perform during the startup, turn on the startup profiling by using the command line switch -J-Dorg.netbeans.log.startup=print
You'll see cut-through the startup process in the following format:

    @10116 - module preparation started
        @10276 - prepared org.netbeans.modules.text/1 dT=160
	...
        @11104 - prepared org.netbeans.modules.vcs.cmdline.compat/1 dT=1
    @11105 - module preparation finished, took 989ms
where the @10116 is the timestamp from the start of logging (early in the startup phase), there are bigger section ending with label in the form @timestamp - label, took {time for the whole block}ms. The bigger blocks are nested and also contain checkpoints which show the time from the latest checkpoint in the form of dT={time}
The most important parts for module authors are:

@10555 - prepared org.netbeans.modules.editor/1 dT=73: This covers enabling the module's ClassLoader with dependencies, parsing manifest, loading ModuleInstall class and possibly calling ModuleInstall.validate(). In most cases you can't affect this much.

@14272 - sections for org.netbeans.my.module/1 loaded dT=21: covers installation of module actions, nodes and loaders.

@19795 - ModuleInstall for org.netbeans.my.modules/1 called dT=171: covers ModuleInstall.restored().

You should also check the time spent in Project opened and Main window opened checkpoints with and without your module.

Class loading listing

Class loading is one of the most time and memory consuming thing on the IDE startup, so you should avoid loading unnecessary classes during the startup. You can use JVM switch -J-verbose:class, grepping the output for classes of your module and also checking the impact of enabling your module on other classes (wc -l, diff). The most powerful is the combination of both the switches which will show you where are the unnecessary classes are loaded so you can better investigate why are they loaded.


Have fun with the new release of NetBeans! Send feedback to nbdev@netbeans.org.


Built on May 4 2005.  |  Portions Copyright 1997-2005 Sun Microsystems, Inc. All rights reserved.