| By Kirk Pepperdine | Article Rating: |
|
| September 1, 2003 12:00 AM EDT | Reads: |
22,663 |
You have a task that your Ant build process needs to perform
and none of the built-in or dozens of optional tasks fits the bill.
If at this point you're thinking that Ant won't work for you, then
the authors of Ant have some wonderful news. The framework they use
to run built-in tasks is also available for your own task.
If that piques your interest, you'll be happy to know that in the next few paragraphs, I promise you'll have all the information you need to use this framework. If you haven't used Ant yet, read the excellent article by Joey Gibson, "A (Brief) Introduction to Ant" (JDJ, Vol. 7, issue 11), before venturing on this journey.
The framework that supports Ant loosely defines a contract that describes the responsibilities of the task provider. Briefly stated, the task provider provides a class that implements the desired behavior and introduces this task and its supporting class to Ant, exploiting one of the means available for that purpose. The framework is responsible for discovering the task's supporting class and then managing that class's life cycle. Although a task is fronted with a single class, other classes (which may or may not be known to Ant) may be used to help support the completion of the task. Let's take an in-depth look at the custom task contract defined by Ant.
For a class to be considered a custom task it must implement the method, public void execute(). That's it! If you're wondering what the catch is, it's pretty much the same as with everything else. The more information you provide, the less work the framework has to do in order to work with your task. Providing additional information allows the framework to do more work for you. With this in mind, let's describe the portion of the contract we're concerned with here. The description of the providers' responsibilities are as follows:
- Named task must be supported with a Java class.
- The class must implement public void execute().
- The class may extend import org.apache.tools.ant.Task.
- The class may implement public void init().
- The class must implement a set method for each attribute of the task.
- The class should implement the default constructor.
- The name task must be registered with either the taskdef task or via the addition of an entry into the task.properties file.
- The execute method should throw a org.apache.tools.ant. BuildException. Complementary to the providers' responsibilities are the frameworks' responsibilities:
- Call the default constructor for the provider's supporting class.
- Call the public void init() method once.
- Call the appropriate set method for every task attribute.
- Call the public void execute() throws BuildException method once for every occurrence of the corresponding task found in the build file.
In addition to those assuming these responsibilities, the provider must define a set of XML tags to be used when exploiting the custom task. From here, we could proceed to a fairly dry explanation of how all this fits together, but let's see how it all works in practice instead. I'll kick start the process with the following story.
Most applications built today contain a number of "off-the-shelf" components that complement code developed in-house. These components can be supplied by a combination of vendors and infrastructure teams that work alongside the application development team. Given that each group (especially third-party suppliers) will have its own delivery schedule over which you most likely won't have any influence, it would be unheard of not to have to deal with version management. Lack of a concise plan to manage versions often results in confusion among configuration management staff that can result in them having to spend a considerable amount of time sorting out which versions of which components (a.k.a. JARs) are to be used for each build. A tactical solution to this problem is to use a version map to describe the build. Let's first build a sample map and then a custom task to use this map to drive a build process.
To keep things somewhat simplified so we aren't too distracted from our main goal - learning how to build a custom task - we'll purposely limit the functionality of the version map to pulling artifacts from CVS. The test application will be a toy chat application that consists of four layers (see Figure 1).
The JMS bus was acquired from a third-party vendor (SwiftMQ in this instance). Our internal infrastructure group is responsible for the channel component. Finally, the application development team has built chat client and topic server. During the development process, they've also identified a common layer shared by the clients and the server. In accordance with our earlier constraints, the JMS implementation can be found in the file system. The JAR file for the channel, as well as the source for commons, the chat client, and the topic server will be pulled from CVS. Let's use XML to build the version map.
The map contains all the information needed to identify all the attributes that are necessary to locate a component. Note: This mechanism does rely on a stable policy of how code artifacts are to be stored.
As mentioned earlier, we're required to register the custom task. This can be completed using one of two techniques. The taskdef task maps an alias for a task to its implementation. The second technique inserts the definition into the task.properties file found in the Ant distribution. A quick examination of the file will reveal how to do this. I'm going to avoid the discussion as to which approach is best, but there are implications from using either mechanism to register your custom task that go beyond technical issues. Let's look at the build file fragment found in the following code.
This is telling Ant that we'd like to define the custom task aliased as versionmap and implemented in the class com.jpt.ant.task.VersionMap. The task versionmap takes two attributes, the name of the file containing the map and a destination directory to work in.
Again referring back to the contract, we see that we should create a class that implements our custom behavior. Though it's not necessary, it makes life a lot easier to have our class extend org.apache.tools.ant.Task. Extending this class allows us to inherit common task behavior. We'll have to decide on an implement for the methods public void init() and public void execute(). In addition, the XML defines the two attri- butes, file and dest. Fields and supporting access methods must be implemented to support these attributes. Let's look at the custom task class template found in Listing 1.
With this, we've pretty much covered the entire contract. All that's left is to fill in the task's behavior. Let's start with the init method. The init method gets called once and this occurs fairly early on in the build process. Consequently, we should embed behavior that we only want executed once and that can also occur early in the build process. Since our custom task doesn't have any behavior that fits this description, we allow the call to percolate up to the super classes implementation.
A common technique used for coding the execute method is to defer the bulk of the implementation to a helper method called execute0. Doing this allows us to easily handle the inherited attribute, failonerror, in the execute method. The main logic that we need is to read each entry in the version map and use the information to trigger a CVS get (see Listing 2). Note how the functionality of other tasks is being used to help us achieve our goal.
In the execute0() method, the XML file is converted into a map. An iterator runs through each entry in the map and calls its execute method using the task as a parameter. The execute method of the task is then free to make a call back on the getSourceFromCVS method. It's this method that creates an instance of a CVS (from the optional tasks list) task and then configures it. The executeTask method sets the technical parameters on the task before executing it. Our simplified custom task is now complete.
Where to Go from Here
Nested attributes can add a lot of flexibility to your custom
task. Each Ant data type object is supported by a class and,
consequently, the development path is fairly similar to one that's
followed by custom tasks. In addition to tasks and data types, you
can also build specialized build listeners. Though their development
path is slightly different than that of data types and tasks, it's
still not all that difficult. I've always found that it's best to
look into Ant's code when I'm looking for examples or techniques to
help me develop custom tasks, data types, and listeners. Happy AntingS
References
* Ant 1.5.1 source code: www.apache.org
* Williamson, A., et al. (2003). Ant Developer's Handbook. Sams.
Published September 1, 2003 Reads 22,663
Copyright © 2003 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Kirk Pepperdine
Kirk Pepperdine has more than 10 years of experience in OO technologies. In edition to his work in the area of performance tuning, Kirk has focused on building middleware for distributed applications.
![]() |
Phil Willemann 09/23/03 06:07:52 PM EDT | |||
I am a beginner at custom ant tasks. Shouldn't execute0() include a call to getSourceFromCVS? |
||||
![]() |
Phil Willemann 09/23/03 06:05:33 PM EDT | |||
- Cloud CEOs, CTOs & SVPs to Speak at 4th International Cloud Computing Expo
- Kindle 2 vs Nook
- Why IBM’s Server Chief Got Busted
- The Difference Between Web Hosting and Cloud Computing
- Cloud Computing Journal Opens "Readers' Choice Awards" Nominations
- Cloud Computing Expo: Exclusive Q&A with Yahoo! SVP Cloud Computing
- Industry Experts Discuss the State of Cloud Computing
- Ajax in RichFaces 3.3, JSF 2 and RichFaces 4
- It's the Java vs. C++ Shootout Revisited!
- The End of IT 1.0 As We Know It Has Begun
- An Introduction to Abbot
- Java Kicks Ruby on Rails in the Butt
- Interviewing Java Developers With Tears in My Eyes
- Cloud CEOs, CTOs & SVPs to Speak at 4th International Cloud Computing Expo
- 1st Annual Government IT Expo: Call for Papers Deadline July 15
- How to Diagnose Java Resource Starvation
- REA Is Where RIA Becomes the Norm
- Kindle 2 vs Nook
- Anatomy of a Java Finalizer
- Why IBM’s Server Chief Got Busted
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- The i-Technology Right Stuff
- JavaServer Faces (JSF) vs Struts
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate
- What's New in Eclipse?



































