|
|||||||
|
|
![]() |
|
|
Building Applications with Ant by David Morris Building and deploying applications is a complex process, particularly when applications span more than one platform or programming language. The Apache Software Foundation created Ant to help tame this complexity, by providing an extensible build tool that runs on any platform, because Ant is written in Java and an Ant script is an XML document. The XML elements in an Ant script define data and instructions that Ant interprets to build applications. Ant was developed out of the necessity to support building the Tomcat servlet engine, which is part of the foundation's Jakarta project. The Jakarta project supports open-source, Java-based development. After it was found that Ant could be used to support other projects, the Ant code base was extracted from Tomcat, and Ant development continued as a stand-alone Jakarta project. Early this year, Ant was promoted to a top-level project of the foundation. Most integrated development environments (IDEs) that support Java development, like Eclipse and the WebSphere Development Studio client (WDSc), provide built-in support for Ant. IBM doesn't directly support Ant on the iSeries, but because Ant is written in Java and uses XML scripts, it will run on the iSeries. In order to make it easier to run Ant on the iSeries, I wrote a native Ant command that can be run from an iSeries command line. Ant reminds me a lot of the iSeries' CL programming language. Both Ant and CL can script common tasks, and both are extensible. To extend CL, you create custom commands. To extend Ant, you create custom tasks. One difference between CL and Ant is that CL is compiled; Ant is interpreted at runtime. Introductory Example Ant is a tool for interpreting and running script files written in XML. Individual instructions in an Ant script are called "tasks." Tasks are Java programs and allow Ant to script any function that can be accomplished with Java. Tasks are grouped under conditionally executed targets. One way to understand how Ant works is through an example. The example that follows is simple but powerful and can be used as a base for just about any Java project's build file. (Ant and its associated tasks are well documented on Ant's documentation Web site, so I won't run through all of the options that each task in the example supports.)
<?xml version="1.0" encoding="iso-8859-1"?>
<project basedir="." default="default" name="examples"> <- 1
<property name="src" value="src"/>
<property name="target" value="dist"/>
<property name="debug" value="on"/>
<property name="deprecation" value="on"/>
<target name="default" depends="config,compile"/>
<target name="all" depends="config,clean,compile"/>
<path id="project.classpath">
<fileset dir=".">
<include name="**/*.jar"/>
</fileset>
</path>
<target name="config">
<echo message="Property settings:"/>
<echo message="src = ${src}"/>
<echo message="target = ${target}"/>
<echo message="debug = ${debug}"/>
<echo message="deprecation = ${deprecation}"/>
</target>
<target name="clean">
<deltree dir="${target}"/>
</target>
<target name="prepare">
<mkdir dir="${target}"/>
</target>
<target name="compile" depends="prepare">
<javac debug="${debug}"
deprecation="${deprecation}"
destdir="${target}"
failOnError="true"
includes="**/*.java" srcdir="${src}">
<classpath refid="project.classpath"/>
</javac>
</target>
</project>
This example will compile Java source and place the resulting class files in the directory identified by the target property. The class path for this build includes all JAR files found in the directory tree. Ant build-scripts like this one are generally named build.xml, but that is not a requirement. There are several options that this example Ant script supports, such as object cleanup and listing current configuration information. The first thing you probably noticed is that this Ant script is an XML document. Like all XML documents, it starts with an XML declaration that specifies the XML version and encoding scheme. Following this is a root project element, followed by several property and target elements. On the iSeries, I generally use ISO-8859-1 encoding, which is a single-byte character, Latin-1 character set. When I create an ISO-8859-1 XML document, I set the coded character set ID (CCSID) to 819. You can use Qshell and run touch -C 819 /filedir/filename to create an empty file with a CCSID of 819. The project element identifies the default target for the build file and sets the base directory. The default target is the target that runs when a target is not specified on the command line. The base directory supplies a default directory used by tasks. Specifying a period (.) tells tasks to run with a parent directory that is relative to the directory containing the build file. Following the project element are several property elements, which act like program variables. Properties can be set several ways, but, once set, they retain their value on a first-setter-wins basis. You can set properties using a command line switch like -Dpropertyname=value, employing a property file, or using property elements. Following the property elements are several target elements that group together individual tasks. Each target has a name and may specify dependent targets, which are run before the target that specifies the dependencies. In the example, the compile target specifies the prepare target as a dependency. Some of the targets in the example contain tasks like echo, javac, mkdir, and deltree. These tasks are representative of the tasks that Ant currently supports. If Ant doesn't provide support for what you need, you can write your own tasks similar to the way you can write your own CL commands. Ant Tasks There are hundreds of prebuilt Ant tasks you can use to assemble your own Ant scripts. Ant comes with support for about 75 core tasks and another 40 or so optional tasks. Core tasks are included in the ant.jar file, and optional tasks are found in optional.jar. Most of the optional tasks also have library dependencies that make it necessary to install additional JAR files. In addition to the tasks that Ant itself provides, there are tasks available, from sources like SourceForge, that add to Ant's capabilities. The Ant site provides a partial list of external tools and tasks. You can also create your own tasks. Tasks are Java programs that add functionality to Ant. Creating an Ant task is not too difficult, and is similar to creating a command interface for a CL program. In the next section, I will show you an Ant task that I wrote to support the running of CL commands. Anatomy of a Task An Ant task is a Java class that extends org.apache.tools.ant.Task. All tasks used in an Ant task are instantiated when the script starts running, by calling the constructor for the task with no parameters. Again, an example should help to demonstrate how an Ant task works. The following example will execute an OS/400 command on the specified system:
package org.iseriestoolkit.ant;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import com.ibm.as400.access.AS400;
import com.ibm.as400.access.AS400Message;
import com.ibm.as400.access.CommandCall;
/**
* AS400Command provides an Ant task to run iSeries commands.
* @author David Morris
*/
public class OS400Command extends Task {
String command = null;
boolean threadSafe = false;
String systemName = null;
String userId = null;
String password = null;
/**
* @see org.apache.tools.ant.Task#execute()
*/
public void execute()
throws BuildException {
AS400 as400 = null;
if (password != null) {
as400 = new AS400(systemName, userId, password);
}
else if (userId != null) {
as400 = new AS400(systemName, userId);
}
else if (systemName != null) {
as400 = new AS400(systemName);
}
else {
as400 = new AS400();
}
CommandCall commandCall = new CommandCall(as400);
commandCall.setThreadSafe(isThreadSafe());
try {
commandCall.run(getCommand());
}
catch (Throwable t) {
throw new BuildException(
"Error executing: " + command + ".", t);
}
AS400Message[] as400Messages = commandCall.getMessageList();
for (int i = 0; i < as400Messages.length; i++) {
// Show messages
System.out.println(as400Messages[i].getText());
try {
as400Messages[i].load();
log(as400Messages[i].getHelp());
}
catch (Exception e) {
log(
"Error processing com.ibm.as400.access.
CommandCall messages.");
log(e.getLocalizedMessage());
}
}
as400.disconnectService(AS400.COMMAND);
}
/**
* Returns the command.
* @return String
*/
public String getCommand() {
return command;
}
/**
* Sets the command.
* @param command The command to set
*/
public void setCommand(String command) {
this.command = command;
}
/**
* Returns the threadSafe.
* @return boolean
*/
public boolean isThreadSafe() {
return threadSafe;
}
/**
* Sets the threadSafe.
* @param threadSafe The threadSafe to set
*/
public void setThreadSafe(boolean threadSafe) {
this.threadSafe = threadSafe;
}
/**
* Returns the systemName.
* @return String
*/
public String getSystemName() {
return systemName;
}
/**
* Sets the systemName.
* @param systemName The systemName to set
*/
public void setSystemName(String systemName) {
this.systemName = systemName;
}
/**
* Returns the userId.
* @return String
*/
public String getUserId() {
return userId;
}
/**
* Sets the userId.
* @param userId The userId to set
*/
public void setUserId(String userId) {
this.userId = userId;
}
/**
* Returns the password.
* @return String
*/
public String getPassword() {
return password;
}
}
As you can see, the OS400Command task extends org.apache.tools.ant.Task and implements an execute() method. The values used in OS400Command are set by Ant before calling execute(). Here is a simple script that uses the OS400Command task:
<?xml version="1.0" encoding="iso-8859-1"?>
<project basedir="." default="test" name="examples">
<taskdef name="os400Command"
classname="org.iseriestoolkit.ant.OS400Command"/>
<target name="test">
<echo>Running dspjob command.</echo>
<os400Command
command="dspjob output(*print)"/>
</target>
</project>
One thing you may notice about this script is that it contains a taskdef element. The taskdef element associates the OS400Command class with the os400command task element. Another way to identify a task to Ant is to add the task to the defaults.properties file, found in the org.apache.tools.ant.taskdefs directory. Tasks added to defaults.properties are available as built-in tasks. You can find the defaults.properties file inside the ant.jar file, included in the Ant distribution. When you look at the example script, you may also wonder how Ant knows to set the command value in the OS400Command class. Ant uses reflection to locate a setter method for each attribute. In this case, Ant will locate and call the setCommand(String) method, passing the value found for the attribute. Before you can run the example script and OS400Command task, you have to set a few things up. First, you have to compile OS400Command to a class file. Next, you need to add that class file to your class path, along with ant.jar and jt400.jar. Finally, you have to run Ant, passing the name of the build file containing the OS400Command task. When you run this script the dspjob output(*print) command is executed. If you run the script on your PC, you will be prompted for a system, user, and password. Running an Ant Script There are many ways to run Ant. First, you can run it using the java command from Qshell or a Windows command line, like so: java -classpath /java/lib/ant/ant.jar: /java/lib/ant/optional.jar: /java/lib/ant/xercesImpl.jar: /java/lib/ant/xml-apis.jar org.apache.tools.ant.Main In some environments, you can run Ant using a native Ant program. On Windows there is an ant.exe file that will run Ant for you. IBM doesn't supply native Ant support on the iSeries, so I created an ANT command and an RPG IV program that allow you to run Ant from the command line. That command and program are available as part of the iSeries-toolkit. That support is evolving, so keep an eye on the CVS versions of the iSeries-toolkit source, which have the latest changes. One of the best ways to run Ant is from within your IDE. Most IDEs, like Eclipse and WDSc, provide support for Ant. There are several steps to running Ant from within Eclipse or WDSc:
The resulting messages from running the Ant script are displayed in the Console window. Getting Ant to Your iSeries Loading Ant on your iSeries is not too difficult. First, go to the Ant Download Web site and download the latest version to your PC. After downloading Ant, copy the downloaded file to your iSeries system and unzip it. You can transfer the downloaded Zip file to your iSeries system, using a mapped drive or FTP. The FTP to transfer Ant will look like this: ftp yourSystemHost cd /tmp lcd c:\temp bin put apache-ant-1.5.3-1-bin.zip Once you have the Zip file on your iSeries, start a Qshell session using the command QSH. Change to the OS/400 Integrated File System (OS/400 IFS) directory, where you want to install Ant and extract the Zip file, using the following commands: mkdir /java mkdir /java/ant cd /java/ant jar -xf /tmp/apache-ant-1.5.3-1-bin.zip Wait for the dollar sign ($) to appear after each command, which indicates that the command is complete. Symbolic links allow you to make a single file or directory appear to be in two places at once. When you extract Ant, you end up with a directory name that includes the version name. I create a symbolic link that strips off the version name, and refer to the stripped-down name in programs and scripts. This makes it easier to upgrade to new versions and to revert to an older version, in case there are problems. For Ant, I created a symbolic link to the install directories, using the link (ln) Qshell command, as follows: ln -s /java/ant/apache-ant-1.5.3-1/lib /java/lib/ant With the link created, I can then refer to the various Ant jar files using the directory /java/lib/ant, such as /java/lib/ant/ant.jar. When I install a new version of Ant, I replace the existing link with one that points to the location of the latest version of Ant. "Antilizing" the Results Ant is a mature and stable Java tool that runs well on the iSeries. You will save a lot of time building applications in general, and Web applications in particular, by creating Ant build-scripts to run repetitive tasks. However, that doesn't mean that Ant is limited to Web applications and Java. Ant can be extended to support just about any task. You extend Ant in much the same way that you add new commands to the iSeries. The main difference is that Ant is not limited to the iSeries. If you are beginning to use WDSc, it is worth your time to investigate the Ant Runner, included as part of the IDE. The Ant Runner is a neat tool that remembers prior build information and makes it easy to run Ant scripts. David Morris is a software architect at Plum Creek Timber Company, and started the iSeries-toolkit open-source project. E-mail: dmmorris@itjungle.com.
|
Editors
Contact the Editors |
| Copyright © 1996-2008 Guild Companies, Inc. All Rights Reserved. |