【问题标题】:How to share common properties among several maven projects?如何在几个maven项目之间共享公共属性?
【发布时间】:2010-11-16 22:09:29
【问题描述】:

我有几个maven构建的项目,我想分享其中一些共同的属性——spring版本、mysql驱动版本、svn base url等——这样我可以更新一次,它将反映在所有项目中.

我想拥有一个包含所有属性的超级 pom,但如果我更改其中一个问题,我需要增加它的版本(并更新从它继承的所有 pom)或从所有开发人员中删除它' 我不想做的机器。

可以在 pom 外部指定这些参数吗?我仍然希望在父 pom 中有外部位置定义。

【问题讨论】:

  • 走超级 pom 路线,当你更新超级 pom 版本号时,执行:mvn -N versions:update-child-modulesstackoverflow.com/questions/30571/#1172805
  • @Tim 问题是我有几个超级 pom 的层次结构(一般定义 -> spring 项目 -> webapps/services -> 实际模块。AFAIK 版本插件不执行此级联更新。跨度>

标签: configuration maven-2 properties-file


【解决方案1】:

您可以使用Properties Maven plugin。这将允许您在外部文件中定义属性,插件将读取此文件。

有了这个配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>properties-maven-plugin</artifactId>
            <version>1.0-alpha-1</version>
            <executions>
                <execution>
                    <phase>initialize</phase>
                    <goals>
                        <goal>read-project-properties</goal>
                    </goals>
                    <configuration>
                        <files>
                            <file>my-file.properties</file>
                        </files>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

如果有,请在属性文件中添加以下行:

spring-version=1.0
mysql-version=4.0.0

那么就像你在 pom.xml 中写下以下几行一样:

<properties>
    <spring-version>1.0</spring-version>
    <mysql-version>4.0.0</mysql-version>
</properties>

使用此插件,您将获得以下好处:

  • 轻松设置一长串属性
  • 在不修改父 pom.xml 的情况下修改这些属性的值。

【讨论】:

  • 考虑到它是 alpha-1 版本,这个插件有多成熟?
  • 根据这个错误,它仍然对我不可用,因为我想在父 pom 中减速:jira.codehaus.org/browse/MOJO-1303
  • 您能否提供完整的pom.com,您在哪里引用spring-version?根据stackoverflow.com/questions/849389/… 中的讨论,不可能定义依赖项的版本。对吗?
  • 这个答案是不正确的,这与您编写版本属性然后将它们传递给依赖项不同,因为创建依赖关系图与读取属性时的阶段(它们按此顺序发生) ,如果您打算使用它们来声明 deps 的版本,那么从文件中读取这些属性实际上是无用的)
  • 您建议将此属性文件保存在哪里?假设它由 git repo A 和 B 中的两个不同项目共享。
【解决方案2】:

请注意,我在这里的最初想法是我正在做的事情,但我可能已经找到了一个更好的想法,我也在下面列出了它。为了完整起见,我想保留这两个想法,以防新想法不起作用


我认为你可以使用父 pom 解决这个问题,但你需要有一个 maven 存储库和一个 CI 构建工具。

我有几个项目都继承了父 POM 的基本属性。我们使用 Java 1.5,因此在那里设置了构建属性。一切都是UTF-8。我希望运行的所有报告、声纳设置等都在父 POM 中。

假设您的项目处于版本控制中并且您有一个 CI 工具,当您签入时,您的 CI 工具可以构建到 POM 项目并将 SNAPSHOT 部署到 maven 存储库。如果您的项目指向父 POM 的 SNAPSHOT 版本,他们将检查存储库以验证它们是否具有最新版本……如果不是,则下载最新版本。因此,如果您更新父项目,所有其他项目都会更新。

我想诀窍是通过 SNAPSHOT 发布。我会说你的发布会比你的更改要少得多。因此,您执行 POM 的发布,然后更新从它们继承的 POM 并将它们检查到版本控制中。让开发人员知道他们需要进行更新并从那里开始。

您可以在那里触发构建,强制将新的 POM 放入存储库,然后让所有开发人员在构建时自动获取更改。


我删除了 LATEST/RELEASE 关键字的想法,因为它们不适用于父 POM。它们仅适用于依赖项或插件。问题区域在 DefaultMavenProjectBuilder 中。本质上,它很难确定要查找哪个存储库来确定最新或发布版本是什么。不知道为什么这对于依赖项或插件来说是不同的。


听起来这比每次更改父 POM 时都必须更新 POM 更痛苦。

【讨论】:

  • 如果已经有父 pom,如何添加该父 pom?
【解决方案3】:

我认为 properties-maven-plugin 是长期的正确方法,但是当您回复该答案时,它不允许继承属性。 maven-shared-io 中有一些工具可让您发现项目类路径上的资源。我在下面包含了一些代码,这些代码扩展了属性插件以在插件的依赖项中查找属性文件。

