Ant 扩展深入探究
1. 日志记录相关
Ant 提供了多种日志记录的方式,下面将详细介绍使用 Log4j 进行日志记录以及自定义日志记录器的相关内容。
1.1 使用 Log4j 日志记录功能
Ant 包含了
Log4jListener
和
CommonsLoggingListener
,二者都能将日志输出到 Log4j。
CommonsLoggingListener
适用于需要灵活选择日志记录 API 的环境,但除了 Log4j 依赖外,还需要
commons - logging.jar
。
当应用程序记录事件时,事件会被记录到特定的类别中,类别是基于点分隔的文本表示的层次结构。Ant 的
CommonsLoggingListener
类使用
BuildEvent
提供的上下文的完全限定类名作为类别名称。每个事件会被记录到
DEBUG
、
WARN
、
INFO
、
ERROR
或
FATAL
优先级级别(注意:
CommonsLoggingListener
不使用
FATAL
级别)。
Log4j 从
log4j.properties
文件(或使用
ANT_OPTS=-Dlog4j.configuration=<filename>
)进行初始化。默认情况下,配置文件从当前目录加载。
CommonsLoggingListener
通过 Log4j 触发每个事件,包括调试信息,这使得我们可以对记录哪些事件、记录位置以及使用的格式进行配置控制。
以下是一个
log4j.properties
文件示例,用于实现日志记录和邮件通知功能:
log4j.rootCategory=INFO,file
log4j.category.org.apache.tools.ant.Project=INFO,file,mail
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.layout=org.apache.log4j.TTCCLayout
log4j.appender.file.file=build.log
log4j.appender.file.maxBackupIndex=3
log4j.appender.file.maxFileSize=100KB
log4j.appender.mail=org.apache.log4j.net.SMTPAppender
log4j.appender.mail.layout=org.apache.log4j.HTMLLayout
log4j.appender.mail.Threshold=ERROR
log4j.appender.mail.SMTPHost=localhost
log4j.appender.mail.bufferSize=1
log4j.appender.mail.to=erik@example.org
log4j.appender.mail.from=erik@example.org
log4j.appender.mail.subject=Build Failure!
下面是一个简单的
build.xml
文件示例,用于触发构建失败:
<project default="fail">
<target name="fail">
<fail message="Example build failure"/>
</target>
</project>
使用以下命令行运行:
ant -listener org.apache.tools.ant.listener.CommonsLoggingListener
控制台输出如下:
Buildfile: build.xml
fail:
BUILD FAILED
C:\AntBook\Sections\Extending\listeners\build.xml:3: Example build failure
Total time: 1 second
同时,
build.log
文件会被创建或追加内容,示例输出如下:
[main] INFO org.apache.tools.ant.Project - Build started.
[main] INFO org.apache.tools.ant.Project - Build started.
[main] INFO org.apache.tools.ant.Target - Target "fail" started.
[main] INFO org.apache.tools.ant.taskdefs.Exit - Task "fail" started.
[main] ERROR org.apache.tools.ant.taskdefs.Exit - Task "fail" finished with error.
C:\AntBook\Sections\Extending\listeners\build.xml:3: Example build failure
at org.apache.tools.ant.taskdefs.Exit.execute(Exit.java:90)
at org.apache.tools.ant.Task.perform(Task.java:317)
at org.apache.tools.ant.Target.execute(Target.java:309)
at org.apache.tools.ant.Target.performTasks(Target.java:334)
at org.apache.tools.ant.Project.executeTarget(Project.java:1216)
at org.apache.tools.ant.Project.executeTargets(Project.java:1160)
at org.apache.tools.ant.Main.runBuild(Main.java:605)
at org.apache.tools.ant.Main.start(Main.java:195)
at org.apache.tools.ant.Main.main(Main.java:234)
[main] ERROR org.apache.tools.ant.Target - Target "fail" finished with error.
C:\AntBook\Sections\Extending\listeners\build.xml:3: Example build failure
at org.apache.tools.ant.taskdefs.Exit.execute(Exit.java:90)
at org.apache.tools.ant.Task.perform(Task.java:317)
at org.apache.tools.ant.Target.execute(Target.java:309)
at org.apache.tools.ant.Target.performTasks(Target.java:334)
at org.apache.tools.ant.Project.executeTarget(Project.java:1216)
at org.apache.tools.ant.Project.executeTargets(Project.java:1160)
at org.apache.tools.ant.Main.runBuild(Main.java:605)
at org.apache.tools.ant.Main.start(Main.java:195)
at org.apache.tools.ant.Main.main(Main.java:234)
[main] ERROR org.apache.tools.ant.Project - Build finished with error.
C:\AntBook\Sections\Extending\listeners\build.xml:3: Example build failure
at org.apache.tools.ant.taskdefs.Exit.execute(Exit.java:90)
at org.apache.tools.ant.Task.perform(Task.java:317)
at org.apache.tools.ant.Target.execute(Target.java:309)
at org.apache.tools.ant.Target.performTasks(Target.java:334)
at org.apache.tools.ant.Project.executeTarget(Project.java:1216)
at org.apache.tools.ant.Project.executeTargets(Project.java:1160)
at org.apache.tools.ant.Main.runBuild(Main.java:605)
at org.apache.tools.ant.Main.start(Main.java:195)
at org.apache.tools.ant.Main.main(Main.java:234)
[main] ERROR org.apache.tools.ant.Project - Build finished with error.
C:\AntBook\Sections\Extending\listeners\build.xml:3: Example build failure
at org.apache.tools.ant.taskdefs.Exit.execute(Exit.java:90)
at org.apache.tools.ant.Task.perform(Task.java:317)
at org.apache.tools.ant.Target.execute(Target.java:309)
at org.apache.tools.ant.Target.performTasks(Target.java:334)
at org.apache.tools.ant.Project.executeTarget(Project.java:1216)
at org.apache.tools.ant.Project.executeTargets(Project.java:1160)
at org.apache.tools.ant.Main.runBuild(Main.java:605)
at org.apache.tools.ant.Main.start(Main.java:195)
at org.apache.tools.ant.Main.main(Main.java:234)
上述输出由
TTCCAppender
生成,它会显示线程名称、事件优先级和事件消息,包括记录的任何异常的堆栈跟踪。当
build.log
文件达到 100KB 大小时,会生成备份文件(如
backup.log.1
、
backup.log.2
等),最多生成三个备份文件。
配置总结如下:
| 配置项 | 说明 |
| ---- | ---- |
|
log4j.rootCategory
| 根类别配置,指定日志级别和输出目标 |
|
log4j.category.org.apache.tools.ant.Project
| 特定类别的配置,可覆盖根类别配置 |
|
log4j.appender.file
| 文件输出配置,包括布局和文件大小限制 |
|
log4j.appender.mail
| 邮件输出配置,包括阈值、SMTP 主机等 |
1.2 编写自定义日志记录器
自定义
BuildLogger
是一个带有四个额外必需方法的
BuildListener
。这里我们开发了
MailLogger
类,用于发送完整构建日志的电子邮件。
MailLogger
类继承自
DefaultLogger
,通过重写
log
方法来捕获输出,并将其缓冲用于邮件正文。以下是
MailLogger
类的代码:
package org.apache.tools.ant.listener;
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.util.StringUtils;
import org.apache.tools.mail.MailMessage;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
public class MailLogger extends DefaultLogger {
private StringBuffer buffer = new StringBuffer();
public void buildFinished(BuildEvent event) {
super.buildFinished(event);
Project project = event.getProject();
Hashtable properties = project.getProperties();
Properties fileProperties = new Properties();
String filename = (String) properties.get("MailLogger.properties.file");
if (filename != null) {
InputStream is = null;
try {
is = new FileInputStream(filename);
fileProperties.load(is);
} catch (IOException ioe) {
// ignore because properties file is not required
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
}
}
}
}
for (Enumeration e = fileProperties.keys();
e.hasMoreElements();) {
String key = (String) e.nextElement();
String value = fileProperties.getProperty(key);
properties.put(key, project.replaceProperties(value));
}
boolean success = (event.getException() == null);
String prefix = success ? "success" : "failure";
try {
boolean notify = Project.toBoolean(getValue(properties,
prefix + ".notify", "on"));
if (!notify) {
return;
}
String mailhost = getValue(properties, "mailhost",
"localhost");
String from = getValue(properties, "from", null);
String toList = getValue(properties, prefix + ".to",
null);
String subject = getValue(properties,
prefix + ".subject",
(success) ? "Build Success" : "Build Failure");
sendMail(mailhost, from, toList, subject,
buffer.toString());
} catch (Exception e) {
System.out.println("MailLogger failed to send e-mail!");
e.printStackTrace(System.err);
}
}
protected void log(String message) {
buffer.append(message).append(StringUtils.LINE_SEP);
}
private String getValue(Hashtable properties, String name,
String defaultValue) throws Exception {
String propertyName = "MailLogger." + name;
String value = (String) properties.get(propertyName);
if (value == null) {
value = defaultValue;
}
if (value == null) {
throw new Exception("Missing required parameter: " +
propertyName);
}
return value;
}
private void sendMail (String mailhost, String from,
String toList, String subject,
String message) throws IOException {
MailMessage mailMessage = new MailMessage(mailhost);
mailMessage.from(from);
StringTokenizer t = new StringTokenizer(toList, ", ", false);
while (t.hasMoreTokens()) {
mailMessage.to(t.nextToken());
}
mailMessage.setSubject(subject);
PrintStream ps = mailMessage.getPrintStream();
ps.println(message);
mailMessage.sendAndClose();
}
}
buildFinished
方法首先调用
DefaultLogger
的
buildFinished
实现,以确保在发送邮件之前将最终输出生成到控制台或日志文件。创建一个健壮的邮件日志记录器需要几个可配置的参数,如发件人邮箱地址、收件人邮箱地址和主题。此外,还可以分别启用/禁用失败和成功消息,为失败和成功邮件设置不同的邮箱地址列表,以及根据构建的成功或失败设置不同的主题。
参数可以通过属性文件和 Ant 属性进行配置,Ant 属性优先于属性文件中指定的设置。我们可以使用特殊的项目属性
MailLogger.properties.file
来定义配置文件的位置,然后加载它并覆盖项目属性。构建的成功状态基于
BuildEvent
是否包含异常。
2. 自定义映射器开发
Ant 的一些任务支持
<mapper>
数据类型,用于将文件名映射到一个或多个相应的文件。大多数情况下,内置的映射器就足够了,但有时可能需要编写自定义映射器。
这里开发了
PackageNameMapper
类来解决单元测试结果与 Java 源文件时间戳比较的问题。Java 源文件基于包名的目录结构,而单元测试结果存储在扁平目录结构中,XML 文件名使用点分隔的包名。
以下是
PackageNameMapper
类的实现:
public class PackageNameMapper extends GlobPatternMapper {
protected String extractVariablePart(String name) {
String var = name.substring(prefixLength,
name.length() - postfixLength);
return var.replace(File.separatorChar, '.');
}
}
自定义映射器必须实现
org.apache.tools.ant.util.FileNameMapper
接口,这里通过继承
GlobPatternMapper
类来继承星号(
*
)模式匹配功能,并重写
extractVariablePart
方法,将文件分隔符替换为点。
FileNameMapper
接口的主要方法签名为:
String[] mapFileName(String sourceFileName)
在构建文件中使用自定义映射器时,只需为
<mapper>
指定类名,并可选择指定类路径、类路径引用或嵌套的
<classpath>
元素。例如:
<uptodate property="is.uptodate">
<srcfiles dir="${some.dir}"/>
<mapper classname="org.example.antbook.MyCustomMapper"
classpathref="mapper.classpath"
from="*Test.java" to="${test.data.dir}/TEST-*Test.xml"/>
</uptodate>
如果自定义映射器已经包含在 Ant 发行版中,可以直接通过名称引用:
<uptodate property="is.uptodate">
<srcfiles dir="${some.dir}"/>
<mapper type="package"
from="*Test.java" to="${test.data.dir}/TEST-*Test.xml"/>
</uptodate>
3. 创建自定义选择器
Ant 的优势之一是能够在高级别上表示特定领域的需求,如
filesets
表示基于基目录的文件集合。
Patternsets
可根据文件名模式包含或排除文件,内置选择器提供了更多选择功能。
这里我们开发了
ReadOnlySelector
类,用于选择只读文件。以下是
ReadOnlySelector
类的代码:
package org.example.antbook;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.types.selectors.BaseExtendSelector;
import java.io.File;
public class ReadOnlySelector extends BaseExtendSelector {
public boolean isSelected(File basedir, String filename, File file)
throws BuildException {
return (!file.canWrite());
}
}
编写自定义选择器主要是继承
BaseExtendSelector
并实现
isSelected
方法。自定义选择器还可以使用嵌套的
<param>
标签来接受参数。例如,可以将选择器编写为通用的文件属性选择器,允许使用
<param name="attribute" value="readonly"/>
(或
value="writable"
)。
综上所述,通过使用 Log4j 进行日志记录、开发自定义日志记录器、映射器和选择器,我们可以根据具体需求对 Ant 进行扩展,提高构建过程的灵活性和效率。
Ant 扩展深入探究
4. 使用自定义组件的操作步骤
4.1 使用 Log4j 日志记录的操作步骤
-
准备依赖
:确保
commons - logging.jar和log4j.jar在 Ant 的lib目录中。 -
配置
log4j.properties文件 :根据需求配置日志记录的级别、输出目标等,示例配置如下:
log4j.rootCategory=INFO,file
log4j.category.org.apache.tools.ant.Project=INFO,file,mail
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.layout=org.apache.log4j.TTCCLayout
log4j.appender.file.file=build.log
log4j.appender.file.maxBackupIndex=3
log4j.appender.file.maxFileSize=100KB
log4j.appender.mail=org.apache.log4j.net.SMTPAppender
log4j.appender.mail.layout=org.apache.log4j.HTMLLayout
log4j.appender.mail.Threshold=ERROR
log4j.appender.mail.SMTPHost=localhost
log4j.appender.mail.bufferSize=1
log4j.appender.mail.to=erik@example.org
log4j.appender.mail.from=erik@example.org
log4j.appender.mail.subject=Build Failure!
-
编写
build.xml文件 :编写 Ant 构建文件,示例如下:
<project default="fail">
<target name="fail">
<fail message="Example build failure"/>
</target>
</project>
-
运行 Ant 任务
:使用以下命令行运行 Ant 任务,并指定
CommonsLoggingListener:
ant -listener org.apache.tools.ant.listener.CommonsLoggingListener
-
查看结果
:查看控制台输出和
build.log文件,以及是否收到邮件通知。
4.2 使用
MailLogger
的操作步骤
-
准备配置文件
:创建
maillogger.properties文件,示例如下:
MailLogger.from = erik@example.org
MailLogger.failure.to = erik@example.org
MailLogger.mailhost = localhost
MailLogger.success.to = erik@example.org
MailLogger.success.subject = ${ant.project.name} - Build success
MailLogger.failure.subject = FAILURE - ${ant.project.name}
MailLogger.success.notify = off
-
编写
build.xml文件 :编写 Ant 构建文件,示例如下:
<project name="MailLogger example" default="test">
<target name="test">
<echo message="hello out there"/>
</target>
<target name="fail"><fail/></target>
</project>
-
运行 Ant 任务
:使用以下命令行运行 Ant 任务,并指定
MailLogger和配置文件:
ant -f buildmail.xml -logger org.apache.tools.ant.listener.MailLogger -DMailLogger.properties.file=maillogger.properties fail
- 查看结果 :查看控制台输出和是否收到邮件通知。
4.3 使用自定义映射器的操作步骤
-
编写自定义映射器类
:实现
FileNameMapper接口,示例如下:
public class PackageNameMapper extends GlobPatternMapper {
protected String extractVariablePart(String name) {
String var = name.substring(prefixLength,
name.length() - postfixLength);
return var.replace(File.separatorChar, '.');
}
}
-
在
build.xml文件中使用 :在构建文件中使用自定义映射器,示例如下:
<uptodate property="is.uptodate">
<srcfiles dir="${some.dir}"/>
<mapper classname="org.example.antbook.MyCustomMapper"
classpathref="mapper.classpath"
from="*Test.java" to="${test.data.dir}/TEST-*Test.xml"/>
</uptodate>
- 运行 Ant 任务 :运行 Ant 任务,查看映射结果。
4.4 使用自定义选择器的操作步骤
-
编写自定义选择器类
:继承
BaseExtendSelector并实现isSelected方法,示例如下:
package org.example.antbook;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.types.selectors.BaseExtendSelector;
import java.io.File;
public class ReadOnlySelector extends BaseExtendSelector {
public boolean isSelected(File basedir, String filename, File file)
throws BuildException {
return (!file.canWrite());
}
}
-
在
build.xml文件中使用 :在构建文件中使用自定义选择器,示例如下:
<fileset dir=".">
<customselector classname="org.example.antbook.ReadOnlySelector"/>
</fileset>
- 运行 Ant 任务 :运行 Ant 任务,查看选择结果。
5. 总结与展望
5.1 总结
通过以上内容,我们深入了解了 Ant 的扩展功能,包括使用 Log4j 进行日志记录、编写自定义日志记录器、开发自定义映射器和选择器。这些扩展功能可以帮助我们更好地控制构建过程,提高构建效率和灵活性。
| 扩展类型 | 实现方式 | 优点 |
|---|---|---|
| 日志记录 |
使用
CommonsLoggingListener
和
MailLogger
| 灵活配置日志输出,可发送邮件通知 |
| 映射器 |
实现
FileNameMapper
接口
| 解决特定的文件名映射问题 |
| 选择器 |
继承
BaseExtendSelector
并实现
isSelected
方法
| 实现自定义的文件选择逻辑 |
5.2 展望
未来,我们可以进一步探索 Ant 的扩展功能,例如开发更多的自定义任务、监听器等。同时,结合其他工具和技术,如持续集成工具、自动化测试框架等,构建更加高效和稳定的软件开发流程。
以下是一个简单的 Ant 扩展开发流程图:
graph TD;
A[需求分析] --> B[选择扩展类型];
B --> C[编写代码];
C --> D[测试与调试];
D --> E[集成到项目中];
E --> F[持续优化];
通过不断地学习和实践,我们可以更好地利用 Ant 的扩展功能,为软件开发带来更多的便利和价值。
超级会员免费看
90

被折叠的 条评论
为什么被折叠?



