zoukankan      html  css  js  c++  java
  • 13 Maven 编写插件

    Maven 编写插件

    Maven 的任何行为都是由插件完成的,包括项目的清理、绵编译、测试以及打包等操作都有其对应的 Maven 插件。每个插件拥有一个或者多个目标,用户可以直接从命令行运行这些插件目标,或者选择将目标绑定到 Maven 的生命周期。

    大量的 Maven 插件可以从 Aapche 获得,当你发现自己有特殊需要的时候,首先应该搜索一下看是否已经有现成的插件可供使用。在一些非常情况下(几率低于1%),你有非常特殊的需求,并且无法找到现成的插件可供使用,那么就只能自己编写 Maven 插件了。编写 Maven 插件并不是特别复杂,本章将详细介绍如何一步步编写能够满足自己需要的 Maven 插件。

    1. 编写 Maven 插件的一般步骤

    为了能让读者对编写 Maven 插件的方法和过程有一个总体的认识,下面先简要介绍下编写 Maven 插件的主要步骤。

    1. 创建一个 maven-plugin 项目:插件本身也是 Maven 项目,特殊的地方在于它的 packagng 必须是 maven-plugin。

    2. 为插件编写目标:每个插件都必须包含一个或者多个目标, Maven 称之为 Mojo(与 POJO 对应,后者指 Plain Old Java Object,这里指 Maven Old Java Object)。编写插件的时候必须提供一个或者多个继承自 AbstractMojo的类。

    3. 为目标提供配置点:大部分 Maven 插件及其日标都是可配置的,因此在编写 Mojo 的时候需要注意提供可配置的参数

    4. 编写代码实现目标行为:根据实际的需要实现 Mojo

    5. 错误处理及日志:当 Mojo 发生异常时,根据情况控制 Maven 的运行状态。在代码中编写必要的日志以便为用户提供足够的信息。

    6. 测试插件:编写自动化的测试代码测试行为,然后再实际运行插件以验证其行为。

    2. 案例:编写一个用于代码行统计的 Maven 插件

    为了便于大家实践,下面将详细演示如何实际编写一个简单的用于代码行统计的 Maven 插件。使用该插件,用户可以了解到到 Maven 项目中各个源代码目录下文件的数量,以及它们加起来共有多少代码行。

    要创建一个 Maven 插件项目,首先使用 maven-archetype-plugin 命令:

    mvn archetype:generate
    

    然后选择:

    maven-archetype-plugin (An archetype which contains a sample Maven plugin.)
    

    输入 Maven 坐标等信息之后,一个 Maven 插件项目就创建好了。打开项目的 pom,xml 可以看到如代码:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.github.binarylei.maven</groupId>
        <artifactId>maven-count-plugin</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>maven-plugin</packaging>
    
        <properties>
            <maven.version>3.0</maven.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.apache.maven</groupId>
                <artifactId>maven-plugin-api</artifactId>
                <version>${maven.version}</version>
            </dependency>
        </dependencies>
    </project>
    

    Maven 插件项目的 POM 有两个特殊的地方:

    1. 它的 packaging 必须为 maven-plugin,这种特殊的打包类型能控制 Maven 为其在生命周期阶段绑定插件处理相关的目标,例如在 compile 阶段, Maven 需要为插件项目构建个特殊插件描述符文件。

    2. 从上述代码中可以看到一个 artifactId 为 maven-plugin-api 的依赖,该依赖中包含了插件开发所必需的类,例如稍后会看到的 AbstractMojo。

    插件项目创建好之后,下一步是为插件编写目标。创建一个 CountMojo 类:

    package com.github.binarylei.maven;
    
    import org.apache.maven.model.Resource;
    import org.apache.maven.plugin.AbstractMojo;
    import org.apache.maven.plugin.MojoExecutionException;
    import org.apache.maven.plugin.MojoFailureException;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.List;
    
    /**
     * 统计代码的 Maven 插件
     * @author: leigang
     * @version: 2018-03-25
     * @goal count
     */
    public class CountMojo extends AbstractMojo {
        private static final String[] INCLUDES_DEFALUT = {"java", "xml", "properties"};
    
        /**
         * @parameter expression="${project.basedir}"
         * @required
         * @readonly
         */
        private File basedir;
    
        /**
         * @parameter expression="${project.build.sourceDirectory}"
         * @required
         * @readonly
         */
        private File sourceDirectory;
    
        /**
         * @parameter expression="${project.build.testSourceDirectory}"
         * @required
         * @readonly
         */
        private File testSourceDirectory;
    
        /**
         * @parameter expression="${project.build.resources}"
         * @required
         * @readonly
         */
        private List<Resource> resources;
    
        /**
         * @parameter expression="${project.build.testResources}"
         * @required
         * @readonly
         */
        private List<Resource> testResources;
    
        /**
         * The file types which will be included for counting
         * @parameter
         */
        private String[] includes;
    
        public void execute() throws MojoExecutionException, MojoFailureException {
            System.out.println("my maven plugin");
    
            if (includes == null || includes.length == 0) {
                includes = INCLUDES_DEFALUT;
            }
    
            try {
                countDir(sourceDirectory);
                countDir(testSourceDirectory);
                for (Resource resource : resources) {
                    countDir(new File(resource.getDirectory()));
                }
                for (Resource resource : testResources) {
                    countDir(new File(resource.getDirectory()));
                }
            } catch (IOException e){
                throw new MojoExecutionException("Unable to count lines of code.", e);
            }
        }
    }
    
    1. 每个插件目标类,或者说 Mojo,都必须继承 AbstractMojo 并实现 execute() 方法,只有这样 Maven 才能识别该插件目标,并执行 execute() 方法中的行为。

    2. 由于历史原因,上述 CountMojo 类使用了 Java1.4 风格的标注(将标注写在注释中),这里要关注的是 @goal,任何一个 Mojo 都必须使用该标注写明自己的目标名称,有了目标定义之后,我们才能在项目中配置该插件目标,或者在命令行调用之。例如

      mvn com.github.binarylei.maven:maven-plugin:1.0.0-SNAPSHOT:count

    创建一个 Mojo 所必要的工作就是这三项:继承 AbstractMojo、实现 execute() 方法、提供 @goal 标注。

    下一步是为插件提供配置点。我们希望该插件默认统计所有 Java、XML,以及 properties 文件,但是允许用户配置包含哪些类型的文件。includes 字段就是用来为用户提供该配置点的,它的类型为 String 数组,并且使用了 @parameter 参数表示用户可以在使用该插件的时候在 POM 中配置该字段。

    <plugin>
        <groupId>org.github.binarylei.maven</groupId>
        <artifactId>maven-count-plugin</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <configuration>
            <includes>
                <include>java</include>
                <include>sql</include>
            </includes>
        </configuration>
    </plugin>
    

    上述配置了 CountMojo 统计 Java 和 SQL 文件,而不是默认的 Java、XML 和 Properties

    CountMojo 类中还包含了 basedir、 sourcedirectory、 testsource Directory 等字段,它们都使用了 @parameter 标注,但同时关键字 expression 表示从系统属性读取这几个字段的值。${project.basedir}, ${project.build.sourceDirectory} , ${project.build.testSourceDirectory} 等表达式读者应该已经熟悉,它们分别表示了项目的基础目录、主代码目录和测试代码目录。@readonly 标注表示不允许用户对其进行配置,因为对于一个项目来说,这几个目录位置都是固定的。

    了解这些简单的配置点之后,下一步就该实现插件的具体行为了。CountMojo 类的 execute() 方法中大家能看到这样一些信息:如果用户没有配置 includes 则就是用默认的统计包含配置,然后再分别统计项目主代码目录、测试代码目录、主资源目录,以及测试资源目录。这里涉及一个 countDir() 方法,代码如下:

    private void countDir(File dir) throws IOException {
        if (! dir.exists()) {
            return;
        }
    
        List<File> collected = new ArrayList<File>();
        collectFiles(collected, dir);
    
        int lines = 0;
        for (File souceFile : collected) {
            lines += countLine(souceFile);
        }
    
        String path = dir.getAbsolutePath().substring(basedir.getAbsolutePath().length());
        getLog().info(path + ":" + lines + " lines of code in " + collected.size() + " files");
    }
    
    private void collectFiles(List<File> collected, File file) {
        if (file.isFile()) {
            for (String include : includes) {
                if (file.getName().endsWith("." + include)) {
                    collected.add(file);
                    break;
                }
            }
        } else {
            for (File sub : file.listFiles()) {
                collectFiles(collected, sub);
            }
        }
    }
    
    private int countLine(File file) throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader(file));
        int line = 0;
    
        try {
            while (reader.ready()) {
                reader.readLine();
                line++;
            }
        } finally {
            reader.close();
        }
        return line;
    }
    

    使用 mvn clean install 命令将该插件项目构建并安装到本地仓库后,就能使用它统计 Maven 项目的代码了。如下所示:

    mvn com.github.binarylei.maven:maven-plugin:1.0.0-SNAPSHOT:count
    

    输出结果如下:

    [INFO] srcmainjava:132 lines of code in 1 files
    [INFO] src	estjava:0 lines of code in 0 files
    [INFO] srcmain
    esources:0 lines of code in 0 files
    

    如果嫌命令行太长太复杂,可以将该插侏的 groupId 添加到 settings.xml 中。如下所示:

    <settings>
        <pluginGroups>
            <pluginGroup>com.github.binarylei.maven</pluginGroup>
        </pluginGroups>
    </settings>
    

    现在 Maven 命令行就可以简化成

    mvn loc: count
    

    3. Mojo 标注

    每个 Mojo 都必须使用 @Goal 标注来注明其目标名称,否则 Maven 将无法识别该目标。Mojo 的标注不仅限于 @Coal,以下是一些可以用来控制 Mjo 行为的标注。

    1. @goal

      这是唯一必须声明的标注,当用户使用命令行调用插件,或者在 POM 中配置插件的时候,都需要使用该日标名称。

    2. @phase

      默认将该目标绑定至 Default 生命周期的某个阶段,这样在配置使用该插件目标的时候就不需要声明 phase。例如, maven-surefire-plugin的 test 目标就带有 @phase test标注。

    3. @requiresDependencyResolution scope

      表示在运行该 Mojo 之前必须解析所有指定范围的依赖。例如, maven-surefire-plugin 的 test 目标带有 @requiresDependencyResolution test 标注,表示在执行测试之前,所有测试范围的依赖必须得到解析。这里可用的依赖范围有 compile、test和 runtime,默认值为 runtime

    4. @requiresProject true/false

      表示该目标是否必须在一个 Maven 项日中运行,默认为 true。大部分插件目标都需要依赖一个项目才能执行,但有一些例外。例如 maven-help-plugin 的 system 目标,它用来显示系统属性和环境变量信息,不需要实际项目,因此使用了 @requiresProject false 标注。另外,maven-archetype-plugin 的 generate 目标也是一个很好的例子。

    5. @requiresDirectInvocation true/false

      当值为 true 的时候,该目标就只能通过命令行直接调用,如果试图在 POM 中将其绑定到生命周期阶段, Maven 就会报错,默认值为 false。如果你希望编写的插件只能在命令行独立运行,就应当使用该标注。

    6. @requiresonline true/false

      表示是否要求 Maven 必须是在线状态,默认值是 false

    7. @requiresReport true/false

      表示是否要求项目报告已经生成,默认值是 false

    8. @aggregator

      当 Mojo 在多模块项目上运行时,使用该标注表示该目标只会在顶层模块运行。例如 maven-javadoc-plugin 的使用了 @aggregator 标注,它不会为多模块项目的每个模块生成 Javadoc,而是在顶层项目生成一个已经聚合的 Javadoc 文档。

    9. @execute goal=""

      在运行该目标之前先让 Maven 运行另外一个目标,如果是本插件的目标,则直接使用目标名称,否则使用 "prefix:goal" 的形式,即注明目标前缀。例如, maven-pmd-plugin 是一个使用 PMD 来分析项目源码的工具,它包含 pmd 和 check 等目标,其中 pmd 用来生成报告,而 check 用来验证报告。由于 check 是依赖于 pmd 生成的内容的,因此可以看到它使用了标注 @execute goal="pmd"。

    10. @execute phase=""

      在运行该目标之前让 Maven 先运行一个并行的生命周期,到指定的阶段为止。例如 maven-dependency-plugin 的 analyze 使用了标注 @execute phase="test-compile",因此当用户在命令行执行 dependency:analyze 的时候, Maven 会首先执行 default 生命周期所有至 test-compile 的阶段。

    11. @execute lifecycle="" phase=""

      在运行该日标之前让 Maven 先运行一个自定义的生命周期,到指定的阶段为止。例如 maven-surefire-report-plugin 这个用来生成测试报告的插件,它有一个 report 目标,标注了 @execute phase="test" lifecycle=" surefire",表示运行这个自定义的 surefire 声明周期至 test 阶段。自定义生命周期的配置文件位于 sre/main/resources/META-INF/maven/lifecycle.xml。代码如下:

    <lifecyles>
        <lifecyle>
            <id>surefire</id>
            <phases>
                <phase>
                    <id>test</id>
                    <configuration>
                        <testFailureIgnore>true</testFailureIgnore>
                    </configuration>
                </phase>
            </phases>
        </lifecyle>
    </lifecyles>
    

    4. Mojo 参数

    我们可以使用 @parameter 将 Mojo 的某个字段标注为可配置的参数,即 Mojo 参数。事实上几乎每个 Mojo 都有一个或者多个 Mojo 参数,通过配置这些参数, Maven 用户可以自定义插件的行为。

    Maven 支持种类多样的 Mojo 参数,包括单值的 boolean、int、 float、 String、Date、File 和 URL,多值的数组、 Collection、Map、 Properties 等。

    1. boolean(包括 boolean 和 Boolean)

      /**
       * @parameter
       */
      private boolean sampleBoolean;
      

      对应的配置如下:

      <sampleboolean >true</sampleboolean>
      
    2. int(包括 Integer、long、Iong、 short、 Short、byte、Byte)

      /**
       * @parameter
       */
      private int sampleint;
      

      对应的配置如下:

      <sampleint >8</sampleint>
      
    3. float(包括 Float、 double、 Double)

      /**
       * @parameter
       */
      private float sampleFloat;
      

      对应的配置如下:

      <sampleFloat >8.0</sampleFloat>
      
    4. String(包括 StringBuffer、 char、 Character)

      /**
       * @parameter
       */
      private String sampleString;
      

      对应的配置如下:

      <sampleString >Hello World</sampleString>
      
    5. Date(格式为 yyyy-MM-dd HH:mm:ss.S a 或 yyyy-MM-dd HH:mm:ssa)

      /**
       * @parameter
       */
      private Date sampleDate;
      

      对应的配置如下:

      <sampleDate >2018-03-25 13:10:00</sampleDate>
      
    6. File

      /**
       * @parameter
       */
      private File sampleFile;
      

      对应的配置如下:

      <sampleFile >c:/tmp</sampleFile>
      
    7. URL

      /**
       * @parameter
       */
      private URL sampleURL;
      

      对应的配置如下:

      <sampleURL >http://www.baidu.com</sampleURL>
      
    8. 数组

      /**
       * @parameter
       */
      private String[] includes;
      

      对应的配置如下:

      <includes>
         <include>java</include>
         <include>sql</include>
      </includes>
      
    9. Collection(任何实现 Collection 接口的类,如 ArrayList 和 HashSet)

      /**
       * @parameter
       */
      private List includes;
      

      对应的配置如下:

      <includes>
         <include>java</include>
      </includes>
      
    10. Map

      /**
       * @parameter
       */
      private Map sampleMap;
      

      对应的配置如下:

      <sampleMap>
         <key1>java</key1>
      </sampleMap>    
      
    11. Properties

      /**
       * @parameter
       */
      private Properties sampleProperties;
      

      对应的配置如下:

      <sampleProperties>
          <property>
              <name>key_1</name>
              <value>value_1</value>
          </property>
          <property>
              <name>key_2</name>
              <value>value_2</value>
          </property>
      </sampleProperties>    
      

    一个简单的 @parameter 标注就能让用户配置各种类型的 Mojo 字段,不过在此基础上用户还能为 @parameter 标注提供一些额外的属性,进一步自定义 Moio 参数。

    1. @parameter alias=""

      使用 alias,用户就可以为 Mojo 参数使用别名,当 Mojo 字段名称太长或者可读性不强时,这个别名就非常有用。例如

      /**
       * @parameter alias="uid"
       */
      private String uniqueIdentity;
      

      对应的配置如下:

      <uid>maven</uid>    
      
    2. @parameter expression="${aSystemProperty}"

      使用系统属性表达式对Mj参数进行赋值,这是非常有用的特性。配置了@parameter 的 expression 之后,用户可以在命令行配置该 Mojo 参数。例如, maven-surefire-plugin 的 test 目标有如下源码:

      /**
       * @parameter expression="${maven.test.skip}"
       */
      private boolean skip;
      

      用户可以在 POM 中配置 skip 参数,同时也可以直接在命令行使用 -Dmaven.test.skip=true 来跳过测试。如果 Mojo 参数没有提供 expression,那就意味着该参数无法在命令行直接配置。还需要注意的是,Mojo 参数的名称和 expression 名称不一定相同。

    3. @parameter default-value="aValue/${anExpression}"

      如果用户没有配置该 Mojo 参数,就为其提供一个默认值。该值可以是一个简单字面量如 "true"、"hello" 或者 "1.5",也可以是一个表达式,以方便使用 POM 的某个元素。

      /**
       * @parameter defaultValue="true"
       */
      private boolean sampleBoolean;
      

    除了 @parameter 标注外,还看到可以为 Mojo 参数使用 @readonly 和 @required 标注。

    1. @readonly

      表示该 Mojo 参数是只读的,如果使用了该标注,用户就无法对其进行配置。通常在应用 POM 元素内容的时候,我们不希望用户干涉。

    2. @required

      表示该 Mojo 参数是必须的,如果使用了该标注,但是用户没有配置该 Mojo 参数且其没有默认值, Maven 就会报错。

    5. 错误处理和日志

    如果大家看一下 Maven 的源码,会发现 AbstractMojo 实现了 Mojo 接口, execute() 方法正是在这个接口中定义的。具体代码如下:

    public interface Mojo {
        String ROLE = Mojo.class.getName();
    
        void execute() throws MojoExecutionException, MojoFailureException;
    
        void setLog(Log var1);
    
        Log getLog();
    }
    

    这个方法可以抛出两种异常,分别是 MojoExecutionException 和 MojoFailureException。如果 Maven 执行插件目标的时候遇到 MojoFailureException,就会显示 “BUILD FAIL,URF” 的错误信息,这种异常表示 Mojo 在运行时发现了预期的错误。例如 maven-surefire-plugin 运行后若发现有失败的测试就会抛出该异常。

    如果 Maven 执行插件目标的时候遇到 MojoExecutionException,就会显示 “BUILD ERROR” 的错误信息。这种异常表示 Maven 在运行时发现了未预期的错误。

    上述两种异常能够在 Mojo 执行出错的时候提供一定的信息,但这往往是不够的,用户在编写插件的时候还应该提供足够的日志信息, AbstractMojo 提供了一个 getlog() 方法,用户可以使用该方法获得一个 Log 对象。该对象支持四种级别的日志方法,它们从低到高分别为:

    1. debug 调试级别的日志。 Maven 默认不会输出该级别的日志,不过用户可以在执行 mvn 命令的时候使用 -X 参数开启调试日志,该级别的日志是用来帮助程序员了解插件具体运行状态的,因此应该尽量详细。需要注意的是,不要指望你的用户会主动去看该级别的日志。

    2. info 消息级别的日志。 Maven 默认会输出该级别的日志,该级别的日志应该足够简洁,帮助用户了解插件重要的运行状态。例如, maven-compiler-plugin 会使用该级别的日志告诉用户源代码编译的目标目录。

    3. warn 警告级别的日志。当插件运行的时候遇到了一些问题或错误,不过这类问题不会导致运行失败的时候,就应该使用该级别的日志警告用户尽快修复。

    4. error 错误级别的日志。当插件运行的时候遇到了一些问题或错误,并且这类问题导致 Mojo 无法继续运行,就应该使用该级别的日志提供详细的错误信息。

    上述每个级别的日志都提供了三个方法。以 debug 为例,它们分别为:

    void debug (Charsequence content)
    void debug (Charsequence content, Throwable error)
    void debug (Throwable error)
    

    用户在编写插件的时候,应该根据实际情况选择适应的方法。基本的原则是,如果有异常出现,就应该尽量使用适宜的日志方法将异常堆栈记录下来,方便将来的问题分析。

    如果使用过 Log4j 之类的日志框架,就应该不会对 Maven 日志支持感到陌生,日志不是一个 Maven 插件的核心代码,但是为了方便使用和调试,完整的插件应该具备足够丰富的日志代码。

    6. 測试 Maven 插件

    Maven 社区有一个用来帮助插件集成测试的插件,它就是 maven-invoker-plugin。该插件能够用来在一组项目上执行 Maven,并检查每个项目的构建是否成功,最后,它还可以执行 Beanshell 或者 Groovy 脚本来验证项目构建的输出。Beanshell 和 Groovy 都是基于 JVM 平台的脚本语言,读者可以访问 http://www.beanshel.org/http://groovy.codehaus.org/ 以了解更多的信息。本章下面的内容会用到少许的 Groovy 代码,不过这些代码十分简单,很容易理解。

    为了验证这一行为,先配置 maven-count-plugin 的 POM 使用 maven-invoker-plugin,代码如下所示:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-invoker-plugin</artifactId>
        <version>3.0.0</version>
        <configuration>
            <projectsDirectory>src/it</projectsDirectory>
            <goals>
                <goal>install</goal>
            </goals>
            <postBuildHookScript>validate.groovy</postBuildHookScript>
        </configuration>
        <executions>
            <execution>
                <id>integration-test</id>
                <goals>
                    <goal>install</goal>
                    <goal>run</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
    

    maven-invoker-plugin 有三项配置:

    1. 首先 projectsDirectory 用来配置测试项目的目录,也就是说在 src/it 目录下存放要测试的 Maven项目源码;
    2. 其次 goals 表示在测试项目上要运行的 Maven 目标,这里的配置表示 maven-invoker-plugin 会在 src/it 目录下的各个 Maven 项目中运行 mvn install 命令;
    3. 最后 postBuildHookScript 表示在测试完成后要运行的验证脚本,这里是一个 groovy 文件。

    maven-invoker-plugin 的两个目标 install 和 run 被绑定到了 integration-test 生命周期阶段。这里的 install 目标用来将当前的插件构建并安装到仓库中供测试项目使用,run 目标则会执行定义好的 mvn 命令并运行验证脚本。当然仅仅该配置还不够,src/it 目录下必须有一个或者多个供测试的 Maven 项目。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.github.binarylei.maven</groupId>
        <artifactId>app</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>3.8.1</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>com.github.binarylei.maven</groupId>
                    <artifactId>maven-count-plugin</artifactId>
                    <version>1.0.0-SNAPSHOT</version>
                    <executions>
                        <execution>
                            <phase>verify</phase>
                            <goals>
                                <goal>count</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>
    

    测试项目准备好了,现在要准备的是与该项目对应的验证脚本文件,即 validate.groovy ,它位于 src/it/app 目录下(即上述测试项目的根目录)。

    def file = new  File(basedir, 'build.log')
    
    def countMain = false
    def countTest = false
    
    file.eachLine {
        if (it =~ /java:11 lines of code in 1 files/)
            countMain = true
        if (it =~ /java:0 lines of code in 0 files/)
            countTest = true
    }
    
    if (!countMain)
        throw new RuntimeException("incorrect src/main/java count info");
    
    if (!countTest)
        throw new RuntimeException("incorrect src/test/java count info");
    

    这段 Groovy 代码做的事情很简单。它首先读取 app 项目目录下的 build.log 文件,当 mavel-invoker-plugin 构建測试项目的时候,会把 mvn 输出保存到项目下的 build.log 文件中。因此,可以解析该日志文件来验证 maven-count-plugin 是否输出了正确的代码行信息。

    上述 Groovy代码首先假设没有找到正确的主代码统计信息和测试代码统计信息,然后它逐行遍历日志文件,紧接着使用正则表达式检査寻找要检查的内容(两个斜杠//中间的内容是正则表达式,而 =~ 表示寻找该正则表达式匹配的内容),如果找到期望的输出,就将 countMain 和 countTest 置为 true。最后,如果这两个变量的值有 false,就就抛出对应的异常信息。

    Maven 会首先在测试项目 app 上运行 mvn install 命令,如果运行成功,则再执行 validate.groovy 脚本。只有脚本运行通过且没有异常,集成测试才算成功。现在在 maven-count-plugin 下运行 mvn clean install,就能看到如下的输出:

    [INFO] --- maven-invoker-plugin:3.0.0:install (integration-test) @ maven-count-plugin ---
    [INFO] Installing C:UserslenDesktopjavamaven-pluginpom.xml to D:Program FilesMavenLocalRepositorycomgithubinaryleimavenmaven-count-plugin1.0.0-SNAPSHOTmaven-count-plugin-1.0.0-SNAPSHOT.pom
    [INFO] Installing C:UserslenDesktopjavamaven-plugin	argetmaven-count-plugin-1.0.0-SNAPSHOT.jar to D:Program FilesMavenLocalRepositorycomgithubinaryleimavenmaven-count-plugin1.0.0-SNAPSHOTmaven-count-plugin-1.0.0-SNAPSHOT.jar
    [INFO] 
    [INFO] --- maven-invoker-plugin:3.0.0:run (integration-test) @ maven-count-plugin ---
    [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
    [INFO] Running Setup Jobs
    [INFO] Building: apppom.xml
    [INFO] run script validate.groovy.groovy
    [INFO]           apppom.xml ...................................... SUCCESS (9.6 s)
    

    从输出中可以看到 maven-invoker-plugin 的 install 目标将当前项日目 maven-count-plugin 安装至本地仓库,然后它的 run 目标构建测试项目 app,并最后报告运行结果。至此,所有 Maven 插件集成测试的步骤就都完成了。

    上述样例只涉及了 maven-invoker-plugin 的很少一部分配置点,用户还可以配置:

    1. debug(boolean) 是否在构建测试项目的时候开启 debug 输出。
    2. settingsFile(File) 执行集成测试所使用的 settings.xml,默认为本机环境 settings.xml
    3. localRepositoryPath(File) 执行集成测试所使用的本地仓库,默认就是本机环境仓库。
    4. preBuildHookScript(String) 构建测试项目之前运行的 Beanshell 或 Groovy 脚本。
    5. postBuildHookScript(String) 构建测试项目之后运行的 Beanshell 或 Groovy 脚本

    要了解更多的配置点,或者查看更多的样例。读者可以访问 maven-invoker-plugin 的站点: http://maven.apache.org/plugins/maven-invoker-plugin/

  • 相关阅读:
    Head first javascript(七)
    Python Fundamental for Django
    Head first javascript(六)
    Head first javascript(五)
    Head first javascript(四)
    Head first javascript(三)
    Head first javascript(二)
    Head first javascript(一)
    Sicily 1090. Highways 解题报告
    Python GUI programming(tkinter)
  • 原文地址:https://www.cnblogs.com/binarylei/p/8647367.html
Copyright © 2011-2022 走看看