Power Java programming—free!

Build an open source IDE with jEdit

Sometimes the best things in life are free, like fresh air and sunshine, but not software. Until recently, a professional-grade IDE could have cost hundreds or even thousands of dollars. However, after Linux entered the marketplace, open source software proliferated and became competitive with commercial software. Today, open source development tools are as free as fresh air and sunshine, and just as plentiful.

For example, the jEdit programmer's editor is an open source utility with advanced editing features. Using jEdit plug-ins, you can integrate open source development tools such as CVS (Concurrent Versions System), Ant, and JUnit within jEdit's graphical user interface (GUI). This article shows you how to transform jEdit into an IDE comparable to proprietary software. In this article, I use the term open source software instead of free software. To set the record straight, "Sidebar 1: Open Source Software = Free Software" explains the similarities and differences between open source and free software.

IDE features

The first step in building an IDE is answering a simple question: what features should the IDE support? Many developers request features in the following list. These requirements may be the same for you:

  • Small, fast, intuitive, but powerful programming editor: it starts up and responds quickly; you can use it without spending hours reading documentation; and it has powerful features such as advanced search and replace
  • Source code beautification: configurable, programmatic formatting of Java source files to a consistent standard
  • Class browser: tree-structured view of the methods, data members, and inner classes in a Java source file and point-and-click navigation through the file
  • Project view: tree-structured view of a project's files and point-and-click navigation through the project
  • Version control: integration with CVS with a graphical interface
  • In-process compilation: compilation of a Java class in the current edit buffer
  • Unit-testing framework: integration with JUnit to run automated regression tests within the IDE
  • Build management: integration with Ant to run build scripts for a project within the IDE
  • Integrated debugger: when code behaves badly and you don't know why, having a debugger in your toolkit is a blessing
  • Integrated Javadoc generation and viewing with a graphical interface

You might want to add additional features to the above list based on your requirements.

Build the jEdit IDE

Once you've defined your requirements for an IDE, download and install the software. If you don't have a Java SDK, download one. The development tools need jar and executable files included in the SDK, but omitted from the JRE (Java Runtime Environment).

Installing jEdit is as simple as downloading and running an executable jar file. From the jEdit project homepage, select the Quick Start page and follow that page's directions. Read the installation instructions for your operating system on the download page. To run jEdit, use the Java application loader (javaw) in the SDK, not the JRE, since the development plug-ins will not work properly with the JRE.

Note: Due to strong corporate backing, NetBeans and Eclipse have received much more press coverage than jEdit. If you're wondering why someone would pick jEdit over NetBeans or Eclipse, see "Sidebar 2: jEdit Versus NetBeans and Eclipse."

Downloading jEdit plug-ins is equally simple. Using the Plugin Manager, simply point and click—no analysis required. The Plugin Manager resolves dependencies and automatically downloads any jar files required by the plug-ins you select. The following is a list of the plug-ins that provide the features discussed earlier:

  • AntFarm: integrated graphical Ant build manager
  • JavaStyle: Java code beautifier and template Javadoc comment generator
  • JBrowse: class browsing of a Java source file
  • JCompiler: in-process compiler for Java files in the edit buffer
  • GruntspudPlugin: a graphical CVS client that supports most CVS commands and many connection methods, including local, pserver, and ext
  • JIndex: with a single key-press, shows Javadoc API HTML documentation for a highlighted word
  • JUnitPlugin: integrated graphical JUnit test runner
  • ProjectViewer: tree-structured view of related project files
  • Swat: integrated graphical interactive debugger
  • JDocPlugin: integrated graphical Javadoc generation

You might want to install additional plug-ins based on your requirements (the jEdit Website lists 80 plug-ins). Also, jEdit automatically downloads some plug-ins such as the Common Controls and Console plug-ins to resolve dependencies between plug-ins.

To install plug-ins, from the jEdit menu bar, select Plugins, then Plugin Manager. Next, click the Install Plugins button at the bottom left of the Plugin Manager dialog box. On the Install Plugins dialog box, check the boxes for the plug-ins you want to download (see Figure 1); check the box labeled Download Source Code; select the radio button labeled Install In User Plugin Directory; and click the Install button. The plug-ins should download in a few minutes (assuming a high-speed Internet connection). Restart jEdit and you're ready to start programming.