配置声明了一个属性文件的路径,因为描述符项目是在插件配置上声明的,所以 ClasspathResourceLocatorStrategy 可以访问它。配置可以在父项目中定义,并将被所有子项目继承(如果这样做,请避免声明任何文件,因为它们不会被发现,只需设置 filePaths 属性)。

下面的配置假设有另一个名为 name.seller.rich:test-properties-descriptor:0.0.1 的 jar 项目有一个名为 external.properties 的文件打包到 jar 中(即它定义在 src/main /资源)。

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>properties-ext-maven-plugin</artifactId>
  <version>0.0.1</version>
  <executions>
    <execution>
      <id>read-properties</id>
      <phase>initialize</phase>
      <goals>
        <goal>read-project-properties</goal>
      </goals>
    </execution>
  </executions>                              
  <configuration>
    <filePaths>
      <filePath>external.properties</filePath>
    </filePaths>
  </configuration> 
  <dependencies>
    <!-- declare any jars that host the required properties files here -->
    <dependency>
      <groupId>name.seller.rich</groupId>
      <artifactId>test-properties-descriptor</artifactId>
      <version>0.0.1</version>
    </dependency>
  </dependencies>
</plugin>

插件项目的 pom 如下所示:

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>properties-ext-maven-plugin</artifactId>
  <packaging>maven-plugin</packaging>
  <version>0.0.1</version>
  <dependencies>
    <dependency>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>properties-maven-plugin</artifactId>
      <version>1.0-alpha-1</version>
    </dependency>
    <dependency>
      <groupId>org.apache.maven.shared</groupId>
      <artifactId>maven-shared-io</artifactId>
      <version>1.1</version>
    </dependency>
  </dependencies>
</project>

mojo 是属性插件的 ReadPropertiesMojo 的副本,带有一个额外的“filePaths”属性,允许您在类路径中定义外部属性文件的相对路径,它使 files 属性可选,并添加了 readPropertyFiles( ) 和 getLocation() 方法来定位文件并将任何文件路径合并到文件数组中,然后再继续。我已对我的更改进行了评论,以使它们更清晰。

package org.codehaus.mojo.xml;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file 
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, 
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
 * KIND, either express or implied.  See the License for the 
 * specific language governing permissions and limitations 
 * under the License.
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.io.location.ClasspathResourceLocatorStrategy;
import org.apache.maven.shared.io.location.FileLocatorStrategy;
import org.apache.maven.shared.io.location.Location;
import org.apache.maven.shared.io.location.Locator;
import org.apache.maven.shared.io.location.LocatorStrategy;
import org.apache.maven.shared.io.location.URLLocatorStrategy;
import org.codehaus.plexus.util.cli.CommandLineUtils;

/**
 * The read-project-properties goal reads property files and stores the
 * properties as project properties. It serves as an alternate to specifying
 * properties in pom.xml.
 * 
 * @author <a href="mailto:zarars@gmail.com">Zarar Siddiqi</a>
 * @author <a href="mailto:Krystian.Nowak@gmail.com">Krystian Nowak</a>
 * @version $Id: ReadPropertiesMojo.java 8861 2009-01-21 15:35:38Z pgier $
 * @goal read-project-properties
 */
public class ReadPropertiesMojo extends AbstractMojo {
    /**
     * @parameter default-value="${project}"
     * @required
     * @readonly
     */
    private MavenProject project;

    /**
     * The properties files that will be used when reading properties.
     * RS: made optional to avoid issue for inherited plugins
     * @parameter
     */
    private File[] files;

    //Begin: RS addition
    /**
     * Optional paths to properties files to be used.
     * 
     * @parameter
     */
    private String[] filePaths;
    //End: RS addition

    /**
     * If the plugin should be quiet if any of the files was not found
     * 
     * @parameter default-value="false"
     */
    private boolean quiet;

    public void execute() throws MojoExecutionException {
        //Begin: RS addition
        readPropertyFiles();
        //End: RS addition

        Properties projectProperties = new Properties();
        for (int i = 0; i < files.length; i++) {
            File file = files[i];

            if (file.exists()) {
                try {
                    getLog().debug("Loading property file: " + file);

                    FileInputStream stream = new FileInputStream(file);
                    projectProperties = project.getProperties();

                    try {
                        projectProperties.load(stream);
                    } finally {
                        if (stream != null) {
                            stream.close();
                        }
                    }
                } catch (IOException e) {
                    throw new MojoExecutionException(
                            "Error reading properties file "
                                    + file.getAbsolutePath(), e);
                }
            } else {
                if (quiet) {
                    getLog().warn(
                            "Ignoring missing properties file: "
                                    + file.getAbsolutePath());
                } else {
                    throw new MojoExecutionException(
                            "Properties file not found: "
                                    + file.getAbsolutePath());
                }
            }
        }

        boolean useEnvVariables = false;
        for (Enumeration n = projectProperties.propertyNames(); n
                .hasMoreElements();) {
            String k = (String) n.nextElement();
            String p = (String) projectProperties.get(k);
            if (p.indexOf("${env.") != -1) {
                useEnvVariables = true;
                break;
            }
        }
        Properties environment = null;
        if (useEnvVariables) {
            try {
                environment = CommandLineUtils.getSystemEnvVars();
            } catch (IOException e) {
                throw new MojoExecutionException(
                        "Error getting system envorinment variables: ", e);
            }
        }
        for (Enumeration n = projectProperties.propertyNames(); n
                .hasMoreElements();) {
            String k = (String) n.nextElement();
            projectProperties.setProperty(k, getPropertyValue(k,
                    projectProperties, environment));
        }
    }

