Overview

Tips for Module Authors

This document provides a few tips for module authors, centering around the basic steps that you need to take to get a hello-world module up and running, and how to progress from there.

Where to Start?

It is of course impossible to give a one-sentence summary of everything you need to know before you start writing a module - first you must have identified the exact functionality that the module will add to the IDE, and what APIs will be needed to do this. You should first at least browse the descriptions for various APIs to familiarize yourself with what they contain. Then try to select one or two APIs to concentrate them, and read the whole description.

For example, someone might want to write a module which would add a menu item to pop up a new window showing the Explorer in one pane and some information about selected files in the other, while sending progress messages to the Output Window, and also providing an Environment node describing the current status of the processing. This might involve:

  1. The Actions API to install the proper menu item.
  2. The Window System API to create and display the window and have it interact well with the IDE's window manager.
  3. The Explorer API to display the standard tree view of the Explorer in one pane, to create a new view in the other pane (displaying custom information), and to synchronize the two properly.
  4. The Nodes API to get access to the selected nodes in the Explorer window; the DataSystems API to examine the data objects contained in these nodes; and the FileSystems API to get access to the content of the files in these data objects.
  5. The Window System API again to display messages in the Output Window.
  6. The Nodes API again to create an appropriate Environment node (perhaps with some children, and user-visible node properties) reflecting the running state of the task.
  7. The Modules API to create the installation code for the module, which would need to specify the menu item and the environment node.
There are quite a number of APIs and it will probably not be clear right from the start which ones you need for your project! So we suggest that you look through the code for example modules doing something similar to what you need. Please see the EAP web site to look for interesting module source code.

The first step you should take is to download the OpenAPIs Support Module. It not only includes the full API documentation for you to browse, but includes a variety of templates for common API objects that will help you get started, and also has support features such as special testers and a module assembler that can streamline the process of developing and deploying a new module. Tips on how to use the support module to more easily accomplish some task will be inserted throughout this document, preceded by Support module: to alert you.

Arranging Source Files and Resources

Before starting to write code, you should of course select a Java package to place the module code into. Unlike applets and some other Java technologies, NetBeans modules are not placed in a protected "sandbox", so they should be polite and use specific package names according to the usual conventions - e.g. com.mycom.mymodule.*. This way, you can be sure that your module will not conflict with any other (they are all loaded into the same namespace).

Most modules will also need some images, localized text, and so on. Typically these are placed in the same package together with source code, and the usual Java mechanisms (or NetBeans convenience variations) are used to load these - you will almost surely want to use classloader-based mechanisms for finding these resources. For example, localized text may be retrieved using code like:

String localizedText = NbBundle.getMessage (DialogPopper.class, "MSG_doYouWantToContinue");
which would look in a Bundle.properties file (or Bundle_ja.properties, etc.) in the same directory as the DialogPopper source file:
# ---DialogPopper class resources---
# Message displayed when dialog such-and-such is popped up.
MSG_doYouWantToContinue=Do you wish to continue with this installation now?
The NetBeans I18N website has much more information, including detailed recommendations on how to organize localizable resources, and how to test that you are doing it right. Support module: all Java code templates which make use of human-displayable strings include calls to NbBundle to get you started - you will need to provide the bundle yourself. Also if you use the I18N (Internationalization) module in conjunction with the API Support, you may ask to use NbBundle rather than ResourceBundle.

Images are requested in a number of different contexts and should generally be loaded with similar mechanisms. For example, the BeanInfo for a system option will typically override SimpleBeanInfo.getIcon(...) with a call to Utilities.loadImage(String) and pass in a pathname such as com/mycom/mymodule/optionIcon.gif.

Support module: all bean info templates includes code to retrieve an image icon, and corresponding dummy images are included in the group templates.

Writing the Manifest

For many modules, the manifest file is sufficient to tell the IDE how to install the module - no extra coding is required. The Modules API describes all of the available tags which may be placed in it. Here is an example of what a manifest for the example above might look like (marginal comments in red should not be included):
OpenIDE-Module: com.mycom.mymodule                 module code name
OpenIDE-Module-Name: My First Module               display name
                                                   blank line between header and first section
Name: com/mycom/mymodule/MyModuleSettings.class    extends SystemOption
OpenIDE-Module-Class: Option                       please install as an option!
                                                   blank lines between sections
Name: com/mycom/mymodule/MyEnvironmentNode.class   extends Node
OpenIDE-Module-Class: Node                         please install as a node!
Type: Environment                                  put in the Environment part of the Explorer
It does not matter what you name the manifest file - it will be renamed in the JAR for you anyway.

Make sure you have a blank line at the end of the manifest, if you are having problems.

Support module: the Module JAR template, which creates a special API-aware JAR file, lets you visually copy API-related classes and their associated code and resources, then paste as a group onto the proper section node to add the correct manifest entry as well as incorporating files into the JAR. The Property Sheet lets you configure options such as the module display name, or the destination for a node section. The Module Manifest template creates just the bare manifest for a module, but otherwise behaves the same. In both cases, the support is bidirectional so you may modify the manifest via the Explorer, or see in the Explorer modifications you have made in the text.

