|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
I needed to add a new task to an Ant-driven build and I implemented that task using Spring, a lightweight IoC (Inversion of Control) framework. I encountered virtually no issues since, because an IoC container is noninvasive, it is relatively easy to create a wrapper or just use the objects that implement the task. I then began to wonder if Ant could directly use Spring-configured objects, thus reusing the dependency graph and configuration already specified and tested. Why duplicate and introduce side effects or other problems? If IoC containers really do provide a benefit, a more direct use is warranted.
This article introduces this approach and presents a proof-of-concept implementation. Developers new to Ant extension coding should find this example interesting.
To add a custom task to Ant, the Ant manual recommends using an Ant class intended for this purpose, like the Task class. But, even this recommendation is not mandatory; Ant can invoke any class that has an execute() method. (Of course, Ant can also invoke any program using the exec or java task, but that is a different type of extension.) Ant also supports aggregating these task extensions into various kinds
of properties or XML files.
The best approach for adding a custom task to Ant is one that reuses the IoC framework via a Task extension. Thus, a Task that performs what a standalone application must do to set up and use the framework's hosted objects and resources builds
upon the strength of Ant.
The Inversion of Control (IoC) design pattern, also called Dependency Injection (DI), in the framework context, is concerned with the componentization of Java objects. The growing interest in IoC frameworks is due in large part to the Spring framework's developers showing the synergy available in an IoC/AOP/XML/JavaBeans lightweight framework, which offers capabilities beyond DI by allowing the creation of a powerful abstraction layer to other APIs or components. Spring is itself an example of the use of IoC. Also note that Ant seems conformant with this IoC container since it too is XML/JavaBeans-based and, in some ways, already uses IoC.
Our Ant IoC task extension requirements can be specified in an actor/goal/requirements format (the requirements are not ordered):
FQCN (fully qualified class name) as target
antBeanexecuteantBean if none specified
execute()The task specification that supports these requirements is SpringContextTask.
This task invokes a method on an object either managed by a Spring container or unmanaged and specified as a FQCN. Classpath Spring bean definition references are not supported yet.
SpringContextTask's parameters are shown in the table below.
Parameters
|
The simplest example of applying our new Ant task extension is:
<!-- create the task definition -->
<taskdef name="runBean" classpathref="testpath"
classname="jbetancourt.ant.task.spring.SpringContextTask"/>
<target name="simpleAppContextUseWithDefaults">
<runBean beanLocations="applicationContext.xml"></runBean>
</target>
The simpleAppContextUseWithDefaults target invokes execute() on the bean named antBean in the bean definition applicationContext.xml found on the file path. The path attribute name is plural to support future use of multiple bean definition files.
This bean invocation resembles how Ant already invokes an object; however, this time, an IoC container manages the bean. The container could have added transaction dependencies, wired in datastores, set up a Web services proxy, used remoting, or even supplied an AOP (aspect-oriented programming) proxy instead of an actual target bean. Our approach simplifies configuration since the Ant script does not need to know how to configure an object, especially a complex one. But, what if the Ant script must set some required properties for a service call on a bean:
<target name="publish">
<spring
beanLocations="applicationContext.xml"
beanName="siteGenerator"
methodName="generateSite"
host="${host.site.url}"
port="${site.port}">
Made a few tweaks. Removed some sentence fragments.
</spring>
</target>
Note that since the task name is defined in a taskdef, the name used depends on the Ant taskdef definition (not shown); here, the task name is spring. Now we specify the bean name and a method to call. The element text is also pushed to the target bean. In this example,
the text is a publish comment.
With the use of Ant's dynamic attributes feature, we also push required properties to the target object. Usually when an attribute
is parsed in an Ant file, a setter is called on the task class. With dynamic attributes, a non-object property or field is
added to the object with the setDynamicAttribute() method. In essence, this property injection provides an override capability, since the container could have already wired
in the properties in the hosted bean. But, won't this complicate configuration? Will we have to maintain properties for use
by Ant tasks and also properties needed by the managed objects?
Archived Discussions (Read only)