Figure 1. Install plug-ins

Configure and use the jEdit IDE

After installing jEdit and the plug-ins, configure your IDE using the Utilities command from the Global Options menu. Most plug-ins require little if any configuration. If you have any questions about configuring a plug-in, select the Help menu—interactive help is part of the plug-in architecture.

To improve ease of use, you'll want to "dock" frequently used plug-ins such as Gruntspud, ProjectViewer, JBrowse, AntFarm, and JUnit. Plug-ins can either float or dock on one of the four sides of the jEdit window. Docking a plug-in allows you to minimize it to the side of the edit window and launch it quickly by clicking on the plug-in's tab. To configure docking, select Utilities, Global Options, jEdit, Docking; set the docking position for these plug-ins to the location you prefer; and click the Apply button. Tabs for these plug-ins will appear on the jEdit window side that you picked (see Figure 4).

You'll also want to configure the Console plug-in to display a console toolbar for entering operating system commands within jEdit. To configure the console toolbar, select Utilities, Global Options, Plugins, Console, General, and check the Console Tool Bar box.

I next describe configuring and using several of the more important plug-ins: Gruntspud, ProjectViewer, AntFarm, and JUnit. The rest require little if any configuration, and using them is intuitive.

Gruntspud

Gruntspud is a CVS client, so knowledge of CVS is a prerequisite for using it. The following example uses a local CVS directory to create a working directory of the downloaded JDocPlugin source code in a directory named \javapkgs\src (I refer to the JDocPlugin project in this article's later sections).

First, select the Gruntspud connection dialog box from Global Options and configure a local connection. For my configuration, I used the name localCVS, for a local type and a repository path of C:\CVS. Click the Add button on the Add Connection dialog box and the OK button on the Global Options dialog box to store the configuration.

Next, import the JDocPlugin project into the local CVS repository. To import this project, select Plugins, then Gruntspud; click the All Tools icon on the toolbar's left-hand side (an image of a crossed wrench and screwdriver); and select Create, then Import to display the Import dialog box. On the Import dialog box, complete the General pane as shown in Figure 2; enter the version message on the Message pane; click the Rescan button on the Files pane; and then click the Import button.

Figure 2. Gruntspud Import dialog box

Finally, create a CVS working directory by checking out the JDocPlugin module from the CVS repository. Select Create, then Checkout to display the Checkout dialog box. Navigate to \javapkgs\src and set the current directory to the JDocPlugin directory's location. Select the General tab, type JDocPlugin for the module name and checkout name, and click the Checkout button. After the checkout executes, Gruntspud displays the CVS working copy's status. Click the Explorer tab in Gruntspud, select one of the Gruntspud files, then right click and select Status. Figure 3 shows the Gruntspud display of a CVS working directory.

Figure 3. CVS working directory

ProjectViewer

Use the ProjectViewer plug-in to view and edit the JDocPlugin source files. First, create a JDocPlugin project. Select the ProjectViewer tab, left click the All Projects folder and select Add Project. Type the project name JDocPlugin. Open the JDocPlugin root directory that you created in the CVS configuration (/javapkgs/src). Highlight the JDocPlugin directory and then click the Open button. Click OK and select Import All Files. jEdit will add the JDocPlugin source files to your project view. Now you can edit the project's files by double-clicking their icons. Open the jdoc directory and double-click the JDocPlugin.java file. Figure 4 shows the JDocPlugin project's ProjectViewer display.

Figure 4. ProjectViewer

AntFarm

For any project with more than a few Java files, you'll want to use Ant to build the application. To use Ant with the JDocPlugin project, you need to configure AntFarm to use JDocPlugin's build file. First, select Utilities, then Global Options to display the Global Options dialog box. On the Global Options dialog box, select AntFarm, then Build Options. Set the radio button labeled Run Ant Targets In The Same JVM and check the box labeled Load Build Files From The Project Viewer Plugin. Then click the OK button.

To run a build for the JDocPlugin, change the classpath in the build.xml file to match your environment. Select the ProjectViewer tab and double-click the build.xml icon to edit the build.xml file. Locate project.class.path and change the jar files' locations to the path on your system (either the jEdit installation directory or the user directory). Use the following as a guide:

   <property name="jedit.install.dir"
      value="/Program Files/jedit 4.1/jars"/>
   <property name="user.install.dir"
      value="/Documents and Settings/Bob/.jedit/jars"/>
   <path id="project.class.path">
      <pathelement location="${user.install.dir}/ErrorList.jar"/>
      <pathelement location="${user.install.dir}/Console.jar"/>
      <pathelement location="${jedit.install.dir}/jedit.jar"/>
   </path>

Now click the AntFarm tab and you should see an icon for the JDocPlugin build file. Open it and select the Compile target. Click the green arrow icon to execute the target. Ant will write the build output to a jEdit console window.

JUnit

JUnit is a unit test framework that facilitates automated testing. You can improve software quality and shorten development time if you run automated unit tests to verify correct operation of modified code. Use the JDocCommandTest class (in the jdoc.options package) to familiarize yourself with the JUnit plug-in's operation. This class lacks a main() method that calls a JUnit test runner because the JUnit plug-in performs this task.

Configure the JUnit plug-in to run the JDocCommandTest JUnit test class that you compiled in the AntFarm example. Click on the JUnit plug-in tab and click the grid icon to display the JUnit classpath dialog box. Enter JDocCommandTest's path and click OK. Next, type the full class name, jdoc.options.JDocCommandTest, on the JUnit dialog box's first line and press Enter. If you've configured the JUnit plug-in correctly, the dialog will display a green bar indicating a successful JUnit test. Note that you can select from many JUnit tests by clicking on the folder icon and selecting the desired test.

Extreme jEdit

Now that you've built an IDE with source code version control, automated build management, and unit testing, how do you use it? It just so happens that these features are required for an extreme programming (XP) approach. Although XP is a software development methodology that doesn't depend on any particular tools, the right tools are helpful.

Gruntspud integrates a CVS client into jEdit and facilitates collective code ownership and iterative programming. XP programmers share the entire codebase and develop software in many small steps. CVS allows multiple programmers to synchronize concurrent changes to the same file.

The AntFarm plug-in integrates Ant into jEdit and facilitates continuous integration. Ant provides an automated and consistent build process that eliminates error-prone manual processes. Store the build script and required jar files in the version control and build the application from the version control to guarantee all developers a consistent environment. Ant encourages frequent builds, which is fundamental to the XP approach.

The JUnit plug-in facilitates test-driven development. Automated unit testing represents one of XP's most important ideas. When you write code, you also write a unit test for the code. When the code passes the test, it is complete. Automated regression testing is a core concept of the XP approach.

Other plug-ins also contribute to an XP approach. Since XP teams practice collective code ownership, you need a consistent coding standard to minimize the learning curve when looking at other team members' code. The JavaStyle plug-in can be used to format Java source files according to a single coding standard. Another XP concept is simple, self-documenting code. The JDocPlugin can be used to create HTML documentation from the Java code.

So, how might you develop with this jEdit IDE using an XP approach? Here's a prototypical scenario:

  1. Create a CVS working directory using Gruntspud to perform a CVS checkout command.
  2. Create a project for this working directory using the ProjectViewer plug-in. Import the files from the CVS working directory into the project.
  3. Modify source files to add new functionality and write JUnit tests to unit test the new feature(s).
  4. Write Javadoc comments for new application classes or methods or new JUnit tests. Generate Javadoc with the JDocPlugin and view it using the InfoViewer plug-in.
  5. Run a build for the project using the AntFarm plug-in.
  6. Run unit tests with the JUnit plug-in.
  7. If the tests fail, fix the code and repeat Steps 5 and 6.
  8. Perform a CVS update to ensure your code is in sync with the repository.
  9. Run the build and unit tests again.
  10. If all tests pass, use Gruntspud to perform a CVS commit to send your changes to the CVS repository.

jEdit extensibility: How to build a jEdit plug-in

But, what if you need features jEdit doesn't support? In my case, I wanted integrated, graphical Javadoc generation, so I developed the Javadoc plug-in. The plug-in architecture simplified this task because it provides a simple mechanism for extending jEdit to support new development requirements. You implement the jEdit Plugin API the same way for any jEdit plug-in, regardless of the application. GruntSpud, AntFarm, JUnit, Project Viewer, and the Javadoc plug-in all implement the same jEdit Plugin API. So, a step-by-step review of the Javadoc plug-in will show you how to build a jEdit plug-in. Two components play an important role in this extensibility: BeanShell and the jEdit Plugin API. BeanShell joins the plug-in to the jEdit host, and the Plugin API is the interface between jEdit and the plug-in.

BeanShell

BeanShell is an open source project developed by Pat Niemeyer as an example in his book, Learning Java. It is a small Java source interpreter written in Java that executes standard Java syntax as well as some scripting statements. It represents the glue that binds plug-ins to jEdit. The action.xml file forms the primary interface between the jEdit host and a plug-in application. The action tags contained in this file are short BeanShell scripts that execute plug-in methods. jEdit executes these BeanShell scripts when the corresponding plug-in menu items are selected.

The jEdit Plugin API

The jEdit Plugin API is a simple one, consisting of a plug-in core class (one with a name ending in Plugin) that implements four public methods of the abstract EditPlugin class: start(), stop(), createMenuItems(), and createOptionPanes(). jEdit calls these four methods at specific times, thereby creating the interface between itself and the plug-in. The start(), createMenuItems(), and createOptionPanes() methods are called when jEdit starts. The stop() method is called when jEdit exits. To implement its functionality, the plug-in core class overrides these methods with its application-specific code; otherwise, jEdit executes the empty methods in the EditPlugin class. Although the plug-in core class is not required syntactically to override any of these methods, it needs to override the createMenuItems() method to display a menu within the Plugins menu and the createOptionPanes() method to display a menu in the Global Options dialog box.

Additionally, the plug-in needs to define resources that jEdit uses to build the plug-in's user interface components. At a minimum, the plug-in needs two resources, an action file and a properties file:

  • actions.xml: this file contains a BeanShell script that executes plug-in core class methods
  • <plugin>.props: this file contains name and value properties that define the plug-in menu items and other information

If the plug-in is dockable (one that can be minimized to the top, bottom, or sides of the jEdit user interface), then the plug-in also needs to provide a dockables.xml file.

This section described jEdit Plugin API's parts; the following example shows how the parts fit together.

jEdit plug-in example: The Javadoc plug-in

The best way to explain a programming interface is by illustrating it with an example, so I'll review the Javadoc plug-in for jEdit, which displays option dialogs to collect javadoc runtime arguments and runs the javadoc command within jEdit. It is a simple plug-in that performs the following five tasks:

  1. Implements the jEdit Plugin API for communication between jEdit and the plug-in
  2. Creates option dialogs that collect the javadoc runtime arguments
  3. Invokes the Runtime.exec() method to run the javadoc command
  4. Displays javadoc output using the Console plug-in
  5. Persists the values of the javadoc command-line arguments in a file

To keep the example simple, I concentrate on the code that implements the jEdit Plugin API.

Explore The Javadoc plug-in

We begin our exploration of the Javadoc plug-in by looking at the plug-in core class, JDocPlugin.java. This class name is set by convention—when jEdit loads the plug-in, it looks for a core class with a name ending in Plugin.class.

Class JDocPlugin.java implements all four of the methods in the jEdit Plugin API as well as some other methods I discuss later. jEdit executes this class's methods at specific points in its execution.

The start() method

When jEdit starts, it executes JDocPlugin's start() method. This method performs two initialization tasks. First, it initializes name and value properties for the javadoc command-line arguments. Second, it calls the Shell class's registerShell() method to make the Console plug-in aware of the Javadoc plug-in's shell, the JDocShell class. This class executes the javadoc command using the Console plug-in's exposed methods.

The stop() method

When jEdit exits, it calls the JDocPlugin class's stop() method. This method performs two tasks: it calls the Shell class's unregisterShell() method to release the resources. Second, it calls a helper method in the JDocPlugin class to persist the properties for the javadoc command-line arguments to storage.

The createOptionPanes() method

jEdit calls the createOptionPanes() method when it creates the Global Options dialog box. The method is just a few lines of code, but the jEdit classes do a lot of work. The OptionGroup class displays multiple option panes shown as one branch in the option dialog box. The addOptionPane() method adds the option pane to the option group. Each of the separate option panes is implemented as a separate class that extends the jEdit AbstractOptionPane class. Despite the name, AbstractOptionPane is not an abstract class and implements much of the option pane's functionality. The option pane classes need to only implement a constructor, and the _init() and _save() methods. The following code in the createOptionPanes() method creates multiple option panes in the Global Options dialog for the Javadoc plug-in.

   public void createOptionPanes(OptionsDialog od) {
      OptionGroup jdocGroup = new OptionGroup("jdoc");
      jdocGroup.addOptionPane(new JDocGeneralOptionPane());
      jdocGroup.addOptionPane(new JDocVisibilityOptionPane());
      jdocGroup.addOptionPane(new JDocPathOptionPane());
      jdocGroup.addOptionPane(new JDocDirectoryOptionPane());
      jdocGroup.addOptionPane(new JDocFlagOptionPane());
      jdocGroup.addOptionPane(new JDocFormatOptionPane());
      od.addOptionGroup(jdocGroup);
   }

These option panes provide a GUI for entering the javadoc command-line arguments. Similar arguments group into separate option panes. Alternatively, a plug-in could add a single option pane to the option dialog box with the OptionsDialog()addOptionPane() method. Figure 5 shows the option pane for the path options.

Figure 5. Global Options dialog box

The createMenuItems() method

To allow users to select plug-in actions, jEdit plug-ins display a submenu within the jEdit plug-in menu. The createMenuItems() method with one argument and one line of code in its body is the most important interface between jEdit and the Javadoc plug-in. It allows jEdit to build a menu for the external plug-in component and link the menu items to dynamically executed code. The relevant code in JDocPlugin.java is as follows:

   public final static String MENU = "jdoc-menu";
   ...
   public void createMenuItems(Vector menuItems) {
       menuItems.addElement(GUIUtilities.loadMenu(MENU));
   }

jEdit calls createMenuItems() when it builds the plug-ins menu for the Javadoc plug-in. The method's argument is a vector of the installed plug-in menus. The call to the GUIUtilities.loadMenu() method creates the top-level menu for the Javadoc plug-in, which is added to this vector. The argument passed to this method is the name of the menu property in the Javadoc plug-in properties file. Every jEdit plug-in needs a property file that contains the key and value pairs for the menu item names and the menu labels that jEdit displays. The following fragment from the JDocPlugin properties file describes a submenu with three labels:

   #
   # jdoc.props application menu properties
   #
   jdoc-menu=jdoc - jdoc-help - jdoc-options
   jdoc-menu.label=Javadoc
   jdoc.label=Execute Javadoc
   jdoc-help.label=Javadoc Help
   jdoc-options.label=Javadoc Options...

This value of the jdoc-menu property contains three property names, which are the menu item properties for this plug-in menu. The dash (-) character separates the three values in the jdoc-menu property. When the plug-in's menu is selected, the item properties look up the labels for the menu items (or submenus: if a percent sign (%) precedes the item property names, jEdit builds submenus recursively).

jEdit uses the property file's item names (jdoc, jdoc-help, jdoc-options) to link to the XML action name attributes in the JDocPlugin's actions.xml file. This file contains the top-level \ <ACTIONS\> tag and three <ACTION\> tags within it for each of the three menu items. The actions.xml file for the Javadoc plug-in that follows shows how the menu item name is used to link the menu item to its action, which is a method in the JDocPlugin class:

   <!-- javadoc plugin actions.xml file -->
   <ACTIONS>
      <ACTION NAME="jdoc">
         <CODE>
            jdoc.JDocPlugin.executeJavadoc(view, "execute");
         </CODE>
      </ACTION>
      <ACTION NAME="jdoc-help">
         <CODE>
            jdoc.JDocPlugin.executeJavadoc(view, "help");
         </CODE>
      </ACTION>
      <ACTION NAME="jdoc-options">
         <CODE>
            new jdoc.options.JDocOptionDialog(view);
         </CODE>
      </ACTION>
   </ACTIONS>

During startup, when jEdit loads the plug-in, it builds an internal data structure of actions from these \<ACTION\> tags. The action names in the actions.xml file correspond to the item names in the property, establishing the link between the menu item and the action. The \<CODE\> tag within each action contains a short BeanShell script. The view variable in each method's argument is a BeanShell variable, which contains a reference to jEdit's top-level user interface component.

Each BeanShell script within the \<CODE\> tags executes a method in the plug-in core class that performs that action's work.

In the code above, the JDocOptionDialog() method displays the same option dialog box as the Global Options menu. This tabbed dialog box allows you to set all command-line arguments for the javadoc command.

The executeJavadoc() method with the execute argument runs the javadoc command using the command-line argument settings configured in the option dialog box.

The executeJavadoc() method with the help argument runs the javadoc command with the -h command-line argument and displays the javadoc help output on the jEdit console.

Supporting classes

So far, this article has concentrated on showing how the JDocPlugin class implements the jEdit Plugin API and the plug-in resources (the actions.xml file and plug-in properties file) to illustrate how to implement a jEdit plug-in. These components are similar for any jEdit plug-in. The Javadoc plug-in also contains numerous classes that perform the actual work of the plug-in, such as setting the javadoc command-line options and executing the javadoc command. The implementation of these classes depends on the function of the particular plug-in, but these classes also use some common jEdit GUI facilities. This section introduces these classes.

Option pane display

The option pane classes provide the user interface to enter plug-in options. All option pane classes encapsulate the jEdit AbstractOptionPane class. This jEdit class's name is misleading because it is not an abstract class. It extends the Swing JPanel class, implements the jEdit OptionPane interface, and provides a default framework for option pane classes. Many classes in the jdoc.options package display the option dialog boxes for the Global Options menu and the JDocPlugin Options menu.

The JDocOptionDialog class displays the dialog box with tabbed panes for the Javadoc Options menu. Figure 6 shows the Javadoc Options dialog box.

Figure 6. Javadoc Options dialog box

The JDocCommand class manages constants for all Java commands and property keys.

The option pane classes that set different groups of command-line arguments extend the jEdit AbstractOptionPane class. These classes include: JDocDirectoryOptionPane, JDocFlagOptionPane, JDocFormatOptionPane, JDocGeneralOptionPane, JDocPathOptionPane, and JDocVisibilityOptionPane.

The DisplayPane class is a helper class used by all option pane classes to build the main pane and tool-tip area.

Javadoc command execution

Three classes execute the javadoc command: JDocShell, JDocTask, and JDoc. The JDocPlugin class uses the Runtime.exec() method to run the native javadoc executable. Discerning how to use this method is not intuitively obvious from the Java documentation. I used Michael J. Daconta's article "When Runtime.exec() Won't" (JavaWorld, December 2000) and the JCompiler plug-in's source code to guide me.

JDocShell extends the Console plug-in's Shell class and provides an object that encapsulates the command execution threads. JDocTask extends the Thread class and provides the command's main execution thread. JDoc executes the native javadoc command using the Runtime.exec() method, runs threads to collect the command's standard output and standard error output, and writes javadoc output to the console.

While I developed the JDocPlugin, the jEdit Plugin API simplified the interface to jEdit and allowed me to concentrate on the application logic: collecting and storing the values for the javadoc command-line arguments, and executing the javadoc command.

Why pay for an IDE?

Plug-ins transform the jEdit programmer's text editor into a powerful IDE that can be used for an XP software development approach. The jEdit Plugin API allows integration of free tools like CVS, Ant, and JUnit, and development of custom applications to meet new requirements.

jEdit demonstrates the vitality of the open source model. A community numbering more than 4,000 users supports its rapid development. The software is sophisticated and reliable. So, why pay for proprietary development software when you can achieve the same result with free software?

Robert Swarr is a Java consultant with more than 20 years of programming experience and a contributor to the jEdit open source project. For the last five years he has specialized in server-side Java consulting on projects using BEA WebLogic and IBM WebSphere. He has worked as a software support specialist in major computer and software vendor corporations, and as a programmer in user organizations. He has taught Java as an adjunct professor at Connecticut State University. He holds a MS in computer science from Rensselaer Polytechnic Institute and is a Sun Certified Developer for the Java 2 Platform. He operates his own consulting practice AgoraLogos in Connecticut.

Learn more about this topic