Building the JAR

First, of course, you need to compile all of your sources using your favorite compiler - you may want to do this inside the IDE itself, if that is where you are developing the module. Remember that these are Java 2 (JDK 1.3) sources, so you will need a compiler that can handle them - we recommend the 1.3 Javac, though it is not the fastest compiler available. Jikes should also work (complain if it does not). You need to at least include openide.jar in your classpath so the API classes can be found (this is done for you automatically if you have the API Support module installed):
$ cd ~/mysourcedir/
$ ls -R
manifest.txt
com

com/:
mycom

com/mycom/:
mymodule

com/mycom/mymodule/:
MyModuleSettings.java
MyEnvironmentNode.java
MyOtherClass.java
Bundle.properties
optionIcon.gif
$ javac -deprecation -g -classpath .:/usr/local/netbeans/lib/openide.jar com/mycom/mymodule/*.java
$ ls -R
manifest.txt
com

com/:
mycom

com/mycom/:
mymodule

com/mycom/mymodule/:
MyModuleSettings.java
MyModuleSettings.class
MyEnvironmentNode.java
MyEnvironmentNode.class
MyEnvironmentNode$1.class
MyEnvironmentNode$2.class
MyOtherClass.java
MyOtherClass.class
Bundle.properties
optionIcon.gif
You should use the JDK's jar command, or the JAR Packager module, to actually build the JAR file:
$ jar cvfm0 mymodule.jar manifest.txt com/mycom/mymodule/
Adding com/mycom/mymodule/MyModuleSettings.java (storing 0%)...
Adding com/mycom/mymodule/MyModuleSettings.class (storing 0%)...
Adding com/mycom/mymodule/MyEnvironmentNode.java (storing 0%)...
Adding com/mycom/mymodule/MyEnvironmentNode.class (storing 0%)...
Adding com/mycom/mymodule/MyEnvironmentNode$1.class (storing 0%)...
Adding com/mycom/mymodule/MyEnvironmentNode$2.class (storing 0%)...
Adding com/mycom/mymodule/MyOtherClass.java (storing 0%)...
Adding com/mycom/mymodule/MyOtherClass.class (storing 0%)...
Adding com/mycom/mymodule/Bundle.properties (storing 0%)...
Adding com/mycom/mymodule/optionIcon.gif (storing 0%)...
$ ls -l mymodule.jar
-rw-r--r--   1 myacct   myacct     27123 Dec 31 12:34 mymodule.jar
(Here we just included the *.java source files for simplicity, but you could easily exclude these, of course.) Remember to:
  1. Specify the -0 (zero) option to jar so as not to compress the files. This reduces startup overhead.
  2. Run jar from the top of your package hierarchy; it needs to "see" the included files in the correct relative subdirectories, according to the Java package name (or you could use the -C option).
  3. Double-check the manifest file created in the JAR to make sure you specified the filenames correctly in your input manifest. Any ZIP browser should do for this; or you can change to a different directory and run something like:
    $ cd ~/elsewhere/
    $ jar xf ~/mysourcedir/mymodule.jar meta-inf/manifest.mf
    Extracting meta-inf/manifest.mf...
    $ cat meta-inf/manifest.mf
    Manifest-Version: 1.0
    OpenIDE-Module: com.mycom.mymodule
    Created-By: 1.2 (Sun Microsystems Inc.)
    OpenIDE-Module-Name: My First Module
    
    Name: com/mycom/mymodule/MyModuleSettings.class
    OpenIDE-Module-Class: Option
    
    Name: com/mycom/mymodule/MyEnvironmentNode.class
    OpenIDE-Module-Class: Node
    Type: Environment
    
    $ cd ~/mysourcedir/
    
    You should see the entries you created, plus a few lines added by the JAR tool.
  4. Include all inner classes and resources in their proper paths.

Many people prefer to use Ant to build modules, as the entire sequence can be easily scripted, and there is support in the IDE for working with it too. See nbbuild.netbeans.org for some information on building NetBeans modules using Ant.

Support module: if you used the Module JAR template, then you can use the regular JAR Packager interface to build the module without needing to use a command line. You can develop and compile your classes inside the IDE too. You can hit Execute to try the module.

Installing the Module into the IDE

Under Tools | Options | Modules you will find a list of all modules installed in the IDE. Some "test" modules are distinguished with a red T superimposed on the module icon, meaning that they are reloadable. All modules can in principle be reloaded after they are installed if you just disable them, change their JAR file, and enable them again; in practice, various IDE and JDK bugs can make this not work reliably. If a module's Reloadable property is set to True (this property only appears with the special startup option -J-Dnetbeans.module.test=true, set also by the API Support), then the IDE will treat it in a special way making it reliable to reload it while the IDE is still running.

Do not place your module's classes into the IDE's startup classpath. If you do so, it may seem to work, but the classes will be loaded from the wrong classloader: the startup loader and not the module loader (for regular modules) or the user loader (for test modules). Various subtle errors can occur in this case. For this reason, the IDE will try to detect this situation and warn you about what is wrong.

Support module: the default executor for module JARs installs the module into the running IDE in test (reloadable) mode. If you execute it again, the JAR will be reloaded so normally there is no need to restart the IDE while testing! This is merely a presentation variant of using test modules from the Modules node. Note that you can make a regular module reloadable and reload it without restarting the IDE.

The support module also defines a special Ant task, <nbinstaller/>, which can be used to install module JARs in test mode from within an Ant script.

The Update Center feature (from the Tools menu) also permits you to dynamically publish module updates and upgrade them from an update server, which might live on netbeans.org, or on the Forte Portal, or on your own special server. Please see the Update Center's home page for more details on making NBM (NetBeans Module) packages and publishing them.

Testing the Module

The main device for testing modules is to develop them in test mode, as above. This permits you to make changes and try the changes within seconds of recompiling.

There is not much to say specifically about how to test modules once you are sure they got installed, other than trying out their functionality. Mainly, keep your console window open! If running on Unix, make sure you ran NetBeans from a command window (e.g. xterm) so that its output is visible. If there are any serious errors or diagnostics, they may be printed to the console, so you should check this window if in doubt.

If messages overrun the available space on screen, you must make sure the console window is scrollable. On Unix, this is usually true by default. On Windows, you should give the console window a scroll buffer - right-click on the "MSDOS" icon in the upper-left corner of the window, select "Properties...", go to the "Layout" tab, and make sure "Screen Buffer Size" is set to something appropriate (e.g. a few hundred wide, and a thousand or two tall). Save your settings, and agree to apply them to the shortcut so that they will take effect the next time you restart.

If it is not there already, add -J-Dnetbeans.debug.exceptions=true to your IDE command-line options to make sure you are informed of all unexpected exceptions that occur. You may also want to turn on various ErrorManager flags, though exactly which ones will depend on what you want to know.

Support module: If your manifest file syntax is incorrect, a red X will appear superimposed on the JAR or manifest icon. You can check the property sheet to see the exact errors. Of course, other parts of the module are not checked by this, just the manifest syntax.

Testing Code Outside of Modules

To just test out functionality of the Open APIs, without necessarily wrapping everything up in a module, you can just write Java main classes that set up some test code, and use System.out.println (e.g.) to display the results. You must set such a file to use internal execution; then the code will be run inside the IDE, so you can test it as you go. You can use the Scripting Console to use your favorite scripting language to test expressions interactively. Also, if you have a Debugger module (e.g.) which provides interactive evaluation of expressions, this may be used for similar purposes.

Support module: there is a template API Test Script which will be run using internal execution and includes a few useful testing methods and some standard imports.

Here are some specific things which can be done using internal execution that would make testing easier than rerunning the module:

Debugging Modules

The difficulty in debugging is that it is not possible to debug a module by itself within the IDE, since it runs within the same VM as the IDE and integrates into it. The same goes for code which needs to be run in internal execution (as it makes API calls) - there is no such thing as "internal debugging" using the normal VM-supported debugger implementation. So, you will need to run the entire IDE in debugging mode, using either another copy of the IDE, or some alternate debugger such as command-line JDB, and set breakpoints on the module code of interest. (Note that module code is dynamically loaded, i.e. not statically referenced, and therefore you may need to delay settings breakpoints until the module has already been loaded.) To use (another instance of) the IDE as the debugger, you should modify the startup script of the to-be-debugged IDE to include -Xdebug (and maybe -classic for HotSpot, and some sundry other flags for JPDA as documented in general for the JPDA switches) and also include tools.jar (from the JDK) in the boot class path. Start the debuggee manually and use the connect-to feature of the IDE's debugger to connect to the process as usual with the host name and agent password (or for JPDA, socket or shared memory segment or whatever is required).

To avoid running the whole IDE in a debugger (this may be slow), you may prefer to use the fallback method of including copious debugging statements which print interesting information as the IDE runs your module code. In practice this is feasible - most API objects either have a sensible implementation of Object.toString(), or provide an obvious display name that would identify them easily when printed. Printing to System.err will not only print to the console, but will include the output in the netbeans.log file in the installation's system/ directory, for later perusal. See above for redirecting output to the Output Window. If you use test modules, it is simple enough to insert a few debug prints, reload, see what is going wrong, try to fix it, verify that it is fixed, and then remove the statements again.

The scripting module (currently in NetBeans builds only) may be used to evaluate expressions and run commands interactively inside the IDE's VM.

The ErrorManager may be used to emit debugging information from modules at customizable output levels, so that critical diagnostic information can be left in production code and dynamically enabled when required.

If you are having problems related to Java security mechanisms, there are a couple of things you can do. First, if SecurityException is being thrown, you can add -J-Djava.security.debug=access,failure to the IDE's command-line options to show the ProtectionDomain that failed. Second, if you are getting IllegalAccessError, try printing to standard error IllegalClass.class.getProtectionDomain().getCodeSource(), which will show the URL from which the class was loaded; if this URL is outside the IDE's installation, it is likely you need to manually add permissions to the relevant classloader.

Support module: many things about the IDE that you might wish to know (current settings and status of various objects, etc.) can be seen under Runtime | Bean Browser, without running a debugger.


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