57、Ant扩展:日志记录、自定义映射器与选择器的深入探索

Ant扩展:日志记录、自定义映射器与选择器的深入探索

在软件开发过程中,Ant作为一个强大的构建工具,为我们提供了许多便利。然而,为了满足特定的需求,我们常常需要对Ant进行进一步的扩展。本文将详细介绍如何扩展Ant的日志记录功能、开发自定义映射器和选择器。

1. 日志记录扩展

Ant提供了多种日志记录方式,其中Log4j和自定义日志记录器是常用的扩展方式。

1.1 使用Log4j进行日志记录

Ant包含了 Log4jListener CommonsLoggingListener ,它们都可以将日志输出到Log4j。 CommonsLoggingListener 在需要灵活选择日志记录API的环境中更为推荐,但需要额外依赖 commons-logging.jar

日志记录的配置通过 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!                          

这个配置文件的作用如下:
| 配置项 | 作用 |
| — | — |
| log4j.rootCategory | 设置根日志级别和输出目标 |
| log4j.category.org.apache.tools.ant.Project | 设置特定类别的日志级别和输出目标 |
| log4j.appender.file | 配置文件输出的日志追加器 |
| log4j.appender.mail | 配置邮件输出的日志追加器 |

以下是使用 CommonsLoggingListener 的示例:

<project default="fail">
  <target name="fail">
    <fail message="Example build failure"/>
  </target>
</project>

命令行运行:

ant -listener org.apache.tools.ant.listener.CommonsLoggingListener

运行过程中, build.log 文件会记录详细的日志信息,当构建失败时,还会发送一封包含错误信息的HTML格式邮件。

1.2 开发自定义日志记录器

自定义日志记录器可以满足特定的日志记录需求,例如发送完整构建日志的邮件。以下是 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();
    }
}

这个自定义日志记录器的工作流程如下:
1. 在构建完成时,首先调用父类的 buildFinished 方法。
2. 加载配置文件中的属性,并与项目属性合并。
3. 根据构建结果判断是否需要发送邮件。
4. 如果需要发送邮件,获取邮件相关的配置信息,如邮件服务器、发件人、收件人、主题等。
5. 调用 sendMail 方法发送邮件。

使用 MailLogger 时,需要提供必要的配置参数,推荐使用外部属性文件:

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

命令行运行:

ant -f buildmail.xml -logger org.apache.tools.ant.listener.MailLogger -DMailLogger.properties.file=maillogger.properties fail
2. 开发自定义映射器

Ant的一些任务支持 <mapper> 数据类型,用于将文件名映射到一个或多个对应的文件。在大多数情况下,内置的映射器已经足够,但有时需要开发自定义映射器。

例如,为了加速包含单元测试的构建,我们开发了 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 以利用其通配符匹配功能。

在构建文件中使用自定义映射器的示例:

<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>
3. 开发自定义选择器

Ant的文件集和模式集提供了强大的文件选择功能,但有时需要开发自定义选择器来满足特定需求。例如,我们开发了 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 方法。

通过以上扩展,我们可以根据具体需求定制Ant的日志记录、文件映射和文件选择功能,提高构建过程的效率和灵活性。

Ant扩展:日志记录、自定义映射器与选择器的深入探索

4. 自定义映射器与选择器的应用场景分析

在实际开发中,自定义映射器和选择器能够解决许多特定的问题,下面我们详细分析它们的应用场景。

4.1 自定义映射器的应用场景

自定义映射器在处理文件路径和命名规则不一致的情况时非常有用。例如,在软件开发中,Java源文件通常按照包名的目录结构存放,而单元测试结果可能以扁平的目录结构存储,文件名中包含点分的包名。使用 PackageNameMapper 可以将Java源文件的路径映射到对应的单元测试结果文件。

以下是一个更详细的流程图,展示了使用自定义映射器加速单元测试的流程:

graph TD;
    A[开始构建] --> B[检查单元测试结果与源文件时间戳];
    B --> C{是否需要运行测试};
    C -- 是 --> D[运行单元测试];
    C -- 否 --> E[跳过测试];
    D --> F[记录测试结果];
    E --> F;
    F --> G[完成构建];
    B --> H[使用自定义映射器进行路径映射];
    H --> C;

这个流程图清晰地展示了自定义映射器在整个构建过程中的作用,通过比较时间戳和路径映射,避免了不必要的单元测试,从而提高了构建速度。

4.2 自定义选择器的应用场景

自定义选择器可以根据特定的文件属性进行文件筛选。例如,在项目中,我们可能只需要处理只读文件,或者只处理特定日期之后修改的文件。 ReadOnlySelector 就是一个很好的例子,它可以筛选出只读文件。

