Prerequisites:
- Understanding of Java and Java Syntax
- Familiarity with JBuilder
- Understanding of JBuilder OpenTools API is helpful but not necessary
Background
An important function of an integrated development environment (IDE)
is generating repetitive and trivial source code. JBuilder supports
source code generation through wizards found in the Object Gallery
(see Figure 1).
The Object Gallery can be located by selecting File | New from the
main menu. Depending on whether JBuilder Enterprise, Professional
or Personal is installed, varying wizards ranging from the basic Class and Project
wizards to complex EJB and CORBA wizards will be enabled. When
invoked, wizards display a series of dialogs that requests information
from the developer. The information provided is then used to generate
the desired source code.
Figure 1 - JBuilder 5.0 Enterprise Object Gallery
Problem
Like most JBuilder users, I use the Class wizard on a regular
basis. The Class wizard is very effective at generating basic class
skeleton code (see Listing 1). However, the
results are too generic and require additional manipulation. This is
especially true for organizations that have strict coding standards.
For example, I often have to perform two additional operations to format
the source code generated by the class wizard to comply with my organization's
required standards.
First, I move the generated project comments prior to the import statements
since they document the project and not the class itself. Second, I
add a predefined licensing agreement to the top of the java file using
a code template (ctrl+j) (see Listing 2). While these may seem like
petty requests, doing this for every class does add up to a significant amount
of time.
package com.actsi.ot.wizard.classes;
import com.borland.primetime.wizard.BasicWizard;
/**
* Title: Gallery Open Tools
* Description: Demonstration of using JBuilder OpenTools API and Apache Velocity to create flexible Object Gallery Wizards.
* Copyright: Copyright (c) 2001
* Company: ACTS, Inc. (www.actsi.com)
* @author Christopher M. Judd (juddc20@hotmail.com)
* @version 1.0
*/
public class ClassWizard extends BasicWizard {
public ClassWizard() {
}
}
Listing 1 - Default generated class code
/**
* Copyright (c) 2001 Advanced Computing Technical Services Inc.
* (ACTS, Inc - www.actsi.com). All rights reserved.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ACTS, Inc. OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package com.actsi.ot.wizard.classes;
/**
* Title: Gallery Open Tools
* Description: Demonstration of using JBuilder OpenTools API and Apache
* Velocity to create flexible Object Gallery Wizards.
*/
import com.borland.primetime.wizard.BasicWizard;
/**
*
* @author Christopher M. Judd (juddc20@hotmail.com)
* @version 1.0
*/
public class ClassWizard extends BasicWizard {
public ClassWizard() {
}
}
Listing 2 - Generated code after making changes
The goal of this article is to demonstrate how to generate
source code by creating a new JBuilder class wizard that will use a
flexible template based approach.
This technique can save time and generate source code that better adheres
to company standards. A secondary goal is to demonstrate how JBuilder can be
extended through its OpenTool API.
The flexibility of a template based approach allows developers to change the
resulting source code without modifying and recompiling the wizard. This is advantageous
since most wizards including those shipped with JBuilder do not provide
the source code to be modified.
JBuilder OpenTools API
It's not practical for JBuilder to provide every possible wizard developers need.
It would take Borland entirely too much time, money and marketing
research to make JBuilder aware of every Java API. Instead, the JBuilder development team
has provided a general Java IDE with the basic wizards and an extremely
extensible application programming interface (API). JBuilder extensions
are called OpenTools and use JBuilder's OpenTools API to produce plug-ins
similar to those found in web browsers. Examples of potential OpenTools
extensions would include custom wizards, nodes, menus, structure
viewers and content viewers. Almost every aspect of JBuilder is
accessible through the OpenTools API.
This article discusses one such OpenTool, the wizard, but if you are
interested in learning more about the capabilities of the OpenTools
API check out the documentation that ships with JBuilder. The documentation
can be found by selecting Help | Help Topics from the JBuilder main
menu. In the contents list there is a JBuilder OpenTools Document
node. Double clicking the node reveals a Developing OpenTools node
that describes creating OpenTools in a tutorial format and a JBuilder
OpenTools API Documentation node with content in Java Doc format.
Many developers have created OpenTools to make Java development
more productive. Some developers openly share them with the rest
of the JBuilder community. See websites listed in Resources for
available OpenTools.
I am often asked why JBuilder does not support specific features or
customizations, I respond with, "Because you have not created an
OpenTool for that". Borland has enabled us to make ourselves more productive.
Don't be shy, take advantage of it.
Cookie Cutter Solution
A template is a document containing both static
and dynamic content defined at execution time.
A template engine combines both the static and dynamic content to
produce the desired output. In recent years, template based approaches
have become extremely popular due to their flexibility and ease of modification.
This is not only true for source code generation, web development has completely
changed due to template driven approaches. JSP (JavaServer
Pages) (see Listing 3), ASP (Active Server Pages) and XSLT (eXtensible
Stylesheet Language Templates) are all examples of such approaches that
can produce HTML.
<html>
<head>
<title>SimpleBeanJSP</title>
</head>
<jsp:useBean id="SimpleBeanId" scope="session" class="com.simple.beans.SimpleBean" />
<jsp:setProperty name="SimpleBeanId" property="*" />
<body>
<h1>JBuilder Generated JSP</h1>
<form method="post">
<br>Enter new value : <input name="sample"><br>
<br><br>
<input type="submit" name="Submit" value="Submit">
<input type="reset" value="Reset">
<br>
Value of Bean property is :<jsp:getProperty name="SimpleBeanId" property="sample" />
</form>
</body>
</html>
Listing 3 - JavaServer Page (JSP)
For the new class wizard, the basic stub of a class will be defined
as the template while a developer will define the dynamic content using
the wizard dialog. This approach adapts to organizational requirements and
standards by allowing developers to modify or customize the template to meet their
needs.
Not so open OpenTools API
The OpenTools API does include a Template Engine. This might be obvious
since JBuilder has a templates directory which contains a number of *.template
files. However, this portion of the OpenTools API and the template
syntax is undocumented. Historically, undocumented portions of the OpenTools
API are unsupported and subject to change. This makes them especially difficult to use.
Additionally, the template engine and related classes are only used by the IDL
(Interface Definition Language) wizards.
Some of the OpenTools API Template Engine classes are difficult to use because they
appear to be tightly coupled to the IDL wizards. Another challenge I found
using the OpenTools API Template Engine was conditional statements
must exist on their own line causing unusual line breaks.
If you are concerned about using the OpenTools API Template Engine after the
issues I have raised, skip to the next section where I introduce an
alternative template engine. Otherwise Listing 4
and 5 demonstrate using the
OpenTools API Template Engine and template syntax respectively.
The OpenTools API Template Engine classes can be found in the
com.borland.jbuilder.template.engine package. The first step in using the engine
is to create an instance of the TemplateEngine class and set the current project.
Next, add key/value pairs using the TemplateEngine's put() method.
The TemplateItem class
identifies the template file from JBuilder's template directory, the output file and
the package it belongs in.
The template is then processed by invoking the executeTemplate() method on the
TemplateEngine instance identifying the TemplateEngine, TemplateItem instance
and whether the standard header should be included. The processing of the template
returns a Node containing the resulting stream.
The OpenTools API Template Engine syntax denotes statements with [].
Using the values contained with in the Template Engine's key/value pairs
requires a simple call to a get() method indicating the key name.
Conditional statements use an IF/ENDIF construct.
protected void finish() throws VetoException {
super.finish();
try {
TemplateEngine te = new TemplateEngine();
if(jbproject != null) {
te.setProject(jbproject);
// copy the properties from the dialog to the template engine.
Properties props = classPage.getProperties();
String key = null;
for(Enumeration enum = props.propertyNames(); enum.hasMoreElements(); ) {
key = (String)enum.nextElement();
te.put(key,props.getProperty(key));
}
// determine file name
String classname = props.getProperty("Class","Untitled");
String packagename = props.getProperty("Package","");
Url[] sourcePaths = jbproject.getPaths().getSourcePath();
String outputFile = sourcePaths[0].getFullName()+ "/" + packagename.replace('.','/') + "/" + classname + ".java";
//template filename (defaults to templates directory)
//output filename,output package
TemplateItem ti = new TemplateItem("class.jbte.template",
outputFile,
packagename);
// Generate source code
Node node = te.executeTemplate(te,ti,false); //template engine, template item, use standard header
// Open node in Content Pane
Node[] nodes = new Node[1];
nodes[0] = node;
Browser.getActiveBrowser().openNodes(nodes, node);
// Refresh Project Pane
host.getBrowser().getProjectView().refreshTree();
}
} catch (Exception ex) {
if(PrimeTime.isVerbose()) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
}
Listing 4 - Example of a method using the OpenTools API Template Engine
(com.actsi.to.wizard.classes.jbte.ClassWizard)
[IF get("Package") != ""]
package [get("Package")];
[ENDIF]
[get("Public")] class [get("Class")] extends [get("SuperClass")]{
[IF get("Constructor") == "true"]
public [get("Class")]() {
}
[ENDIF]
[IF get("Main") == "true"]
public static void main(String[] args) {
}
[ENDIF]
}
Listing 5 - Example of OpenTools template syntax (class.jbte.template)
Generating with Velocity
Due to the challenges and objections of using the OpenTools API Template Engine, I have chosen
to use a different template engine. This does require deploying an additional
development environment jar, but that seems like a fair tradeoff for the benefits of
ease of use and clear documentation. Using a
third party template engine also
provides the ability to use the templates outside of JBuilder. Initially, when
I searched for a third party template engine I thought I would create a framework
that would hide the details of which template engine was being used. My goal
was to make it work like the JAXP API. However,
since each template engine has a proprietary syntax unlike XML, switching
template engines would require a rewrite of the actual template. Therefore,
I did not take the time to decouple the wizard from the template engine.
My hope is in the future Borland will decouple the OpenTools API
Template Engine from the IDL wizards making it a multiple purpose template
engine as well as
provide good documentation on the API and template syntax.
As I mentioned before, there are many template based approaches in web development
today. One such template engine is Velocity, an Open Source Java-based
Apache Jakarta sub project. See Resources
for links to download it, the documentation and related articles. It
was originally implemented to support the Model 2 or Model-View-Controller
(MVC) web architecture. This architecture allows for a separation of presentation,
data and workflow. But because Velocity was implemented as a generic
template engine, it can be used to generate just about any text output
including Java source code.
Velocity Template Syntax
One of Velocity's advantages is its simple but powerful syntax. The syntax
supports comments, variables, conditions, loops and includes as well as other
features. See Listing 11 for a full listing of the
example template that accompanies this article.
Comments
Single line comments are denoted by a ##. Everything between the
## and the end
of the line is ignored by the template engine. Comments can be an important part
of documenting your template. Especially if someone else will be modifying the
template. In this example, I
choose to document every variable passed to the template by identifying the name,
type and description of the reference (see Listing 6).
## Variables:
## License String determines whether license should be displayed
## Comments String determines whether comments should be displayed
## Company String project property company
## Class String class name
## SuperClass String super class name
## Methods List of Strings list of abstract method signatures
Listing 6 - Use of Velocity comments to document template
Variables
Variables are referenced by a $variable or the formal notation ${variable}.
Variables can refer to any object reference providing access to the object's attributes
and methods. Variables can be passed to the template engine using the Context class
(see Listing 7) or declared within the template itself with the #set
directive (see Listing 8).
Context context = TemplateFactory.getContext();
context.put("Author",jbproject.getProperty("sys","Author",""));
context.put("Title",jbproject.getProperty("sys","Title",""));
Template template = TemplateFactory.getTemplate("class.template");
FileWriter fw = new FileWriter(outputFile);
template.merge(context,fw);
fw.close();
Listing 7 - Use of a Context instance to set variables for the template
#set( $Public = "public ")
Listing 8 - Setting a variable of Public with inside the template
Conditions
The #if directive can be used to conditionally perform some action within the
template. Often this is used to include or exclude a portion of the output.
Listing 9 demonstrates using the $License variable to determine whether the
license.template should be included in the output.
#if ($License == "true")
#include( "license.template" )
#end
Listing 9 - Use of the conditional and include directives
Loops
The #foreach directive is used to iterate through a List, Map or Array. Using
Listing 10 as an example, the List $Methods
is iterated though for each item
in the list. During each iteration, the instance in the list is assigned
to the $method variable. The $method variable can then be used like any
variable.
#foreach( $method in $Methods )
$method {
}
#end
Listing 10 - Example of a looping directive
Includes
Velocity supports the include directive for reusing or separating portions of
the template. In this example, I chose to split the license into its own
template to allow it to be used
by multiple source code templates (see Listing 9).
## Variables:
## License String determines whether license should be displayed
## Comments String determines whether comments should be displayed
## Constructor String determines whether the constructor should be displayed
## Main String determines whether the main method should be displayed
## Public String determines whether the class is public
## Package String package name
## Title String project title
## Description String project property description
## Version String project property version
## Author String project property author
## Copyright String project property copyright
## Company String project property company
## Class String class name
## SuperClass String super class name
## Methods List of Strings list of abstract method signatures
#if ($License == "true")
#include( "license.template" )
#end
#if ($Package != "")
package $Package;
#end
#if($Comments == "true")
## todo: get specific comments
/**
* Title: $Title
* Description: $Description
*/
/**
*
* @author $Author
* @version $Version
*/
#end
#if($Public != "")
#set( $Public = "public ")
#end
${Public}class ${Class}#if($SuperClass !="") extends ${SuperClass}#end {
#if($Constructor == "true")
public ${Class}() {
}
#end
#if($Main == "true")
public static void main(String[] args) {
}
#end
#if($Abstract == "true")
#foreach( $method in $Methods )
$method {
}
#end
#end
}
Listing 11 - Complete class.template file
Abracadabra
To utilize the template, the JBuilder OpenTools API will be used to create
the simplest of OpenTools, the wizard. A wizard can either reside in the
Object Gallery or on the Wizard menu. Because template wizards will generate
new files they will most likely reside in the Object Gallery.
Most of the classes related to building
wizards are located in the com.borland.primetime.wizard package.
OpenTool Project Setup
Setting up a template wizard project requires configuring some libraries and
setting some project properties. The library configurations should be named Velocity and JBuilder.
The Velocity library should list the velocity-1.1.jar file in the class tab. To compile
the project and allow for debugging, add the following jars from JBuilder's
lib directory to the JBuilder library: jbuilder.jar, gnuregexp.jar, jdcl.jar
lawt.jar, xml4j.jar, xerces.jar, parser.jar, jdom.jar, help.jar. Every jar
in JBuilder's lib directory could be added but it is not necessary and just
slows down the loading of JBuilder during debugging. If during the debugging
process an existing OpenTool is needed, it will also need to be added from
JBuilder's lib/ext directory.
Debugging an OpenTool involves starting a new JBuilder process from within
JBuilder. It is in the new process that the OpenTool is loaded for testing.
Enabling OpenTool debugging requires the setting of
project properties. On the Project | Project Properties...
Run tab, set the Main Class to com.borland.jbuilder.JBuilder and the Application
Parameters to -verbose -nosplash. Now pressing the Run Project or Debug Project
buttons will start another instance of JBuilder. Breakpoints and other debugging
techniques work as normal.
TemplateFactory
Velocity's template engine requires some initialization before its use.
To consolidate the initialization, I use the Factory design pattern. In the
static section of the class, the file.resource.loader.path property is set to
use JBuilder's template directory. Additionally, the default
behavior of Velocity is to log the activities of the engine. In this case,
logging is not necessary so the runtime.log.logsystem.class property is set
to load the com.actsi.ot.util.EmptyLogSystem which implements Velocity's
org.apache.velocity.runtime.log.LogSystem interface and ignores the calls
(see Listing 12).
public class EmptyLogSystem implements LogSystem {
/** Does nothing (like most of my code) */
public void logVelocityMessage(int level, java.lang.String message) {}
}
Listing 12 - com.actsi.ot.util.EmptyLogSystem ignores logging requests
The TemplateFactory also provides a simple interface for obtaining a Template
and Context (see Listing 13).
public class TemplateFactory {
// Configure Velocity Template Engine
static {
Properties p = new Properties();
p.setProperty("runtime.log.logsystem.class","com.actsi.ot.util.EmptyLogSystem");
p.setProperty("file.resource.loader.path", TemplateEngine.getTemplateDirectory());
try {
Velocity.init(p);
} catch (Exception ex) {
if (PrimeTime.isVerbose()) {
System.out.println("Error configuring Velocity Template Engine:");
ex.printStackTrace();
}
}
}
// Prevents class from being instanciated
private TemplateFactory() {}
/**
* Template representing the template file found in the JBuilder template directory
* @param templateName template file name
* @return Template instance
* @throws ParseErrorException Error parsing template file
* @throws ResourceNotFoundException Template file not found
* @throws Exception Not sure
*/
public static Template getTemplate(String templateName) throws ParseErrorException, ResourceNotFoundException, Exception {
return Velocity.getTemplate(templateName);
}
/**
* Data context
* @return Context instance
*/
public static Context getContext() {
return new VelocityContext();
}
Listing 13 - Entire com.actsi.ot.util.TemplateFactory class
Wizard Classes
Creating a JBuilder wizard generally requires extending three classes:
BasicWizard, WizardAction and BasicWizardPage. The BasicWizard is the
concrete class that simplifies implementing the Wizard interface. In
this case the class extending BasicWizard is the ClassWizard.
Depending
on the requirements of the wizard; the
finish(), wizardCompleted() and invokeWizard()
methods may need to be overridden. The wizardCompleted() method is invoked after
finish() completes or the cancel button was selected. This is a good place
to clean up resources. Since there are no resources to clean up in the ClassWizard
the wizardCompleted()
method will rely on the empty method implementation of the BasicWizard class.
The invokeWizard() method is used to determine the first page of the wizard and
perform some initialization. Listing 14
shows the initializations of private data members as well as the wizard's
title and pages. The selecting of the initial wizard page is delegated to the
superclass BasicWizard which selects the first page added using the addWizardPage()
method.
public WizardPage invokeWizard(WizardHost host) {
this.host = host;
Project p = host.getBrowser().getActiveProject();
if (p instanceof JBProject) {
jbproject = (JBProject) p;
}
setWizardTitle("New Class Wizard (Velocity Template Engine)");
classPage = new ClassWizardPage(jbproject);
addWizardPage(classPage);
return super.invokeWizard(host);
Listing 14 - ClassWizard's invokeWizard method
Most of the real work performed by a wizard is done in the finish() method. The
ClassWizard's finish() method begins by getting a reference to a Velocity Context
by using the TemplateFactory. The Context reference is using to keep track
of the key/value mapping that will ultimately be referred to as variables
in the template. Here the context is initialized using the values set by the
user through the wizard dialog as well as the project comments set during the execution
of the Project Wizard. Next the class and package names are used to
derive the output file name for initializing the FileWriter. To simplify
acquiring the template reference use the TemplateFactory's getTemplate() method
telling it the specific template file. The Template reference is responsible
for combining the context and template using the merge() method
(See Listing 15).
protected void finish() throws VetoException {
super.finish();
try {
Context context = TemplateFactory.getContext();
// copy the properties from the dialog to the template engine.
Properties props = classPage.getProperties();
String key = null;
for (Enumeration enum = props.propertyNames(); enum.hasMoreElements(); ) {
key = (String) enum.nextElement();
context.put(key, props.getProperty(key));
}
// copy comments to properties
context.put("Author", jbproject.getProperty("sys", "Author", ""));
context.put("Title", jbproject.getProperty("sys", "Title", ""));
context.put("Description", jbproject.getProperty("sys", "Description", ""));
context.put("Version", jbproject.getProperty("sys", "Version", ""));
context.put("Copyright", jbproject.getProperty("sys", "Copyright", ""));
context.put("Company", jbproject.getProperty("sys", "Company", ""));
if (props.getProperty("Abstract").equals("true")) {
context.put("Methods", getAbstractMethods(props.getProperty("SuperClass")));
}
String classname = props.getProperty("Class", "Untitled");
String packagename = props.getProperty("Package", "");
Url[] sourcePaths = jbproject.getPaths().getSourcePath();
String outputFile = sourcePaths[0].getFullName() + "/" + packagename.replace('.', '/') + "/" + classname + ".java";
Template template = TemplateFactory.getTemplate("class.template");
FileWriter fw = new FileWriter(outputFile);
template.merge(context, fw);
fw.close();
// Refresh Project Pane
host.getBrowser().getProjectView().refreshTree();
// Open node in Content Pane
Node node = jbproject.getNode(new Url(new File(outputFile)));
Node[] nodes = new Node[1];
nodes[0] = node;
Browser.getActiveBrowser().openNodes(nodes, node);
} catch (Exception ex) {
if (PrimeTime.isVerbose()) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
}
Listing 15 - ClassWizard's finish method (the real work)
For the wizard to appear in the Object Gallery, com.borland.primetime.wizard.WizardAction
must be extended. The subclass can be implemented as a static inner class of
the wizard to prevent managing an additional Java file. Create an instance of
a WizardAction using one of the many
overloaded constructors to define the short text, long text, mnemonic, small icon,
large icon, whether it is a gallery wizard and its category. The short text is
the label displayed beneath the icon. The long text is displayed in the tool tip
when the mouse pointer hovers over the icon. For a gallery wizard, the small icon
is unnecessary because only the large icon is displayed. Gallery Wizards need
to identify themselves as such using the galleryWizard parameter otherwise they
are added to the Wizards menu. The category identifies which tab the wizard appears
on. The default tab is 'New. JBuilder creates and adds custom tabs if the
category does not exist. The only other
requirement is to override the createWizard() method returning an instance of
a class implementing the Wizard interface such as the ClassWizard.
public final static Icon ICON_CLASSWIZARD = Icons.getIcon(com.actsi.ot.wizard.classes.ClassWizard.class, "ClassWizard.gif");
/**
* Display icon in Object Gallery
*/
public final static WizardAction WIZARD_Gallery =
new WizardAction("Velocity Template Engine Class", 'V', "Creates a new Java class",
BrowserIcons.ICON_BLANK,
ICON_CLASSWIZARD,
true) {
protected Wizard createWizard() {
return new ClassWizard();
}
};
Listing 16 - Wizard Action
To complete the wizard classes, extend BasicWizardPage to create the UI a
developer will interact with.
Because the BasicWizardPage is a subclass of JPanel, JBuilder's
UI designer can be used to layout UI controls. Overriding the checkPage() method
can be useful for validating data.
Registering
Once an OpenTool is complete, JBuilder must be told about it. Registering an
OpenTool is a two step process. First, a class must implement a static initOpenTool
method that calls the appropriate manager's registration method. The initOpenTool
is analogous to the main method of an executable class in that it is the main
entry point into the OpenTool. To register a wizard, the WizardManager's
registerWizardAction must be called for JBuilder to know it needs to be added
to the Object Gallery or the Wizard menu (see Listing 17).
public static void initOpenTool(byte majorVersion, byte minorVersion) {
if (majorVersion == PrimeTime.CURRENT_MAJOR_VERSION) {
WizardManager.registerWizardAction(ClassWizard.WIZARD_Gallery);
}
}
Listing 17 - Main entry point of the Class Wizard OpenTool
Step two tells JBuilder which class or classes have the initOpenTool() method.
This step is analogous to creating an executable jar by setting the Main-class
attribute in the manifest file to a class that contains a main() method. Using
the Main-class attribute
tells the virtual machine which class to execute. There are two
differences between the manifest of an executable and OpenTool jar.
An executable jar can have only one Main-class attribute and a single class
associated with it. While an OpenTool jar uses different attributes
to indicate when the initOpenTool() method should be called and may have more
then one class in the category. For a wizard the best attribute to use
is OpenTools-UI. The OpenTools-UI classes have their
initOpenTool() method called
the first time a JBuilder user invokes the Object Gallery or selects the Wizards
menu. This lazy loading prevents objects from unnecessarily being instantiated and makes
JBuilder load faster. Listing 18 shows the two wizards in this project
separated by spaces being initialized in the OpenTools-UI attribute. For
additional manifest attributes see the JBuilder OpenTool documentation.
Don't forget, as with all manifest files the last line must be a new line character.
Typically, the manifest file in an OpenTool project is named classes.opentools.
OpenTools-UI: com.actsi.ot.wizard.classes.jbte.ClassWizard com.actsi.ot.wizard.classes.ClassWizard
Listing 18 - classes.opentools file
Packaging
Now that the classes, images and manifest file are complete they need to be
packaged in a jar that can be easily distributed. If JBuilder Professional
or Enterprise is available use the Wizards | Archive Builder... to create
the jar. Otherwise, use the Jakarta Ant script that accompanies this
project. To find out more about Jakarta Ant see Resources.
NOTE: The Jakarta Ant script that accompanies this article is a very generic
OpenTool build script that can be use for other OpenTool projects by changing
the project and classpath property in build.properties.
NOTE: Use ant ? to list the available targets in the accompanying
build script.
Installation
Installing an OpenTool involves copying the OpenTool jar to JBuilder's lib/ext
directory. For this project the velocity-1.1.jar file must also be copied to
JBuilder's lib/ext directory. Additionally, the templates must be copied to
JBuilder's templates directory. Alternatively, the Jakarta Ant script that
accompanies this article contains a deploy target that performs the
installation if jbuilder.dir property in build.properties is set.
Conclusion
The combination of JBuilder OpenTool wizards and templates can be
a powerful method of generating other types of code besides a basic Java class.
An exercise left to the reader is creating wizards that generate code for
text, property, XML or other relevant files that make Java development
more productive.
Resources:
By Christopher M. Judd, Judd Solutions, LLC.