| Table of Contents |
The chat application consists of five components: JMS, client, server, common, and Channel. The common, client, and server components are domain-specific. JMS is a third-party implementation. Other applications can use Channel as a third-party component. The complete application consists of all the compiled classes, scripts to execute the application, and the program's configuration file. You need to create build scripts for the Channel, client, server, and common layer classes.
The Channel component provides the interface between chatclient and chatserver. Channel also provides the infrastructure for communication between chatclient and chatserver.
The requirements of MyChatRoom define the requirement for Channel.
Listing 5-2 shows how to build the Channel component:
<!--this is the build file required by ant for building the channel application-->
<project name="channel" default="all" basedir=".">
<!--these are Environmental Specific Properties-->
<property file="environment.properties"/>
<!--this is our first target named init-->
<target name="init" unless="${channel.target.init}">
<property name="channel.target.init" value="set"/>
<tstamp/>
<!--these are project specific Properties-->
<property name="channel.src" value="."/>
<property name="channel.classes" value="../../classes"/>
<property name="channel.lib" value="../../lib"/>
<property name="channel.doc" value="../../docs"/>
<property name="channel.javadoc" value="../../${doc}/api"/>
<!--build properties-->
<property name="channel.root.package" value="chtm.dest.ant.channel"/>
<property name="channel.root.path" value="chtm/dest/ant/channel"/>
<property name="channel.unittest.package" value="${channel.root.package}.unittest"/>
<property name="channel.unittest.path" value="${channel.root.path}/unittest"/>
<!-- Project Jar files -->
<property name="channel.jar" value="${channel.lib}/channel.jar"/>
<!-- entry points -->
<property name="channel.testcase" value="chtm.dest.ant.channel.unittest.ChannelTest"/>
</target>
<!-- end of init target -->
<target name="compile" depends="init">
<mkdir dir="${channel.classes}" />
<javac srcdir="${channel.src}" destdir="${channel.classes}">
<classpath>
<pathelement location="${jms.provider.jar}"/>
</classpath>
</javac>
<copy todir="${channel.classes}">
<fileset dir="${channel.src}">
<exclude name="**/*.java"/>
<exclude name="*"/>
</fileset>
</copy>
</target>
<!--here compile target ends -->
<target name="jar" depends="unittest,filter">
<mkdir dir="${channel.lib}"/>
<jar jarfile="${channel.jar}" basedir="${channel.classes}"/>
<delete dir="${channel.classes}"/>
</target>
<!-- here jar target ends -->
<target name="release" depends="compile,jar">
<delete dir="${channel.classes}"/>
</target>
<!-- here clean target end -->
<target name="javadoc" depends="init">
<mkdir dir="${javadoc}"/>
<javadoc sourcepath="${channel.src}"
destdir="${channel.javadoc}"
packagenames="${channel.root.package}"
Windowtitle="${channel.windowtitle}"
Overview="${channel.src}/overview.html">
<classpath>
<pathelement location="${channel.src}"/>
<pathelement location="${jms.path}"/>
</classpath>
</javadoc>
</target>
<!-- here javadoc target end-->
<!-- Delete classes used for unittest -->
<target name="filter" depends="init">
<delete dir="${channel.classes}/${channel.unittest.path}"/>
</target>
<target name="unittest" depends="init">
<parallel>
<java classname="${jms.router}" fork="yes" timeout="15000" dir="${jms.router.config.root}"
output="jmsrouter.log">
<arg value="${jms.router.config}"/>
<classpath>
<fileset dir="${jms.lib}">
<include name="**/*.jar/>
</fileset>
</classpath>
</java>
<sequential>
<sleep seconds="5"/>
<java classname="${channel.testcase}" fork="yes">
<classpath>
<pathelement location="${channel.classes}"/>
<pathelement location="${jms.provider.jar}"/>
<pathelement location="${jms.client.jar}"/>
</classpath>
</java>
</sequential>
</parallel>
<antcall target="filter" inheritAll="yes"/>
</target>
<!--here target unittest ends-->
<target name="clean" depends="init">
<delete dir="${channel.classes}"/>
<delete file="${channel.jar}"/>
<delete dir="${channel.javadoc}"/>
</target>
<!--end of clean target-->
<target name="all" depends="clean,release"/>
</project>
<!--end of channel component build file-->
The above listing shows that all the targets perform the steps required to fulfill only a logical unit of task. The init target contains a property definition for every attribute in the build script. The init target sets the channel.target.init property. The unless target attribute checks the status of channel.target.init to ensure that the init target executes only once.
The compile target uses the <javac> task. The compilation process creates class files in a classes subdirectory. The <mkdir> task ensures that this directory exists. The <copy> task copies all the other non-Java code artifacts that you need to support the application, such as properties and gifs after the <javac> task.
The unittest target includes both the < parallel> and <sequential> tasks. The <sequential> task contains the actual unit test. The <sleep> task precedes the unit test and specifies a wait of five seconds before the unit test starts.
The filter target locates and deletes all the class files used to support the unit testing.
The clean target removes all artifacts from the project, such as class files, resources, and the component's JAR file. The <delete> task in the clean target eliminates the channel.classes directory, the channel.jar file, and the channel.javadoc directory.
You can use the environment.properties file to provide user-specific information separately from the build script. Isolating external properties from the build script and placing them in the environment.properties file allows you to use the build script among various environments without the need for editing. Reducing the need to edit the build script reduces the probability of errors in the script. Listing 5-3 shows the environment.properties file of the Channel component:
# Properties used by Ant scripts to build the channel component
channel.release=v2_0
channel.module=channel
jms.home=C:/swiftmq_2_0_1
jms.lib=${jms.home}/jars
jms.provider.jar=${jms.lib}/jms.jar
jms.client.jar=${jms.lib}/smqclient.jar
jms.console=chtm.swiftmq.admin.explorer.Explorer
jms.router=chtm.swiftmq.router.Router
jms.router.config=jms/conf/ChatroomRouter.properties
jms.router.config.root=../..
The above listing shows the environment.properties file, which sets the properties of the Channel component. This file also identifies parameters, such as jms.home and jms.lib, for using the JMS provider’s implementation.
The common layer represents all classes that the client and the server share. The classes include a number of domain classes that are passed across the channel for the communication between chatclient and chatserver.
Listing 5-4 shows the build script for the common layer:
<project name="chatcommon" default="all" basedir=".">
<!-- Environmental Specific Properties-->
<property file="environment.properties"/>
<! -- Initialize the build and run environments-->
<target name="init" unless="${chatcommon.target.init}">
<property name="chatcommon.target.init" value="set"/>
<tstamp/>
<!--these are Project Properties-->
<property name="chatcommon.src" value="."/>
<property name="chatcommon.classes" value="../../classes"/>
<property name="chatcommon.lib" value="../../lib"/>
<property name="chatcommon.doc" value="../../docs"/>
<property name="chatcommon.javadoc" value="../../${chatcommon.doc}/api"/>
<!--these are build properties-->
<property name="chatcommon.root.package" value="chtm.dest.ant.chatroom"/>
<property name="chatcommon.root.path" value="chtm/dest/ant/chatroom"/>
<!--these are Project Jar files-->
<property name="chatcommon.jar" value="${chatcommon.lib}/common.jar"/>
<!--these are dependency checking-->
<available file="${chatcommon.lib}/${channel.jar}" property="channel.classpath"
value="${chatcommon.lib}/${channel.jar}"/>
<available classname="${channel.class}" property="channel.classpath"
value="${chatcommon.classes}">
<classpath location="${chatcommon.classes}"/>
</available>
<condition property="common.dependencies">
<isset property="channel.classpath"/>
</condition>
</target>
<target name="dependents" unless="common.dependencies">
<fail message="Cannot find classes for channel."/>
</target>
<target name="compile" depends="init,dependents">
<mkdir dir="${chatcommon.classes}"/>
<javac srcdir="${chatcommon.src}" destdir="${chatcommon.classes}">
<classpath>
<pathelement location="${channel.classpath}"/>
</classpath>
</javac>
<copy todir="${chatcommon.classes}">
<fileset dir="${chatcommon.src}">
<exclude name="**/*.java"/>
<exclude name="*"/>
</fileset>
</copy>
</target>
<target name="jar" depends="init">
<mkdir dir="${chatcommon.lib}"/>
<jar jarfile="${chatcommon.jar}" basedir="${chatcommon.classes}"/>
</target>
<target name="release" depends="compile,jar">
<delete dir="${chatcommon.classes}"/>
</target>
<target name="javadoc" depends="init">
<mkdir dir="${javadoc}"/>
<javadoc sourcepath="${chatcommon.src}" destdir="${chatcommon.javadoc}"
packagenames="${chatcommon.root.package}" Windowtitle="${chatcommon.windowtitle}"
Overview="${chatcommon.src}/overview.html">
<classpath>
<pathelement location= "${chatcommon.src}"/>
<pathelement location="${jms.jar}"/>
</classpath>
</javadoc>
</target>
<target name="clean" depends="init">
<delete dir="${chatcommon.classes}"/>
<delete file="${chatcommon.jar}"/>
<delete dir="${chatcommon.javadoc}"/>
</target>
<!--must have a jms router running before this target will work-->
<target name="all" depends="clean,release"/>
</project> <!--ChatRoom-->
The above listing shows the build script for the common layer.
Listing 5-5 shows the environment.properties file for the common layer:
# This is the environment.properties file for the Common Layer components of the application #to set the environment variable properties for the common layer. chatcommon.version=v2_0 chatcommon.module=chatcommon # # Dependency configurations # channel.home=basedir channel.jar=channel.jar channel.class=chtm.dest.ant.channel.Channel
The above listing sets the values for the channel.home, channel.jar, and channel.class variables of the common layer.
The chatserver component is the first executable component that the application delivers.
Listing 5-6 shows the build script for the chatserver component:
<project name="chatserver" default="all" basedir=".">
<!--Environmental Specific Properties-->
<property file="environment.properties"/>
<!--Initialize the build and run environments-->
<target name="init" unless="${chtr.server.target.init}">
<property name="chatserver.target.init" value="set"/>
<tstamp/>
<!--Project Properties-->
<property name="chatserver.src" value="."/>
<property name="chatserver.classes" value="../../classes"/>
<property name="chatserver.lib" value="../../lib" />
<property name="chatserver.doc" value="../../docs"/>
<property name="chatserver.javadoc" value="../../${chtr.server.doc}/api"/>
<!--build properties-->
<property name="chatserver.root.package" value="chtm.dest.ant.connection"/>
<!--Project Jar files-->
<property name="chatserver.jar" value="${chatserver.lib}/chatserver.jar"/>
<available file="${chatserver.lib}/${chatcommon.jar}" property="chatcommon.classpath"
value="${chatserver.lib}/${chatcommon.jar}"/>
<available file="${chatserver.lib}/${channel.jar}" property="channel.classpath"
value="${chatserver.lib}/${channel.jar}"/>
<available classname="${channel.class}" property="channel.classpath"
value="${chatserver.lib}/${chatcommon.jar}">
<classpath path="${chatserver.lib}/${chatcommon.jar}"/>
</available>
<condition property="server.dependencies">
<and>
<isset property="chatcommon.classpath"/>
<isset property="channel.classpath"/>
</and>
</condition>
</target> <!--init-->
<target name="dependents" unless="server.dependencies">
<fail message="Cannot find one of channel or common.">
</target>
<target name="compile" depends="init,dependents">
<mkdir dir="${chatserver.classes}"/>
<javac srcdir="${chatserver.src}" destdir="${chatserver.classes}">
<classpath>
<pathelement location="${channel.classpath}"/>
<pathelement location="${common.classpath}"/>
</classpath>
</javac>
<copy todir="${chatserver.classes}">
<fileset dir="${chatserver.src}" >
<exclude name="**/*.java"/>
<exclude name="*" />
</fileset>
</copy>
</target> <!--compile-->
<target name="jar" depends="init">
<mkdir dir="${chatserver.lib}"/>
<jar jarfile="${chatserver.jar}" basedir="${chatserver.classes}"/>
</target>
<target name="release" depends="compile,jar">
<delete dir="${chatserver.classes}"/>
</target> <!--release -->
<target name="javadoc" depends="init">
<mkdir dir="${javadoc}"/>
<javadoc sourcepath="${chatserver.src}" destdir="${chatserver.javadoc}"
packagenames="${chatserver.root.package}" Windowtitle="${chatserver.windowtitle}" Overview=
"${chatserver.src}/overview.html">
<classpath>
<pathelement location="${chatserver.src}"/>
</classpath>
</javadoc>
</target> <!--javadoc-->
<!--Delete the ${build} and ${dist} directory trees -->
<target name="clean" depends="init">
<delete dir="${chatserver.classes}"/>
<delete file="${chatserver.jar}" />
<delete dir="${chatserver.javadoc}"/>
</target> <!--clean -->
<target name="all" depends="clean,release"/>
</project> <!--chatserver -->
The above listing adds the common layer as a dependency. Due to this dependency, the first two available tasks perform dependency checks. The <condition> task combines the two classpath properties into a server.dependencies property.
Listing 5-7 shows the environment.properties file for chatserver:
#Used to build Chatroom Server #Release Number chatserver.release=v2_0 chatserver.module=chatserver # # Dependency configurations # channel.home=basedir channel.jar=channel.jar channel.class=chtm.dest.ant.channel.Channel chatcommon.home=basedir chatcommon.jar=common.jar chatcommon.class=chtm.dest.ant.chatroom.TopicMessage
The above listing defines the environment.properties file for chatserver. This file includes properties that define the home directory, the JAR file name, and the class file for the Channel and common layer components.
The build script for chatclient depends on the availability of the server component. This is because you have defined this dependency in the unittest target. You need to ensure the existence of all the dependencies of the client that you have defined so far in the scripts.
Listing 5-8 shows the build script for the chatclient component:
<!-- this is the build file required by ant for building the channel application-->
<project name="chatclient" default="all" basedir=".">
<!--Environmental Specific Properties-->
<property file="environment.properties"/>
<!--Initialize the build and run environments-->
<target name="init" unless="${chatclient.target.init}">
<property name="chatclient.target.init" value="set"/>
<tstamp/>
<!--these are Project Properties-->
<property name="chatclient.src" value="."/>
<property name="chatclient.classes" value="../../classes"/>
<property name="chatclient.lib" value="../../lib" />
<property name="chatclient.doc" value="../../docs" />
<property name="chatclient.javadoc" value="../../${chatclient.doc}/api"/>
<!--these are build properties-->
<property name="chatclient.root.package" value="chtm.dest.ant.channel"/>
<property name="chatclient.root.path" value="chtm/dest/ant/channel"/>
<property name="chatclient.unittest.package" value="${chatclient.root.package}.unittest"
/>
<!-- property name="chatclient.unittest.path" value="${chatclient.root.path}/unittest"/
-->
<!--these are Project Jar files -->
<property name="chatclient.jar" value="${chatclient.lib}/chatroom.jar"/>
<!--these are entry points -->
<property name="chatserver.main" value="chtm.dest.ant.chatroom.ChatServer"/>
<property name="chatadmin.main" value="chtm.dest.ant.chatroom.AdminServer"/>
<property name="chatclient.testcase.1.main"
value="chtm.dest.ant.chatroom.unittest.TopicClientTestCase1"/>
<property name="chatclient.testcase.2.main"
value="chtm.dest.ant.chatroom.unittest.TopicClientTestCase2"/>
<!--these are dependencies-->
<available
file="${chatclient.lib}/${chatserver.jar}"property="chatserver.classpath"value="${chatclient.lib
}/${chatserver.jar}"/>
<available
file="${chatclient.lib}/${chatcommon.jar}"property="chatcommon.classpath"value="${chatclient.lib
}/${chatcommon.jar}"/>
<available file="${chatclient.lib}/${channel.jar}"property="channel.classpath"
value="${chatclient.lib}/${channel.jar}"/>
<available classname="${channel.class}"property="channel.classpath"
value="${chatclient.lib}/${chatcommon.jar}">
<classpath path="${chatclient.lib}/${chatcommon.jar}"/>
</available>
<condition property="chatclient.dependencies">
<and>
<isset property="chatcommon.classpath"/>
<isset property="channel.classpath"/>
<isset property="chatserver.classpath"/>
</and>
</condition>
</target>
<target name="dependent"unless="chatclient.dependencies">
<fail message="Cannot find one of channel,.common or chatserver."/>
</target>
<target name="compile" depends="init,dependent">
<mkdir dir="${chatclient.classes}"/>
<javac srcdir="${chatclient.src}" destdir="${chatclient.classes}">
<classpath>
<pathelement location="${channel.classpath}"/>
<pathelement location="${chatcommon.classpath}">
</classpath>
</javac>
<copy todir="${chatclient.classes}">
<fileset dir="${chatclient.src}">
<exclude name="**/*.java"/>
<exclude name="*"/>
</fileset>
</copy>
</target> <!--compile-->
<target name="jar" depends="unittest,filter">
<mkdir dir="${chatclient.lib}"/>
<jar jarfile="${chatclient.jar}" basedir="${chatclient.classes}"/>
</target> <!-- jar -->
<target name="javadoc" depends="init">
<mkdir dir="${javadoc}"/>
<javadoc sourcepath="${chatclient.src}" destdir="${chatclient.javadoc}"
packagenames="${chatclient.root.package}" Windowtitle="${chatclient.windowtitle}"
Overview="${chatclient.src}/overview.html">
<classpath>
<pathelement location="${chatclient.src}"/>
<pathelement location="${jms.jar}"/>
</classpath>
</javadoc>
</target> <!--javadoc-->
<!--Delete classes used for unittest-->
<target name="filter" depends="init">
<delete dir= "${chatclient.classes}/${chatclient.unittest.path}"/>
</target>
<!--Release the client -->
<target name="release" depends="compile,jar">
<delete dir="${chatclient.classes}"/>
</target>
<!--unit testing timeout is set for the task that runs the router because this version
of swiftmq does always respond to a shutdown command.-->
<target name="unittest" depends="init">
<parallel>
<sequential>
<echo message="Router Started"/>
<java classname="${jms.router}" fork="yes" timeout="45000" dir="${jms.router.config.root}"
output="jmsrouter.log" >
<arg value="${jms.router.config}"/>
<classpath>
<fileset dir="${jms.lib}">
<include name="**/*.jar"/>
</fileset>
</classpath>
</java>
<echo message="Router Stopped"/>
</sequential>
<sequential>
<sleep seconds="5"/>
<echo message="ChatServer Started"/>
<java classname="${chatserver.main}" fork="yes">
<classpath>
<pathelement location "${chatserver.classpath}"/>
<pathelement location="${channel.classpath}"/>
<pathelement location="${chatcommon.classpath}"/>
<pathelement location="${jms.provider.jar}"/>
<pathelement location "${jms.client.jar}"/>
</classpath>
</java>
<echo message="ChatServer Stopped"/>
</sequential>
<sequential>
<sleep seconds="15"/>
<parallel>
<sequential>
<echo message="Test Client 1 Started"/>
<java classname"${chatclient.testcase.1.main}" fork ="yes">
<classpath>
<pathelement location= "${chatclient.classes}"/>
<pathelement location="${channel.classpath}"/>
<pathelement location="${chatcommon.classpath}"/>
<pathelement location="${jms.provider.jar}"/>
<pathelement location="${jms.client.jar}"/>
</classpath>
</java>
<echo message="Test Client 1 Completed"/>
</sequential>
<sequential>
<sleep seconds="5" />
<echo message="Test Client 2 Started"/>
<java classname="${chatclient.testcase.2.main}" fork="yes" >
<classpath>
<pathelement location="${chatclient.classes}"/>
<pathelement location="${channel.classpath}"/>
<pathelement location="${chatcommon.classpath}"/>
<pathelement location="${jms.provider.jar}"/>
<pathelement location="${jms.client.jar}"/>
</classpath>
</java>
<echo message="Test Client 2 Completed"/>
</sequential>
</parallel>
<echo message="AdminServer Started"/>
<java classname="${chatadmin.main}" fork="yes">
<classpath>
<pathelement location="${chatserver.classpath}"/>
<pathelement location="${channel.classpath}"/>
<pathelement location="${chatcommon.classpath}"/>
<pathelement location="${jms.provider.jar}"/>
<pathelement location="${jms.client.jar}"/>
</classpath>
</java>
<echo message= "AdminServer Stopped"/>
</sequential>
</parallel>
</target> <!--unittest-->
<!--SwiftMQ router -->
<target name="jmsrouter" depends="init">
<java classname= "${jms.router}" fork="yes" dir="${jms.router.config.root}"
output="jmsrouter.log" >
<arg value="${jms.router.config.root}/${jms.router.config}"/>
<classpath>
<fileset dir="${jms_home}/jars">
<include name="**/*.jar"/>
</fileset>
</classpath>
</java>
</target> <!--jmsrouter-->
<!--Delete the ${build} and ${dist} directory trees-->
<target name="clean" depends="init">
<delete dir="${chatclient.classes}"/>
<delete file="${chatclient.jar}"/>
<delete dir="${chatclient.javadoc}"/>
</target> <!--clean-->
<target name="all" depends="clean,release"/>
</project> <!--client-->
The above listing checks if all the determined dependencies, such as library, jar, and classpath, exist. The build process requires the class files to be bundled into JAR files. The listing uses a combination of sequential and parallel tasks to perform the test. The JMS router, chatserver, and two chatclient processes execute in parallel. This build script contains four blocks of sequential elements, one for each process. Within each sequential attribute, the build script echoes an informational message before the contained tasks are executed. The build script gives the server target a short sleep time, which ensures that the JMS router is ready for execution. The build script gives the two clients a time delay to ensure that the server is up and running. The clients then exchange messages to complete the test.
You need to develop a script for the final integration process to build MyChatRoom.
Listing 5-9 shows the environment.properties file for chatclient:
# External properties used by Ant scripts to build a ChatRoom Client
#Release Number
chatclient.release=v2_0
chatclient.module=chatclient
#
# Root directory of JMS installation
#
jms.home=C:/swiftmq_2_0_1
jms.lib=${jms.home}/jars
jms.provider.jar=${jms.lib}/jms.jar
jms.client.jar=${jms.lib}/smqclient.jar
jms.console=chtm.swiftmq.admin.explorer.Explorer
jms.router=chtm.swiftmq.router.Router
jms.router.config=jms/conf/ChatroomRouter.properties
jms.router.config.root=../..
#
# Dependency configurations
#
channel.home=basedir
channel.jar=channel.jar
channel.class=chtm.dest.ant.channel.Channel
chatcommon.home=basedir
chatcommon.jar=common.jar
chatcommon.class=chtm.dest.ant.chatroom.TopicMessage
chatserver.home=basedir
chatserver.jar=chatserver.jar
chatserver.class=chtm.dest.ant.chatroom.ChatServer
The above listing sets the variables for setting up JMS. The listing also sets the variables of the home directory, the JAR file, and the class file for the Channel, common layer, and server components.
The supporting custom task is the shared library for the MyChatRoom application.
Listing 5-10 shows the source code of the supporting task:
package chtm.dest.ant.taskdef;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Mkdir;
import org.apache.tools.ant.taskdefs.Cvs;
import org.apache.tools.ant.taskdefs.Ant;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
/**
*
* @author santosh
*
*/
public class Release extends Task
{
private static String fileSeparator =System.getProperty("file.separator");
private boolean failonerror = true;
private String file;
private File sourceDir;
public Release() {}
public void setFailonerror(boolean value)
{
failonerror=value;
}
public void setFile(String value)
{
this.file = value;
}
public void setSourceDir(String value)
{
sourceDir = new File(value);
}
public void execute() throws BuildException
{
log("Release: " + this.file);
try
{
execute0();
}
catch (BuildException be)
{
if (failonerror)
throw be;
else
log(be.getMessage());
}
}
public void execute0() throws BuildException
{
try
{
this.createSourceDir();
BufferedReader configuration = new BufferedReader(new FileReader(this.file));
String configComponent;
while ( ( configComponent = configuration.readLine()) != null)
processConfigComponent(configComponent);
}
catch (Exception e)
{
throw new BuildException(e);
}
}
private void processConfigComponent(String component) throws BuildException
{
if ( ( component.length() == 0) || ( component.charAt(0) == '#') ||
(component.indexOf(",")<
0))
return;
StringTokenizer st = new StringTokenizer(component,",");
if ( ! st.hasMoreTokens())
return;
String moduleName = st.nextToken();
String version = st.nextToken();
String buildFile = st.nextToken();
this.getSource(moduleName, version);
while (st.hasMoreTokens())
{
this.perform(moduleName, buildFile, st.nextToken());
}
}
private void executeTask(Task task) throws BuildException
{
task.setProject(this.getProject());
task.setLocation(this.getLocation());
task.setOwningTarget(this.getOwningTarget());
task.init();
task.execute();
}
private void getSource(String moduleName, String version) throws BuildException
{
Cvs task = new Cvs();
task.setCommand("checkout");
task.setPackage(moduleName);
task.setTag(version);
task.setDest(this.sourceDir);
this.executeTask(task);
}
// here we are making the output directory
private void createSourceDir() throws BuildException
{
if ( ( sourceDir.exists()) && ( ! sourceDir.isDirectory()))
throw new BuildException(sourceDir.toString() + "is not a directory");
Mkdir task = new Mkdir();
task.setDir(this.sourceDir);
this.executeTask(task);
}
private void perform(String componentName, String buildFile, String taskName) throws
BuildException
{
Ant task = new Ant();
task.setInheritAll(false);
task.setTarget(taskName);
try
{
task.setAntfile(this.sourceDir.getCanonicalPath() + this.fileSeparator +
componentName +
this.fileSeparator + buildFile );
}
catch (IOException e)
{
throw new BuildException(e);
}
this.executeTask(task);
}
}
The above listing contains the execute0() method, which reads and triggers the process of each line in the configuration file. The first field is the Source Code Control (SCC) label that locks this version of the component. The second field is a version number. This information is passed to the getSource() method by the processConfigComponent() method.
The getSource() method configures a Concurrent Versions System(CVS) task to retrieve all the source code. The method configures the task map directly to their equivalent XML-specified attributes. The mapping between the attribute and the field names follows the JavaBeans specification. The <mkdir> task ensures that the target and source directories exist.
The executeTask() method sets the technical parameters of the task. The parameters include the project, the base directory, and the owning target. The last two lines first initialize the task and then execute it.
When the getSource() method finishes, the processConfigComponent() method executes each of the specified targets in the specified build file using the perform() method. This method, in turn, configures and then triggers the execution of the target in the Ant task.
Every method in the class throws an exception, BuildException. The <ant> task supports the standard failOnError setting that allows you to decide how to react to the exception.
The supporting custom task reads and then processes each line in the configuration file. The following code shows the configuration.properties file for the supporting task:
# the configuration.properties file for the supporting task myapp,v2_0,build.xml,clean,all supportingtasks,v2_0,build.xml,clean,all
Listing 5-11 shows the environment.properties file for the supporting task:
# This is a property file used for building a supporting task for our application # This property file is used by ant build script #Here we can put the version number for our application # # myapp.version=v2.0 #These are the root directory of ant and jms installation ant.home=C:/ant ant.path=C:ant//lib/ant.jar
The above listing sets user environment variables for the Ant installation.
Listing 5-12 shows the build script for the supporting task:
<!--this is the build file required by Ant for building the main chat room-->
<project name="release" default="all" basedir=".">
<!--These are Environmental Specific Properties-->
<property file="environment.properties"/>
<!--this is the target name init -->
<target name="init" unless="${chtm.dest.init}">
<property name="chtm.dest.init" value="set"/>
<tstamp/>
<property name="chtm.lib" value="lib"/>
<!--here we put the name of Project Jar files-->
<property name="chtm.path" value="${chtm.lib}/configmap.jar"/>
<taskdef name="release" classname="chtm.dest.ant.taskdef.Release" >
<classpath>
<pathelement location="${chtm.path}"/>
<pathelement location="${ant.path}"/>
</classpath>
</taskdef>
</target>
<!--here init target ends-->
<!--this is the depended target of init with name release-->
<target name="release" depends="init">
<release file="configuration.properties" sourceDir="src"/>
</target>
<!--this is final target named all which depends on release-->
<target name="all" depends="release"/>
</project>
<!--end of supporting task build-->
The above listing shows the build script for the supporting task. The init target sets the chtm.dest.init property. The unless target attribute checks the status of the chtm.dest.init property to ensure that the init target executes only once.
You need to develop the project-level build script to collect and then integrate all the individual components of the application. Every script maintains a degree of separation. The scripts are independent with respect to the settings of the properties in the environment.properties file.
In the project definition, the first line in the build script loads the environment.properties file. The purpose of that file is to define properties that are external to the project.
You can use the Ant tasks to execute each component-level build script. Custom tasks allow you to decrease the size of the build script.
To construct an application, you need to retrieve the correct version of each component used to build the application. When the build is in progress, you need to conduct tests to ensure the integrity of each component and the subsequent integrations. Each component contains its name and version. You can provide Ant with a list that contains these snippets of information. You need not embed this information in build scripts. You can add this information in a separate configuration file, configuration.properties.
Listing 5-13 shows the configuration.properties file:
# # A configuration describing the current build of MyChatRoom # # Version 2.0 # channel,v2_0,build.xml,clean,compile,unittest,filter chatcommon,v2_0,build.xml,compile,jar chatserver,v2_0,build.xml,release chatclient,v2_0,build.xml,release
The above listing defines the versions of various components. Each line contains four comma-separated columns. The first column contains the name of the component. The next column is the label used to retrieve the source and artifacts from Source Code Control System (SCCS). The third column specifies the name of the build script. The last column contains the names of each target to be executed.
To construct this application, you need to set the Ant home directory variable. In addition, you need to make available the JAR files, such as ant.jar for Ant on the CLASSPATH environment variable and configmap.jar for the supporting task library.
Listing 5-14 shows the environment.properties file for the build process of the MyChatRoom application:
The above listing sets the home and class path environment variables of the Ant installation.
The build script for MyChatRoom contains the init, clean, and all targets. The difference between the targets in the MyChatRoom build script and the targets in the component-level build scripts is that the targets in the MyChatRoom build script are focused at the project level and the targets in the component-level build scripts are focused at target level.
Listing 5-15 shows the build script for the MyChatRoom application:
<!--this is the build file required by ant for building the main chat room-->
<project name="MyChatRoom" default="all" basedir=".">
<!--these are Environmental Specific Properties -->
<property file="environment.properties"/>
<!--this is our first target named init -->
<target name="init" unless="${chtr.target.init}">
<property name="cr.target.init" value="set"/>
<tstamp/>
<!--these are project specific Properties-->
<property name="MyChatRoom.src" value="src"/>
<property name="MyChatRoom.classes" value="classes"/>
<property name="MyChatRoom.lib" value="lib"/>
<taskdef name="release" classname="chtm.dest.ant.taskdef.Release" >
<classpath>
<pathelement location="${ant.extn.path}"/>
<pathelement location="${ant.path}"/>
</classpath>
</taskdef>
</target>
<!--here init target ends-->
<!--this is the depended target of init with name release-->
<target name="release" depends="init">
<release file="configuration.properties" sourceDir="src"/>
</target>
<!--this is final target named all which depends on release-->
<target name="clean" depends="init">
<delete dir="${MyChatRoom.lib}"/>
</target> <!--clean-->
<target name="all" depends="clean,release"/>
</project> <!--channel-->
The above listing parameterizes all the properties considered as internal to the project, such as the source, class, and lib directories. External properties, such as the JMS implementation, the JAR containing the custom task, and Ant, are set in the environment.properties file.
The <taskdef> task defines a custom task. The custom task is not part of MyChatRoom and is not contained in the project directory of MyChatRoom. As a result, the property that identifies the JAR containing the class used to support the custom release task is specified in the environment.properties file.
| Table of Contents |