【问题标题】:Maven: how to filter the same resource multiple times with different property values?Maven:如何使用不同的属性值多次过滤相同的资源?
【发布时间】:2010-07-22 11:36:02
【问题描述】:

我们的项目使用 Log4J,通过 log4j.properties 文件进行配置。我们有多个生产服务器,分别记录到不同的日志文件,以便区分日志。所以节点 1 的 log4j.properties 看起来像这样:

...
log4j.appender.Application.File=D:/logs/application_1.log
...
log4j.appender.tx_info.File=D:/logs/tx_info_1.log
...

虽然节点 2 的 log4j.properties 看起来像

...
log4j.appender.Application.File=D:/logs/application_2.log
...
log4j.appender.tx_info.File=D:/logs/tx_info_2.log
...

我们已经使用 Maven 配置文件来生成我们的服务器配置。到目前为止,它包含几个不同的 log4j.properties 文件,它们仅在日志文件名上有所不同,如上所示。我想使用 Maven 使用这样的资源模板文件生成这些文件:

...
log4j.appender.Application.File=${log.location}/application${log.file.postfix}.log
...
log4j.appender.tx_info.File=${log.location}/tx_info${log.file.postfix}.log
...

使用不同的${log.file.postfix} 值多次运行Maven 以每次生成一个不同的日志属性文件很容易。但是,我想要在一个构建中为每个服务器生成一个不同的属性文件(名称/路径不同)。我相当确定这是可以做到的,例如通过antrun插件,但我不熟悉。实现这一目标的最简单方法是什么?

