Part 1 of this article explained how to run Ant both inside WebSphere® Studio Application Developer (hereafter called Application Developer) and outside it ("headless"). Part 2 explained how special Ant tasks are used for Application Developer production Ant builds of J2EE modules. This part will explain how to create you own AntTasks to provide specialized build functions.
With Part 1 and Part 2 of this article, you have a good set of production Ant build capabilities. However, there may be one or two special things that your build has to do. The "Why use Ant" section of Part 1 explained that one reason to use Ant is that it is written in JavaTM, and you can therefore write new Ant tasks in Java to extend the build capabilities; you just write a new Java program to perform the special operation that you need.
This article will show you how to add two Ant tasks to Application Developer -- ProjectBuild and GetJavacErrors.
The two download files are not part of Application Developer V4 or V5 Early Availability (EA) and are not officially supported. However, Application Developer V5 General Availability (GA) includes these features in the product and they are fully supported. If you have suggestions or encounter problems with the older V4 or V5 EA features, please e-mail Barry Searle . V5 GA is supported via the normal IBM support channel.
The Version 4 download file (
com.ibm.ant.extras_V4.zip
) has been tested with Application Developer 4.0.2 and 4.0.3 and works as described, but will not work with future versions of the product (V5 or later). This code includes the setDebugInfo task from the article
Non-Debug compilations in WebSphere Studio Application Developer
.
The Version 5 Early Availability download file (
com.ibm.ant.extras_V5EA.zip
) has been tested with Application Developer 5.0 Early Availability and works as described. It will not work with future versions of the product (V5 GA or later) and it will not work with Version 4.x. The download code changes from Version 4 (described in this article) to Version 5 are:
-
HeadlessAntListener.javahas been renamed toHeadlessAntLogger.javaand now extendsorg.apache.tools.ant.DefaultLogger. -
HeadlessAntRunner.javano longer creates a newHeadlessAntLogger( ). Instead, it callsaddBuildLogger("HeadlessAntLogger"). -
HeadlessAntRunner.javano longer callslistener.setMessageOutputLevel(...)andsuper.processCommandLine(args).
Important: If you download
com.ibm.ant.extras_V5EA.zip
, you must unzip it into your
x:\Ws_installdir\
WSTOOLS\ECLIPSE
\plugins
directory, where it will create a
com.ibm.ant.extras_5.0.0
subdirectory containing a
runAnt.bat
program and the various programs it requires. (The Version 4 code went into the
x:\Ws_installdir\plugins
directory.)
Application Developer V5 General Availability
Application Developer V5 GA contains the Headless Ant features and is fully supported. Do
not
use the V4 or V5 EA downloads. The
RunAnt.bat
file is now in the directory
X:\WSAD5GA_INSTDIR\wstools\eclipse\plugins\com.ibm.etools.j2ee.ant_5.0.1
, and that directory also contains
readme.htm
. The Eclipse refresh task
<eclipse.refreshLocal>
has changed to
<eclipse.refreshLocal resource="MyProject/MyFolder" depth="infinite"/>
.
Overview of Part 1 of this article
Part 1 of this article explained:
- What Ant is
- Why it is often used
- How to run Ant inside Application Developer
- How to run Ant in a command-line window
- How to start Application Developer "headless"
-
How to download and use the
runAntprogram, which contains the "headless" Ant support.
Overview of Part 2 of this article -- J2EE builds
Application Developer is an excellent tool for developing J2EE modules (EJB JARs, WARs, EARs, etc). During development, some of the deployment descriptor information is stored in ASCII XML files, but these files contain some information in a format convenient for interactive testing and debugging. As part of the Application Developer functions that create (export) stand-alone J2EE modules, this internally optimized deployment descriptor information is merged and changed into standard format. Part 2 includes a set of Application Developer Ant Tasks to perform these build and export functions using the "headless" operations described in Part 1.
Requirement for WorkspaceModifyOperation
If you browse the open source code for WebSphere Studio Workbench, you will discover that its project class (
IProject
) has a
build(int type, IProgressMonitor monitor)
method. Therefore you can create a new
ProjectBuild
Ant task for Application Developer in which you just specify the project name and build type (full, auto, or incremental). As we are interested in running production builds outside Application Developer, we cannot use the normal GUI
ProgressMonitor
and instead need to create a small
AntConsoleProgressMonitor
(described below) to log progress events. Then we can run our new
ProjectBuild
program, which works as expected except for one problem. Application Developer is an integrated development environment. Any operation that potentially changes the workspace, including any resource modification, needs to be put in a
WorkspaceOperation
wrapper. Then, events that typically occur as a result of workspace changes (firing resource deltas, autobuilds, etc.) are deferred until the outermost operation has successfully completed, instead of occurring haphazardly as intermediate changes occur.
ProjectBuildWorkspaceModifyOperation
The open source code for WebSphere Studio Workbench has a
WorkspaceModifyOperation
that is widely used to solve the preceding problem. It creates an
IWorkspaceRunnable
with a run method that calls your custom execute method to do the actual work. You can subclass this "cookie-cutter" framework code for your particular operation. We will create a
ProjectBuildWorkspaceModifyOperation.java
as shown below. The main areas of interest are the constructor and the
project.build
invocation inside the execute method.
package com.ibm.ant.extras;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.CoreException;
public class ProjectBuildWorkspaceModifyOperation extends
WorkspaceModifyOperation
{
private IProject project;
private int buildTypeInt;
public ProjectBuildWorkspaceModifyOperation(IProject proj,
int buildType)
{
project = proj;
buildTypeInt = buildType;
}
// Performs steps as a single logical workspace change.
protected void execute(IProgressMonitor monitor) throws CoreException
{
String projectName = "unknown";
try
{ projectName = project.getDescription().getName();
monitor.setTaskName("Building: " + projectName);
project.build(buildTypeInt, monitor);
} catch (CoreException e)
{
throw e;
} finally
{
monitor.done();
}
}
}
|
Having created the above
ProjectBuildWorkspaceModifyOperation
, most of the work involved in creating the
ProjectBuild
Ant task is just "boiler-plate" checking of parameters, etc. The actual work inside the try/catch statement below is four lines.
-
Construct a non-GUI
AntConsoleProgressMonitor(discussed later) -
Construct the worker
ProjectBuildWorkspaceModifyOperation -
Run that
ProjectBuildWorkspaceModifyOperation -
In case of errors, see whether or not to
failonerror
A design decision that you need to make for every new Ant task is whether it needs to test and obey a
failonerror
parameter.
package com.ibm.ant.extras;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.IncrementalProjectBuilder;
public class ProjectBuild extends Task
{
private IProject project = null;
private String projectName;
private String buildTypeString = "INCREMENTAL";
private String failonerror = "true";
private boolean failOnError = true;
private int buildTypeInt =
IncrementalProjectBuilder.INCREMENTAL_BUILD;
public void execute() throws BuildException
{ validateAttributes();
project = ResourcesPlugin.getWorkspace()
.getRoot().getProject(projectName);
if(project==null)
{ displayError("ProjectBuild: " +projectName
+" project==null");
return;
}
if(!project.exists())
{ displayError("ProjectBuild: " +projectName
+" not found in Workspace.");
return;
}
if(!project.isOpen())
{ displayError("ProjectBuild: " +projectName
+" is not open");
return;
}
try
{ //non-GUI to display progress and handle cancel requests
AntConsoleProgressMonitor monitor = new
AntConsoleProgressMonitor(this);
monitor.setTaskName("Building: " + projectName);
ProjectBuildWorkspaceModifyOperation op =
new ProjectBuildWorkspaceModifyOperation(project, buildTypeInt);
op.run(monitor);
} catch (Exception e)
{
displayError("ProjectBuild: " +projectName
+" Exception="+e.getMessage());
}
}
protected void displayError(String msg) throws BuildException
{
System.out.println(msg);
if(failOnError)
throw new BuildException(msg);
}
public void setProjectName(String name)
{ projectName = name;
}
public void setBuildType(String type)
{ buildTypeString = type;
}
public void setfailonerror(String str)
{ failonerror = str;
}
protected void validateAttributes() throws
BuildException
{ if (projectName == null)
{ displayError("Must supply ProjectName");
return;
}
if (buildTypeString.equalsIgnoreCase("INCREMENTAL")
buildTypeInt = IncrementalProjectBuilder.INCREMENTAL_BUILD;
else if (buildTypeString.equalsIgnoreCase("FULL") )
buildTypeInt = IncrementalProjectBuilder.FULL_BUILD;
else if (buildTypeString.equalsIgnoreCase("AUTO"))
buildTypeInt = IncrementalProjectBuilder.AUTO_BUILD;
else
{ displayError("Invalid BuildType="+buildTypeString
+", must be INCREMENTAL or FULL or AUTO");
return;
}
if(failonerror.equals("true"))
failOnError = true;
else if(failonerror.equals("false"))
failOnError = false;
else
{ displayError("Invalid failonerror="+failonerror
+", must be \"true\" or \"false\" ");
return;
}
}
}
|
Specifying non-debug versus debug compilations
The
ProjectBuild
Ant task can have one other useful enhancement. The current Application Developer always generates debug
javac
compilations. Your build can specify
DebugCompilation=false
if you wish, and it will disable
lineNumber
,
localVariable
, and
sourceFile
debug code for the current Application Developer session (settings are not persistent to the next startup). Because this option is not available as part of the standard Application Developer, generating non-debug compilations is currently an unsupported feature (but one you may wish to try).
The following code is added into
ProjectBuild
as shipped in the download code.
private String debugcompilation = "";
private boolean debugCompilation;
public void setDebugCompilation(String str)
{ debugcompilation = str;
}
public void setDebugOptions(String str)
{
if(str==null || str.equals(""))
return;
Hashtable options = JavaCore.getJavaCore().getOptions();
if(str.equalsIgnoreCase("true"))
{ options.put("org.eclipse.jdt.core.compiler.debug.localVariable",
"generate");
options.put("org.eclipse.jdt.core.compiler.debug.lineNumber",
"generate");
options.put("org.eclipse.jdt.core.compiler.debug.sourceFile",
"generate");
}else if (str.equalsIgnoreCase("false"))
{ options.put("org.eclipse.jdt.core.compiler.debug.localVariable",
"do not generate");
options.put("org.eclipse.jdt.core.compiler.debug.lineNumber",
"do not generate");
options.put("org.eclipse.jdt.core.compiler.debug.sourceFile",
"do not generate");
}
JavaCore.setOptions(options);
}
|
The program above uses
AntConsoleProgressMonitor
, which is a simple non-GUI monitor that displays progress and handles cancellation requests. You can chose to display more or fewer messages to your console:
package com.ibm.ant.extras;
import org.apache.tools.ant.Task;
import org.eclipse.core.runtime.IProgressMonitor;
public class AntConsoleProgressMonitor implements IProgressMonitor
{
private String taskInfo = "";
private Task task;
private String taskname = "unknown";
public AntConsoleProgressMonitor(Task t)
{ task = t;
taskname = t.getTaskName();
}
public void beginTask(String name, int totalTime)
{ taskInfo = "";
taskname = name;
task.log("beginTask "+name + " " + taskInfo + "...");
}
public void done()
{ task.log("doneTask="+taskname);
taskname = "unknown";
}
public void setTaskName(String name)
{ taskname = name;
}
public void subTask(String arg0)
{ task.log(taskname + " ... subtask: " + arg0 +"...");
}
public void displayMsg(String msg)
{ task.log("Message="+msg);
}
public void setCurrentTaskInfo(String info)
{ taskInfo = info;
task.log("TaskInfo=" +info );
}
public void internalWorked(double arg0)
{;}
public void worked(int timework)
{;}
public boolean isCanceled()
{ return false;
}
public void setCanceled(boolean arg0)
{;}
}
|
Summary of ProjectBuild Ant task
-
Create a
ProjectBuildWorkspaceModifyOperationclass that invokes the build method on a project. -
Create a
ProjectBuildclass that invokes theProjectBuildWorkspaceModifyOperation. -
Edit
plugin.xmlto add:
<antTask name="projectBuild" class="com.ibm.ant.extras.ProjectBuild"> </antTask>
-
In your
build.xmlscript, invoke your newprojectBuildAnt task:
<projectBuild ProjectName="myProject" DebugCompilation="false" BuildType="incremental" />
plugin.xml
is in the downloadable code included with this example.
build.xml
is your Ant script.
When a project is built in Application Developer, you may want to know if any
javac
errors occurred. Creating a new Ant task for this involves getting results from the Tasks view (which is where the error markers are listed), and returning data (the error count) back to the Ant task as a property value. Since this new Ant task does not modify any workspace resources, you do not need to put a wrapper around it, and the Ant task is relatively simple. The majority of the work is done in two lines inside the try section of the execute method. The key aspects are:
-
A
getJavacErrorCountmethod that queries the Tasks View to count alljavacerror markers for a specified project. -
An Ant task project property (default name=
JavacErrorCount)that is set to return the error count back to the Ant build script.
package com.ibm.ant.extras;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaModelMarker;
public class GetJavacErrorCount extends Task
{
private IProject project = null;
private String projectName;
private String propertyName = "JavacErrorCount";
AntConsoleProgressMonitor monitor = null;
int UNKNOWN_ERRORS = -1;
public void execute() throws BuildException
{
int errorCount = -1;
this.getProject().setUserProperty(
propertyName, Integer.toString(errorCount));
validateAttributes();
project = ResourcesPlugin.getWorkspace()
.getRoot().getProject(projectName);
if(project==null) throw new BuildException("GetJavacErrorCount: "
+projectName +" project==null");
if(!project.exists()) throw new BuildException("GetJavacErrorCount: "
+projectName + " not found in Workspace.");
if(!project.isOpen()) throw new BuildException("GetJavacErrorCount: "
+projectName +" is not open");
try
{ //non-GUI to display progress and handle cancel requests
AntConsoleProgressMonitor monitor =
new AntConsoleProgressMonitor(this);
monitor.setTaskName("GetJavacErrorCount: "
+projectName);
errorCount = getJavacErrorCount(project, monitor);
monitor.displayMsg("Project " "+projectName+" "
+propertyName+"="+errorCount );
} catch (Exception e)
{
throw new BuildException(e);
}
this.getProject().setUserProperty(
propertyName, Integer.toString(errorCount) );
}
// Returns number of reported errors,
// -1 if errors can't be retrieved.
private int getJavacErrorCount(IProject prj,
AntConsoleProgressMonitor mntr)
{
project = ResourcesPlugin.getWorkspace().
getRoot().getProject(projectName);
if(project==null) throw new BuildException("ProjectBuild: "
+projectName +" project==null");
if(!project.exists()) throw new BuildException("ProjectBuild: "
+projectName + " not found in Workspace.");
if(!project.isOpen()) throw new BuildException("ProjectBuild:"
+projectName +" is not open");
monitor = mntr;
try {
IMarker[] markerList = prj.findMarkers(
IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
true, IResource.DEPTH_INFINITE);
if (markerList == null || markerList.length == 0)
return 0;
IMarker marker = null;
int numErrors = 0;
for (int i = 0; i < markerList.length; i++)
{
marker = markerList[i];
int severity = marker.getAttribute(IMarker.SEVERITY,
IMarker.SEVERITY_ERROR);
// default severity = ERROR
if (severity == IMarker.SEVERITY_ERROR)
{
numErrors++;
Integer lineNum = (Integer)
marker.getAttribute(IMarker.LINE_NUMBER);
String resourceName = (String)
marker.getResource().getName();
String message = (String)
marker.getAttribute(IMarker.MESSAGE);
monitor.displayMsg(lineNum+" "
+resourceName+":"+message);
}
}
return numErrors;
} catch (CoreException e) {
monitor.displayMsg("CoreException: " +
e.getMessage());
}
return UNKNOWN_ERRORS;
}
public void setProjectName(String name)
{ projectName = name;
}
public void setPropertyName(String name)
{ propertyName = name;
}
protected void validateAttributes() throws BuildException
{ if (projectName == null)
throw new BuildException("Must supply ProjectName");
}
}
|
As with the previous
ProjectBuild
example, to activate this new
GetJavacErrorCount
Ant task, you edit
plugin.xml
to add:
<anttask name="getJavacErrorCount" class="com.ibm.ant.extras.GetJavacErrorCount"> </anttask> |
Also, in your
build.xml
script, invoke your new
getJavacErrorCount
Ant task:
<getJavacErrorCount ProjectName="MyProject"
PropertyName="MyJavacErrorCount" />
<echo message="MyJavacErrorCount=${MyJavacErrorCount}" /> |
This article explained how you can create your own Java classes to add new Ant tasks to your custom build environment. In particular, it discussed how to wrapper tasks that modify workspace resources with a
WorkspaceModifyOperation
class and an
AntConsoleProgressMonitor
class, and then use those classes within your own task. It provided a
ProjectBuild
sample as well as a simple
GetJavacErrorCount
sample, which does not modify workspace resources and does not need to be put in a wrapper.
| Name | Size | Download method |
|---|---|---|
| com.ibm.ant.extras_V4.zip | 0.1 MB | FTP |
| com.ibm.ant.extras_V5EA.zip | 0.1 MB | FTP |
Information about download methods

Barry Searle is the Migration Team Leader for WebSphere Studio Application Developer. He is a professional engineer who has worked at the IBM Toronto Lab for over ten years on various application development tools. Prior to that he had many years of industry experience developing command and control systems and leading complex communications development projects. You can reach Barry at searle@ca.ibm.com.
Ellen McKay is an Information Developer for IBM Toronto Lab. She writes online help and publications for WebSphere Studio Application Developer. You can reach Ellen at ecmckay@ca.ibm.com.
Comments (Undergoing maintenance)