以下是一个使用自定义选择器的操作步骤:
1. 在项目中创建 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());
    }
}
  1. 在构建文件中使用自定义选择器:
<fileset dir="your_directory">
    <custom selectorclass="org.example.antbook.ReadOnlySelector"/>
</fileset>
  1. 执行构建任务,Ant会根据自定义选择器筛选出只读文件进行后续处理。
5. 扩展Ant的注意事项

在扩展Ant时,需要注意以下几点:
| 注意事项 | 说明 |
| — | — |
| 依赖管理 | 对于使用的第三方库,如 commons-logging.jar log4j.jar ,要确保正确配置类路径,避免出现类找不到的错误。 |
| 配置文件 | 对于日志记录和自定义日志记录器,配置文件的路径和格式要正确,否则可能导致配置不生效。 |
| 兼容性 | 自定义的映射器和选择器要考虑与Ant的版本兼容性,避免在不同版本的Ant中出现问题。 |
| 性能影响 | 在开发自定义组件时,要考虑其对构建性能的影响,避免引入不必要的开销。 |

6. 总结

通过对Ant的日志记录、自定义映射器和选择器的扩展,我们可以根据项目的具体需求定制构建过程,提高构建的效率和灵活性。在日志记录方面,我们可以使用Log4j进行灵活的日志配置,也可以开发自定义日志记录器满足特定的日志需求,如发送完整构建日志的邮件。在文件处理方面,自定义映射器可以解决文件路径和命名规则不一致的问题,自定义选择器可以根据特定的文件属性进行文件筛选。

在实际应用中,我们要根据项目的具体情况选择合适的扩展方式,并注意扩展过程中的注意事项,确保扩展的稳定性和性能。通过不断地探索和实践,我们可以充分发挥Ant的强大功能,为软件开发提供更好的支持。

内容概要:本文设计了一种基于PLC的全自动洗衣机控制系统内容概要:本文设计了一种,采用三菱FX基于PLC的全自动洗衣机控制系统,采用3U-32MT型PLC作为三菱FX3U核心控制器,替代传统继-32MT电器控制方式,提升了型PLC作为系统的稳定性自动化核心控制器,替代水平。系统具备传统继电器控制方式高/低水,实现洗衣机工作位选择、柔和过程的自动化控制/标准洗衣模式切换。系统具备高、暂停加衣、低水位选择、手动脱水及和柔和、标准两种蜂鸣提示等功能洗衣模式,支持,通过GX Works2软件编写梯形图程序,实现进洗衣过程中暂停添加水、洗涤、排水衣物,并增加了手动脱水功能和、脱水等工序蜂鸣器提示的自动循环控制功能,提升了使用的,并引入MCGS组便捷性灵活性态软件实现人机交互界面监控。控制系统通过GX。硬件设计包括 Works2软件进行主电路、PLC接梯形图编程线关键元,完成了启动、进水器件选型,软件、正反转洗涤部分完成I/O分配、排水、脱、逻辑流程规划水等工序的逻辑及各功能模块梯设计,并实现了大形图编程。循环小循环的嵌; 适合人群:自动化套控制流程。此外、电气工程及相关,还利用MCGS组态软件构建专业本科学生,具备PL了人机交互C基础知识和梯界面,实现对洗衣机形图编程能力的运行状态的监控操作。整体设计涵盖了初级工程技术人员。硬件选型、; 使用场景及目标:I/O分配、电路接线、程序逻辑设计及组①掌握PLC在态监控等多个方面家电自动化控制中的应用方法;②学习,体现了PLC在工业自动化控制中的高效全自动洗衣机控制系统的性可靠性。;软硬件设计流程 适合人群:电气;③实践工程、自动化及相关MCGS组态软件PLC的专业的本科生、初级通信联调工程技术人员以及从事;④完成PLC控制系统开发毕业设计或工业的学习者;具备控制类项目开发参考一定PLC基础知识。; 阅读和梯形图建议:建议结合三菱编程能力的人员GX Works2仿真更为适宜。; 使用场景及目标:①应用于环境MCGS组态平台进行程序高校毕业设计或调试运行验证课程项目,帮助学生掌握PLC控制系统的设计,重点关注I/O分配逻辑、梯形图实现方法;②为工业自动化领域互锁机制及循环控制结构的设计中类似家电控制系统的开发提供参考方案;③思路,深入理解PL通过实际案例理解C在实际工程项目PLC在电机中的应用全过程。控制、时间循环、互锁保护、手动干预等方面的应用逻辑。; 阅读建议:建议结合三菱GX Works2编程软件和MCGS组态软件同步实践,重点理解梯形图程序中各环节的时序逻辑互锁机制,关注I/O分配硬件接线的对应关系,并尝试在仿真环境中调试程序以加深对全自动洗衣机控制流程的理解。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值