【问题讨论】:

    标签: java resources maven-2 filtering maven-antrun-plugin


    【解决方案1】:

    (...) 我很确定这是可以做到的,例如通过antrun插件,但我不熟悉。实现这一目标的最简单方法是什么?

    你确实可以在你的 POM 中使用resources:copy-resources 和几个<execution>(注意resources:copy-resources 不允许更改目标文件的名称)。

    假设您具有以下结构:

    $ tree .
    .
    ├── pom.xml
    └── src
        ├── main
        │   ├── filters
        │   │   ├── filter-node1.properties
        │   │   └── filter-node2.properties
        │   ├── java
        │   └── resources
        │       ├── log4j.properties
        │       └── another.xml
        └── test
            └── java
    

    log4j.properties 使用占位符,filter-nodeN.properties 文件包含这些值。例如:

    # filter-node1.properties
    
    log.location=D:/logs
    log.file.postfix=_1
    

    然后,在您的pom.xml 中,配置资源插件并为每个节点定义一个<execution> 以调用具有特定输出目录和要使用的特定过滤器的copy-resources

    <project>
      ...
      <build>
        <resources>
          <!-- this is for "normal" resources processing -->
          <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering><!-- you might still want to filter them -->
            <excludes>
              <!-- we exclude the file from "normal" resource processing -->
              <exclude>**/log4j.properties</exclude>
            </excludes>
          </resource>
        </resources>
        <plugins>
          <plugin>
            <artifactId>maven-resources-plugin</artifactId>
            <version>2.4.3</version>
            <executions>
              <execution>
                <id>copy-resources-node1</id>
                <phase>process-resources</phase>
                <goals>
                  <goal>copy-resources</goal>
                </goals>
                <configuration>
                  <outputDirectory>${basedir}/target/node1</outputDirectory>
                  <resources>
                    <resource>
                      <directory>src/main/resources</directory>
                      <filtering>true</filtering>
                      <includes>
                        <include>**/log4j.properties</include>
                      </includes>
                    </resource>
                  </resources>
                  <filters>
                    <filter>src/main/filters/filter-node1.properties</filter>
                  </filters>
                </configuration>
              </execution>
              <execution>
                <id>copy-resources-node2</id>
                <phase>process-resources</phase>
                <goals>
                  <goal>copy-resources</goal>
                </goals>
                <configuration>
                  <outputDirectory>${basedir}/target/node2</outputDirectory>
                  <resources>
                    <resource>
                      <directory>src/main/resources</directory>
                      <filtering>true</filtering>
                      <includes>
                        <include>**/log4j.properties</include>
                      </includes>
                    </resource>
                  </resources>
                  <filters>
                    <filter>src/main/filters/filter-node2.properties</filter>
                  </filters>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </project>
    

    运行 mvn process-resources 会产生以下结果:

    $ tree .
    .
    ├── pom.xml
    ├── src
    │   ├── main
    │   │   ├── filters
    │   │   │   ├── filter-node1.properties
    │   │   │   └── filter-node2.properties
    │   │   ├── java
    │   │   └── resources
    │   │       ├── log4j.properties
    │   │       └── another.xml
    │   └── test
    │       └── java
    └── target
        ├── classes
        │   └── another.xml
        ├── node1
        │   └── log4j.properties
        └── node2
            └── log4j.properties
    

    在每个log4j.properties 中使用适当的值。

    $ cat target/node1/log4j.properties 
    log4j.appender.Application.File=D:/logs/application_1.log
    log4j.appender.tx_info.File=D:/logs/tx_info_1.log
    

    这有点工作,但很冗长,如果您有大量节点,这可能是个问题。


    我尝试使用 Maven AntRun 插件编写更简洁和可维护的东西,但我无法让来自 ant-contribfor 任务在 Maven 下工作(由于未知原因,for 任务无法识别)我放弃了

    这是使用 Maven AntRun 插件的替代方法。没什么复杂的,没有循环,我只是将源文件复制到另一个位置,动态更改其名称并过滤内容:

      <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.3</version>
        <executions>
          <execution>
            <id>copy-resources-all-nodes</id>
            <phase>process-resources</phase>
            <configuration>
              <tasks>
                <copy file="src/main/resources/log4j.properties" toFile="target/antrun/log4j-node1.properties">
                  <filterset>
                    <filter token="log.location" value="D:/logs"/>
                    <filter token="log.file.postfix" value="_1"/>
                  </filterset>
                </copy>
                <copy file="src/main/resources/log4j.properties" toFile="target/antrun/log4j-node2.properties">
                  <filterset>
                    <filter token="log.location" value="D:/logs"/>
                    <filter token="log.file.postfix" value="_2"/>
                  </filterset>
                </copy>
              </tasks>
            </configuration>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    

    请注意,Ant 默认使用 @ 作为标记的分隔符(无法让它使用 maven 样式分隔符),因此 log4j.properties 变为:

    log4j.appender.Application.File=@log.location@/application@log.file.postfix@.log
    log4j.appender.tx_info.File=@log.location@/tx_info@log.file.postfix@.log
    

    但是,由于这些值似乎是特定于节点的,您是否考虑改用系统属性(您可以将其放置在启动脚本中)?这是我已经做过的事情(使用log4j.xml),效果很好,并且可以大大简化事情。

    【讨论】:

    • 感谢您提供完整的示例。它确实非常冗长 - 幸运的是我们只有 3 个节点,所以它不会让 too 失控......顺便说一句,你知道有什么方法可以为每个执行配置文件定义一个属性而不是一个完整的属性文件?我们只有一个属性在节点之间变化,所以一个完整的属性文件听起来有点矫枉过正。
    • 使用系统属性而不是多个执行配置文件听起来肯定更优雅。我猜你依靠 JBoss 来解析 log4j.xml 中的属性,是吗?这可能对我们现在不起作用,因为我们的 log4j.properties 位于外部目录中,而不是通过 JBoss 加载。尽管如此,我们计划在 JBoss 下迁移我们的配置,因此一旦我们将 Log4j 配置迁移到 jboss-log4j.xml 中,这可能会成为完美的解决方案......
    • @Péter:很遗憾,不,我不知道如何在不使用文件进行过滤的情况下使用copy-resources。基于上述细节,我添加了一个 antrun 实现。仍然有一些重复(这就是我最初想使用for 循环的原因)但它仍然更简洁。就个人而言,我更喜欢这个用例的 antrun 方法。关于系统属性,是的,这是受 JBoss 启发的(我在大型 weblogic 集群上成功使用过)。我认为值得一提这个选项。
    【解决方案2】:

    虽然这有点旧,但我最近遇到了这个线程,并想提出一个使用 iterator-maven-plugin 的更新解决方案。可以在此处找到概述:http://khmarbaise.github.io/iterator-maven-plugin/

    如何实现目标的一个具体示例是将迭代器插件与复制资源和启用过滤器相结合。您甚至可以添加自定义属性文件以用作过滤器,以防您使用这种方法拥有每个节点唯一的其他属性。

    <plugin>
    
        <groupId>com.soebes.maven.plugins</groupId>
        <artifactId>iterator-maven-plugin</artifactId>
        <version>0.3</version>
    
        <executions>
            <execution>
                <id>configure-log-properties</id>
                <phase>validate</phase>
                <goals>
                    <goal>iterator</goal>
                </goals>
                <configuration>
                    <iteratorName>log.file.postfix</iteratorName>
                    <content>1,2,3,4,5</content>
                    <pluginExecutors>
    
                        <pluginExecutor>
                            <plugin>
                                <artifactId>maven-resources-plugin</artifactId>
                                <version>2.7</version>
                            </plugin>
    
                            <goal>copy-resources</goal>
    
                            <configuration>
                                <outputDirectory>${project.build.directory}/nodes/${log.file.postfix}</outputDirectory>
    
                                <resources>
                                    <resource>
                                        <directory>src/main/resources</directory>
                                        <includes>
                                            <include>log4j.properties</include>
                                        </includes>
                                        <filtering>true</filtering>
                                    </resource>
                                </resources>
                            </configuration>
    
                        </pluginExecutor>
                    </pluginExecutors>
                </configuration>
            </execution>
        </executions>
    </plugin>
    

    【讨论】:

      【解决方案3】:

      您可以尝试以下方法:

      1. 使用 antrun 插件和copy 任务在generate-resources 阶段复制您的资源文件。在copying with maven 上对这个 SO 问题的回答中给出了使用 antrun 插件的示例。您甚至可以使用 ant 的属性扩展将每个 ${log.file.postfix} 扩展为不同的值,可以是文字值 1,2,3 等,也可以是唯一的占位符 ${log.file.postfix1}, ${ log.file.postfix2} 最后在maven做资源过滤的时候被替换掉。

      2. 不要使用 antrun,而是使用您的版本控制系统来设置同一文件的多个副本。然后,您可以运行 resources:copy-resources 目标的多个实例,每个实例都配置了不同的属性值和不同的目标文件名。

      【讨论】:

      • 感谢您的想法。使用resources:copy-resources,是否可以将同一个文件多次复制到具有不同属性的不同目标?但是,我不知道如何为不同的执行配置属性 - 你能指出一个具体的例子吗?
      • 您可以在 pom 中多次调用 copy-resources 目标,每次指定相同的源不同的目标。我不知道 copy-resources 是否允许重命名文件,但它确实允许设置目标目录。阳光明媚——我得去花园里干活,但我稍后会试着把一些具体的东西放在一起。
      【解决方案4】:

      如果需要复制很多目标配置,可以使用maven-antrun-plugin和ant macrodef。

        <plugin>
          <artifactId>maven-antrun-plugin</artifactId>
          <version>1.3</version>
          <executions>
            <execution>
              <id>copy-resources-all-nodes</id>
              <phase>process-resources</phase>
              <configuration>
                <tasks>
                   <macrodef name="copyConfigFile">
                      <attribute name="node"/>
      
                      <sequential>
                         <copy file="src/main/resources/log4j.properties"
                               toFile="target/antrun/log4j-@{node}.properties">
                            <filterset>
                               <!-- put the node-specific config in property files node1.properties etc -->
                               <filtersfile file="config/@{node}.properties"/>
                            </filterset>
                         </copy>
                      </sequential>
                   </macrodef>
      
                   <copyConfigFile node="node1"/>
                   <copyConfigFile node="node2"/>
                   <copyConfigFile node="node3"/>
                   ...
      
                </tasks>
              </configuration>
              <goals>
                <goal>run</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
      

      如果你真的有很多目标配置,你也可以使用 ant-contrib 来迭代目标配置列表。

      有一个例子如何做到这一点here

      【讨论】:

        猜你喜欢
        • 2012-01-12
        • 2015-06-10
        • 1970-01-01
        • 2018-03-28
        • 1970-01-01
        • 2020-02-20
        • 2016-01-27
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多