Writing Ant Tasks
Pages: 1, 2
The Jing Task
Now let's explore a more complex task. James Clark has written an Ant task for Jing. As I mentioned earlier, Jing is an open source, Java validator for the RELAX NG and Schematron schema languages for XML. Jing is reliable and provides good performance, and is a good choice for those wanting to use Ant to automate the validation of documents with an XML pipeline -- the execution of a succession of XML tasks.
To use this task, you need to download the Jing binaries and place jing.jar in the Ant lib subdirectory (or elsewhere in the classpath using -lib). You can also download the Jing source code (see src.zip), where you will find JingTask.java. For your convenience, I have included the source file JingTask.java in the example archive, along with
the Jing license (copying.txt).
JingTask.java is too long to list here in its entirety,
but I will highlight parts of the code in general terms in the
discussion that follows.
The JingTask.java source should provide some hints on writing more complex Ant tasks. In Add.java, all of the task code is neatly self-contained within that class. Of course, in most tasks, the task code comes from external code for an existing program that must be imported into the task code. To illustrate, near the top of JingTask.java, you will notice that it imports many more classes than Add.java, 18 to be exact: eight from Jing, five from Ant, two from SAX, and three from Java itself:
import com.thaiopensource.util.PropertyMapBuilder;
import com.thaiopensource.validate.Flag;
import com.thaiopensource.validate.SchemaReader;
import com.thaiopensource.validate.ValidationDriver;
import com.thaiopensource.validate.schematron.SchematronProperty;
import com.thaiopensource.validate.rng.CompactSchemaReader;
import com.thaiopensource.validate.rng.RngProperty;
import com.thaiopensource.xml.sax.ErrorHandlerImpl;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import java.io.File;
import java.io.IOException;
import java.util.Vector;
JingTask extends Ant's Task class, and
declares a set of private variables, mostly for the management of files
or filesets:
/**
* Ant task to validate XML files using RELAX NG
* or other schema languages.
*/
public class JingTask extends Task {
private File schemaFile;
private File src;
private final Vector filesets = new Vector();
private PropertyMapBuilder properties =
new PropertyMapBuilder();
private boolean failOnError = true;
private SchemaReader schemaReader = null;
The filesets variable of type Vector will be
used for code that manipulates sets of files within the context of a task (search for filesets in JingTask.java to see how this code is pieced together). The properties variable of type PropertyMapBuilder is used with Jing's ValidationDriver.
Next, the task sets up an error handler, LogErrorHandler,
an extension of Jing's ErrorHandlerImpl, which in turn is an implementation of SAX's ErrorHandler interface:
private class LogErrorHandler extends
ErrorHandlerImpl {
int logLevel = Project.MSG_ERR;
public void warning(SAXParseException e)
throws SAXParseException {
logLevel = Project.MSG_WARN;
super.warning(e);
}
public void error(SAXParseException e) {
logLevel = Project.MSG_ERR;
super.error(e);
}
public void printException(Throwable e) {
logLevel = Project.MSG_ERR;
super.printException(e);
}
public void print(String message) {
log(message, logLevel);
}
}
The message constants MSG_ERR and MSG_WARN come
from Ant's Project class. The warning,
error, and printException methods are called
from the superclass ErrorHandlerImpl. The print
method calls the log method, which you can find in Task
and in Task's superclass, Ant's ProjectComponent.
When the JingTask constructor is defined, it sets
the RELAX NG property so that it checks ID/IDREF/IDREFS by default,
if they exist in the instance:
public JingTask() {
RngProperty.CHECK_ID_IDREF.add(properties);
}
The execute method overrides the method
of the same name from Ant's Task class.
This is the most complex part of the code. Basically, this implementation of
execute sets up the task for the retrieval of
instance and schema files for Jing and, unlike the Add
task, does a fair amount of error handling. If the expected files
or filesets are not found, this part of the code will throw
a SAX, I/O, or build exception, depending on the problem.
At the beginning, the method checks to see if the values in
the rngFile attribute or the schemaFile
attribute are in place, then if either a file attribute
or a fileset element exists:
if (schemaFile == null)
throw new BuildException("There must be an
rngFile or schemaFile attribute",location);
if (src == null && filesets.size() == 0)
throw new BuildException("There must be a file
attribute or a fileset child element",
location);
If a value for rngFile is not present, there
must be a value for schemaFile; if file
is not present, the fileset element must be a child
of the jing element.
Following these tests, an error handler is instantiated and an
error flag (hadError) is set up. Next, a try
block contains the code to feed file names to Jing using ValidationDriver. After the try block, several catch blocks
and an if statement stand sentinel to handle any
errors.
After the execute method, JavaTask
defines setter methods for eight possible attributes, namely
setCheckid, setCompactsyntax,
setFailonerror, setFeasible,
setFile, setPhase,
setRngfile, and setSchemafile.
The following table shows the correspondence between these
methods, Jing's command-line options, and Ant task attributes.
Table 1. Jing methods and attributes
| Method | Attribute | Option Description |
|---|---|---|
setCheckid | checkid | Checks for
ID/IDREF/IDREFS compatibility. Corresponds to -i. Possible
values are true or false, with default
true. |
setCompactsyntax | compactsyntax | Turns
compact syntax checking on or off. Corresponds to -c. Possible
values are true or false, with default
false. |
setFailonerror | failonerror | A
value of false means that processing continues after an
error occurs. |
setFeasible | feasible | Checks if document
is feasibly valid.
Corresponds to -f. Possible values are true or
false with default false. |
setFile | file | Name of XML file to validate. |
setPhase | phase | Schematron phase to be used during internal schema creation or validation (not demonstrated here). |
setRngFile | rngfile | Name of RELAX NG schema file. |
setSchemafile | schemafile | Name of Schematron file (not demonstrated here). |
The final bit of code in JingTask.java is the
addFileset method, a utility method for
fileset functionality. Search for filesets
in the code to see how it works.
Following is another build file from the archive (build-jing.xml) that exercises the Jing task:
<?xml version="1.0"?>
<project default="jing">
<taskdef name="jing" classname="com.thaiopensource.relaxng.util.JingTask"/>
<target name="jing">
<jing file="time.xml" rngfile="time.rng"/>
</target>
</project>
taskdef identifies the name of the task (jing)
and the name of the class that holds the code for the task
(com.thaiopensource.relaxng.util.JingTask). The only child
of target is the jing element, with the
attributes file and rngfile. The jing
element triggers the Jing task, telling Jing to validate
time.xml against time.rng.
Now invoke Ant with build-jing.xml. This assumes that you have placed jing.jar in the Ant lib directory. In the working directory, type this command:
ant -f build-jing.xml
This will be the result if all files are in place:
Buildfile: build-jing.xml
jing:
BUILD SUCCESSFUL
Total time: 1 second
If Jing runs quietly, that indicates success. Two other Jing-related
build files are in the example archive: build-jing-compact.xml,
which validates a file against a compact syntax schema, and build-jing-set.xml, which validates
a set of files against a RELAX NG schema. Test both these files yourself
with ant -f. The inner workings of these build files should
now be self-evident.
Conclusion
After reading this article, the basic steps of writing an Ant task should no longer be a mystery to you. To write a task for a program as complex as Jing, however, you need to have an intimate understanding of the code so that you know how to feed the program what it needs to run properly. You'll also want to add error handling of some sort for an industrial-strength task.
To expand your understanding of creating tasks for Ant, you'll can review the section in Ant's official online manual that describes how to write your own task, as well as the tutorial on writing tasks. The Ant site also provides a list of external tasks (tasks written for Ant by third parties) that may be of interest.
Resources
- Sample code and related files for this article
Michael Fitzgerald is Principal at Overdue Books, a publishing and writing consultancy.
Return to ONJava.com.
-
project tag ( why it should be the root tag)
2006-08-20 22:42:58 bishweshwar [View]
-
project tag ( why it should be the root tag)
2006-09-08 07:47:11 Michael Fitzgerald |
[View]
-
buil.xml
2005-01-31 22:58:35 KEENFORJAVA [View]
-
Article is cool and here are my comments
2004-07-29 22:28:52 skmajji [View]
-
Feedback
2004-06-04 02:05:08 erikhatcher [View]
-
Feedback
2004-06-05 22:21:11 Michael Fitzgerald |
[View]