Node.Property
s in a Node.PropertySet
is significantTopComponent
subclasses should override getPersistenceType()
TopComponent
subclasses should
override preferredID()
Node.Property
to PropertyModel
RequestProcessor
API changes
ArchiveBuilder
s, extended properties moved to layer
DataShadow
s
TopComponent
TemplateWizard.Iterator
as a cookie
TopComponent
s
InstanceCookie.Of
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 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.
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.
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.
Check for changes here which might cause problems for your module if left uncorrected.
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>
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:
org.openide.execution.ExecutionEngine
org.openide.execution.ExecutorTask
org.openide.execution.NbClassLoader
org.openide.execution.NbClassPath
(factory methods deprecated however)org.openide.execution.ScriptType
(not useful without scripting
module however, which is no longer supported)Deprecated classes include:
org.openide.actions.ExecuteAction
org.openide.cookies.ArgumentsCookie
org.openide.cookies.ExecCookie
org.openide.execution.ExecInfo
org.openide.execution.Executor
org.openide.execution.ExecutorType
org.openide.execution.NbfsStreamHandlerFactory
(also see Javadoc for information on deprecated URL protocols:
nbresboot
, nbresbootloc
, nbrescurr
, nbrescurrloc
)org.openide.execution.NbfsURLConnection
(nbfs
URL protocol still available via other means, but not widely used in 4.0)org.openide.execution.ProcessExecutor
org.openide.execution.ThreadExecutor
org.openide.loaders.ExecutionSupport
More information:
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.
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.
This upgrade guide outlines notable API changes between NetBeans 3.5 and 3.6.
Check for changes here which might cause problems for your module if left uncorrected.
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.
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:
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.
Rather than RE
, use Pattern
.
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.
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).
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.
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.
If you need to parse from an open-ended stream, it is trickier.
You may try something like the following:
ReaderCharSequence
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()
.
DataObject
s 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
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 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.
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.
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.
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.
Node.Property
s in a Node.PropertySet
is significantAs 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.
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.requestFocus
was used to both activate and focus TopComponent
s
in the old window system. As this caused problems, a new method, TopComponent.requestActive
,
was added. This method must be used to activate TopComponent
s in window system, causing
its parent Mode
s 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.
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.
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.
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.
This upgrade guide outlines notable API changes between NetBeans 3.4 and 3.5.
Check for changes here which might cause problems for your module if left uncorrected.
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.
Cleaning of the Open APIs by separating them into independent modules has progressed in several phases.
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.
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.
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:
Registration of URL stream handler factories using
NbfsStreamHandlerFactory.register(...)
is deprecated.
Simply create an instance of URLStreamHandlerFactory
and add it to Lookup instead.
The method FileUtil.nbfsURLStreamHandler
was added,
but is not intended for use by modules.
Direct use of NbfsURLConnection
to encode and
decode nbfs
protocol URLs is deprecated in favor
of the more general URLMapper
from the Filesystems
API.
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.
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.
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.
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.
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.
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.
This upgrade guide outlines notable API changes between NetBeans 3.3 and 3.4.
Check for changes here which might cause problems for your module if left uncorrected.
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:
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.
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:
|
For more details, see issue #19622.
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.
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.
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:
The filesystems path, from
NbClassPath.createRepositoryPath(FileSystemCapability.EXECUTE)
.
This can be configured by users simply by mounting or unmounting
filesystems.
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.
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.
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:
Runtime nodes. Nodes of type Environment should be placed in the UI/Runtime/ folder using the *.instance syntax, simply specifying the class of the node in question.
Session nodes. Nodes of type Session should be placed in an appropriate subfolder of the UI/Services/ folder, again using the *.instance syntax.
Root nodes. Nodes of type Root should be
placed in the Windows/Components/ folder using *.settings syntax,
using a org.openide.explorer.ExplorerPanel
as the component. Use the ability of
*.settings files to specify a static factory method to associate the explorer panel
with your root node. Consult the Window System API:
XML Layers for details. You may also want to add an action to the View menu
which shows and focuses your Explorer tab if it is not already.
RequestProcessor
API changesIn 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.
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:
netbeans/bin
nor
netbeans/lib
directoriesneedsrestart
attribute
set to false
. You can specify this attribute in the makenbm
task of the module's build script.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.
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.
(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.
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.
ArchiveBuilder
s, extended properties moved to layerRelevant to modules which programmatically extend the capabilities of the JAR Packager. For details see issue #21323.
DataShadow
sBefore 3.4 the only way to create DataShadow
s (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>
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/.
TopComponent
There are several new protected notification methods in
TopComponent
:
componentOpened
componentClosed
componentShowing
componentHidden
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.
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.)
TemplateWizard.Iterator
as a cookieIn 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:
There were excess file attributes in layers for modules with many templates.
Users who created their own templates did not get the wizard, unless messy hacks were implemented.
Other modules making templates of the same object type needed to know the exact class name of the wizard, which is potentially fragile.
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.
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
FileSystem
s are encouraged to supply mappers meaningful
to files residing in filesystems of that type.
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.
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.
This upgrade guide outlines notable API changes between NetBeans 3.2 and 3.3.
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.
This section lists changes desired when upgrading to 3.3 from 3.2, in addition to older changes.
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:
System options. They may be placed in the Services/ folder using *.settings syntax, with a link from an appropriate category of UI/Services/. Provide a localized name and icon for the settings file (not its link).
Services. Register as settings in an appropriate Services/ subfolder (conventionally Executor/, CompilerType/, DebuggerType/, SearchType/, IndentEngine/, etc.). Additionally, if the service has any user-configurable properties such that it would make sense to create more than one instance, add an identical settings file into the matching subdirectory of Templates/Services/. Provide a localized name and icon for the settings file(s). Relative order of services (e.g. priority for choosing a default) may be accomplished using folder ordering.
Currently registered services should normally be marked with the
file attribute SystemFileSystem.layer
set to
project
, making them project-specific. This is not yet an
official API, just a hint recognized for the 3.3 release. The same hint
may be applied to other settings, such as certain system options.
Filesystem types. Register as a setting in Templates/Mount/, generally with a localized name and icon as well as optionally a template iterator and description.
Specific filesystem instances. Filesystems to be initially mounted by the module can also be registered by including settings in Mount/ rather than via procedural mounting. You can either use *.settings syntax if you intend for the mount to be user-configurable, or for the common case that you are trying to mount a Java library ZIP/JAR intended for the user classpath which is present in the IDE installation, or a Javadoc ZIP/JAR/folder for the Javadoc tab which is also present in the IDE installation, you can create XML files using DTDs supported by modules. See the Filesystems API for details.
Warning: when doing this you should consider
compatibility of old user settings. Typically in pre-3.3 releases,
mounts (including those made programmatically from module installers)
would have been stored serialized into project settings; you must be
prepared to handle their deserialization. Assuming the new file under
Mount/ (or a subdirectory) handles the mounting needs and
obsoletes the former serialized setting, you can cause the old mount
to disappear after a restart cycle, as in
org.apache.tools.ant.module.AntModule
.
Do not try to return null
from writeReplace
as it will not work (see #17476 for details).
Debuggers can probably be registered via lookup in Services/. (Untested; only one is active at a time anyway. Debugger types are services, q.v.)
Clipboard convertors. If anyone actually uses these, they should be registered via lookup in Services/.
JavaHelp - see below.
Module installers - many tasks formerly done procedurally can now
be done in layers; covered throughout this document, and also see below. Many installers can be deleted
outright; every installer which currently overrides
readExternal
and writeExternal
should be
changed if at all possible to not keep any serialized state.
(It is safe to delete these methods or the entire installer if you no longer keep any state.
The IDE will not attempt to call the default implementations
at all if these methods are not overridden, so you do not need to override
readExternal
just to keep compatibility.)
<!-- 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:
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:
Better performance - avoid running code during startup. Objects are initialized only as the window system needs them.
Simplification of code - rather than procedurally checking for window system objects and creating them and removing them at uninstallation, more is done declaratively in XML.
More power - you can control MDI frame attachment modes and such advanced features not available through the Java-level API. Modules can also cooperate to incrementally supply pieces of a workspace, and so on.
Better uninstallation and project switching - your layer-supplied workspaces, modes, and components will be uninstalled more cleanly with your module, and will serve as a default for all projects (but customizable in each) without added effort.
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.
TopComponent
sIf 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 TopComponent
s which cannot be safely deserialized
at all should still writeReplace
to null
, so
as to avoid problems in a future release.
There is an existing document describing how to do this conversion: Manifest Localization Guide
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:
Performance during startup - while some effort is made to improve performance over 3.2 even for manifest-installed helpsets, they still need to be parsed in order to retrieve helpset title and home ID. Using a layer, this information is specified explicitly and the IDE need do nothing until the helpset is actually displayed.
Flexibility - using the layer syntax, you can explicitly control whether helpsets are merged into the master helpset or not, and if so in which order; control whether links are made in the Help | Help Sets submenu, and if so in which order, and how the link appears; add additional convenience links with the same helpset; and more.
To use this new system effectively, first remove the obsolete manifest declaration. Now in your layer you need to add three items:
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.
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.
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.
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:
obtain a list of all known modules
determine if a module is enabled or not, and listen to this change
programmatically enable or disable a known module
for your own module, refuse to be installable if some criteria (such as presence of a license file or external application) are not met
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.
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.
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 DataLoader
s 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:
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.
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.
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.
InstanceCookie.Of
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.
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.
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.
Entity resolution. In 3.2 it was possible to add declarations of known entities (in most cases XML DTDs) to the system, using an XML file in a special format. For example, if you had your own DTD for some purpose and wished to ensure that at least from within the IDE a local copy would be used instead of accessing the network, an XML file could be added to the services area with the DTD given by -//NetBeans//Entity Mapping Registration 1.0//EN.
This method continues to work in 3.3 but is deprecated: it adds
some overhead at startup to parse all such entity declarations, and
there were some possible race conditions. Instead you may register
entities according to their public ID directly in a layer: create a
file containing the entity itself and give it a special name beneath
xml/entities/ computed from the public ID. This is more
efficient and safer. See
EntityCatalog
for details.
Processors. In 3.2 you could add your own cookies to XML files
without actually recognizing the files using a data loader. This was
done with an XMLDataObject.Processor
implementation,
registered programmatically via XMLDataObject.Info
.
For 3.3 this is deprecated, as a typical processors may never be used in a given IDE session, and it caused too much work at startup to register them all programmatically. Instead the folder /xml/lookups/ is used, with a file naming convention similar to that given above for entities. But the file names end in *.instance and should give an instance of one of the following things:
Environment.Provider
. This is the best option, new in
3.3. It permits you to provide a lookup for the XML file which may
serve cookies as it likes, including changing the cookies on the fly.
XMLDataObject.Processor
. This is deprecated for 3.3.
XMLDataObject.Info
. Also deprecated for 3.3.
If you were providing a special DataNode
for the XML
file, this is deprecated but no better alternative yet exists in 3.3 -
Looks will probably be used for this in the future. In the meantime it
will still work; you can use XMLDataObject.Processor
subclassing DataNode
, or provide results from a lookup
query on Node
. Either way, register the object in the
layer as above.
The Services API has additional information on associating cookies with XML files.
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:
Turning it on by itself, without something else to use it, would be pointless for the user.
You have made it into an extension, or considered doing so, in order to be able to share it among several first-class modules.
It has no independent user interface, or it does but this UI is just a container to be filled by other modules.
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/.
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 PropertyDescriptor
s in a
BeanInfo
, or to Node.Property
s 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
DataObject
s 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.
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
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
subclass. The SystemOption
s
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. 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.
SystemOption
s is with
ServiceType
s. ServiceType
s 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.
ModuleInstall
s.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 NodeFactory
s 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.
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.
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); }
Consider making these changes.
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(...)
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
ErrorManager
for loggingSystem.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=0You 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:
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.
Changing an exception type. Use annotate
or
copyAnnotation
before rethrowing to preserve the original
stack trace.
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.
These tricks can help you find areas of your module that need performance work.
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 989mswhere 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}
@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 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
.