    //Begin: RS addition
    /**
     * Obtain the file from the local project or the classpath
     * 
     * @throws MojoExecutionException
     */
    private void readPropertyFiles() throws MojoExecutionException {
        if (filePaths != null && filePaths.length > 0) {
            File[] allFiles;

            int offset = 0;
            if (files != null && files.length != 0) {
                allFiles = new File[files.length + filePaths.length];
                System.arraycopy(files, 0, allFiles, 0, files.length);
                offset = files.length;
            } else {
                allFiles = new File[filePaths.length];
            }

            for (int i = 0; i < filePaths.length; i++) {
                Location location = getLocation(filePaths[i], project);

                try {
                    allFiles[offset + i] = location.getFile();
                } catch (IOException e) {
                    throw new MojoExecutionException(
                            "unable to open properties file", e);
                }
            }

            // replace the original array with the merged results
            files = allFiles;
        } else if (files == null || files.length == 0) {
            throw new MojoExecutionException(
                    "no files or filePaths defined, one or both must be specified");
        }
    }
    //End: RS addition

    /**
     * Retrieves a property value, replacing values like ${token} using the
     * Properties to look them up. Shamelessly adapted from:
     * http://maven.apache.
     * org/plugins/maven-war-plugin/xref/org/apache/maven/plugin
     * /war/PropertyUtils.html
     * 
     * It will leave unresolved properties alone, trying for System properties,
     * and environment variables and implements reparsing (in the case that the
     * value of a property contains a key), and will not loop endlessly on a
     * pair like test = ${test}
     * 
     * @param k
     *            property key
     * @param p
     *            project properties
     * @param environment
     *            environment variables
     * @return resolved property value
     */
    private String getPropertyValue(String k, Properties p,
            Properties environment) {
        String v = p.getProperty(k);
        String ret = "";
        int idx, idx2;

        while ((idx = v.indexOf("${")) >= 0) {
            // append prefix to result
            ret += v.substring(0, idx);

            // strip prefix from original
            v = v.substring(idx + 2);

            idx2 = v.indexOf("}");

            // if no matching } then bail
            if (idx2 < 0) {
                break;
            }

            // strip out the key and resolve it
            // resolve the key/value for the ${statement}
            String nk = v.substring(0, idx2);
            v = v.substring(idx2 + 1);
            String nv = p.getProperty(nk);

            // try global environment
            if (nv == null) {
                nv = System.getProperty(nk);
            }

            // try environment variable
            if (nv == null && nk.startsWith("env.") && environment != null) {
                nv = environment.getProperty(nk.substring(4));
            }

            // if the key cannot be resolved,
            // leave it alone ( and don't parse again )
            // else prefix the original string with the
            // resolved property ( so it can be parsed further )
            // taking recursion into account.
            if (nv == null || nv.equals(nk)) {
                ret += "${" + nk + "}";
            } else {
                v = nv + v;
            }
        }
        return ret + v;
    }

    //Begin: RS addition
    /**
     * Use various strategies to discover the file.
     */
    public Location getLocation(String path, MavenProject project) {
        LocatorStrategy classpathStrategy = new ClasspathResourceLocatorStrategy();

        List strategies = new ArrayList();
        strategies.add(classpathStrategy);
        strategies.add(new FileLocatorStrategy());
        strategies.add(new URLLocatorStrategy());

        List refStrategies = new ArrayList();
        refStrategies.add(classpathStrategy);

        Locator locator = new Locator();

        locator.setStrategies(strategies);

        Location location = locator.resolve(path);
        return location;
    }
    //End: RS addition
}

【讨论】:

  • 您是否曾经将它作为补丁提交给 Properties Maven 插件?因为我有同样的需求,我在 jar 中有一个属性文件,然后我想使用 maven-resources-plugin 来过滤一个 xml 文件。
  • 这是一个很好的答案,但我遇到了一些麻烦,我有一个父 pom,我从它旁边的文件中读取版本并调用必要的设置方法,设置属性但是当是时候解决他们不习惯的依赖关系了...我是否需要它强制重新插值,用project.getModel().setProperties(propectProperties) 尝试过,但运气不好,我真的希望它工作:'(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-07
  • 1970-01-01
  • 1970-01-01
  • 2011-01-15
相关资源
最近更新 更多