Ant 是一个 Apache 基金会下的跨平台的构件工具,现已经被广泛的应用在了各个开源的项目的构架。
Ant自身提供了一系列的Core Task,可以轻松的实现目录管理,编译,运行等功能。当然除了Core Task提供的功能之外,我们也有一些功能上的扩展的需求。很简单的方式是把自定义的功能封成jar包,调用Task-java来执行主类,这样做的问题是输入的属性将会以参数串的形式出现,很不直观。所以ant提供了另一种方式:自定义Task。自定义Task和Core-Task一样,有自己的Task名和属性。下面来讲一下如何自定义ant Task,以及自定义Task的的创建。
1.如何创建一个Task
这部分在ant的手册里面写的很清楚,贴一段先
It is very easy to write your own task:
- Create a Java class that extends
org.apache.tools.ant.Task
or another class that was designed to be extended. - For each attribute, write a setter method. The setter method must be a
public void
method that takes a single argument. The name of the method must begin withset
, followed by the attribute name, with the first character of the name in uppercase, and the rest in lowercase*. That is, to support an attribute namedfile
you create a methodsetFile
. Depending on the type of the argument, Ant will perform some conversions for you, see below. - If your task shall contain other tasks as nested elements (like
parallel
), your class must implement the interfaceorg.apache.tools.ant.TaskContainer
. If you do so, your task can not support any other nested elements. See below. - If the task should support character data (text nested between the start end end tags), write a
public void addText(String)
method. Note that Ant does not expand properties on the text it passes to the task. - For each nested element, write a create, add or addConfigured method. A create method must be a
public
method that takes no arguments and returns anObject
type. The name of the create method must begin withcreate
, followed by the element name. An add (or addConfigured) method must be apublic void
method that takes a single argument of anObject
type with a no-argument constructor. The name of the add (addConfigured) method must begin withadd
(addConfigured
), followed by the element name. For a more complete discussion see below. - Write a
public void execute
method, with no arguments, that throws aBuildException
. This method implements the task itself.
简单的翻译一下,
首先要把%ANT_HOME%/lib下的ant.jar引入classpath,创建一个org.apache.tools.ant.Task 的子类。
如果要支持一个属性,只需要简单的实现一个public的函数,函数名为set+属性的小写。如果用户填写了该属性,ant会自动调用该函数。
如果要支持级联的属性,则应该实现org.apache.tools.ant.TaskContainer的接口。
重载父类的public void execute函数,并在里面实现主逻辑,如果出错,请抛出一个BuildException的异常,因为函数没返回值。
下面来两个例子:
package com.mydomain; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; public class MyVeryOwnTask extends Task { private String msg; // The method executing the task public void execute() throws BuildException { System.out.println(msg); } // The setter for the "message" attribute public void setMessage(String msg) { this.msg = msg; } }
It's really this simple ;-)
Adding your task to the system is rather simple too:
- Make sure the class that implements your task is in the classpath when starting Ant.
- Add a
<taskdef>
element to your project. This actually adds your task to the system. - Use your task in the rest of the buildfile.
Example
<?xml version="1.0"?>
<project name="OwnTaskExample2" default="main" basedir=".">
<target name="build" >
<mkdir dir="build"/>
<javac srcdir="source" destdir="build"/>
</target>
<target name="declare" depends="build">
<taskdef name="mytask"
classname="com.mydomain.MyVeryOwnTask"
classpath="build"/>
</target>
<target name="main" depends="declare">
<mytask message="Hello World! MyVeryOwnTask works!"/>
</target>
</project>
嵌套的情况则需要实现一个节点类,并在父类上用Vector来替换String表示属性,这里面就不具体做介绍了。
2.如何调试自定义的Task
ant本身是个批处理,逻辑也是用java实现的,结合上面的了解,可以看出自定义的类Task是以refect的方式由java虚拟机加载执行的。即主类存在用户的工程里面。一种方式是将ant的相应jar引入当前工程的classpath,创建一个application的debug配置,将主类设置为ant的主类:org.apache.tools.ant.launch.Launcher。但存在的问题很多,首先ant执行的环境变量,当前目录等都和真正的运行环境有所区别。要配置很多的环境信息和参数,不利于多次调试。
我推荐的方式是利用java的远程调试来达到调试的目的。
2.1 创建远程调试server
Java远程调试
-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,address=3999,suspend=n
-XDebug 启用调试。
-Xnoagent 禁用默认sun.tools.debug调试器。
-Djava.compiler=NONE 禁止 JIT 编译器的加载。
-Xrunjdwp 加载JDWP的JPDA参考执行实例。
transport 用于在调试程序和 VM 使用的进程之间通讯。
dt_socket 套接字传输。
dt_shmem 共享内存传输,仅限于 Windows。
server=y/n VM 是否需要作为调试服务器执行。
address=3999 调试服务器的端口号,客户端用来连接服务器的端口号。
suspend=y/n 是否在调试客户端建立连接之后启动 VM 。
Java的远程调试JDPA是基于JDWP协议的一种交互式调试方式,最大的特点是VM的运行环境和调试环境相互独立。这样就可以很好的解决运行和调试的环境问题。
要实现远程调试,需要在server端的虚拟机参数中加入:
-Xdebug -Xrunjdwp:transport=dt_socket,address=5050,server=y,suspend=y
即:使用端口的方式进行JDWP通讯,端口为5050,采用挂起的方式,只有客户端连上才启动虚拟机。
对于ant来说,此参数无疑要加到启动ant主类的参数中。我们分析ant.bat看到:
"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%/lib/ant-launcher.jar" "-Dant.home=%ANT_HOME%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% %ANT_CMD_LINE_ARGS%
而%ANN_OPTS%这个环境变量在批处理之前并没有得到设置,看来这就是ANT留给我们的操作参数变量,创建一个debug_ant.bat
set ANT_OPTS=
-Xdebug -Xrunjdwp:transport=dt_socket,address=5050,server=y,suspend=y
call ant %*
用bebug_ant.bat来替换ant.bat就可以在不影响任何其它环境变量的情况下,启动远程调试server。
2.2 创建远程服务Client
用eclipse打开你的自定义Task所在的工程,打开Debug configuration,新增Remote Java Application
选择connection Type: Standard(SocketAttach)
选择connection Properties:
Host: local host
Port: 5050
2.3 开始调试
在对应的位置用debug_ant来替换ant执行相关的xml脚本,此时虚拟机会挂起。
在eclipse的代码相应位置设置好断点,采用刚刚创建的configuration进行调试。
此时ant的虚拟机将开始执行,并会在断点相应的位置上做出响应。
OK,现在享受你自定义的task带来的方便吧。