代码之家  ›  专栏  ›  技术社区  ›  David Rabinowitz

如何在多个Maven项目中共享公共属性?

  •  49
  • David Rabinowitz  · 技术社区  · 15 年前

    我有几个由Maven构建的项目,我想在它们之间共享一些共同的属性-Spring版本、MySQL驱动程序版本、SVN基URL等-所以我可以更新它们一次,它将反映在所有项目上。

    我想拥有一个具有所有属性的单一超级POM,但是如果我改变了其中一个问题,我需要增加它的版本(并更新它继承的所有POM),或者从所有开发人员的机器上删除它,我不想这样做。

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

    3 回复  |  直到 7 年前
        1
  •  14
  •   Mike Cornell    15 年前

    请注意,我在这里的原始想法是我正在做的,但我可能已经找到了一个更好的想法,我也在下面列出了。我想把这两个想法都保存在这里,以备更新的想法时使用。 工作。


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

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

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

    我想诀窍是用快照发布。我想说的是,发布版本的频率要比更改的频率低得多。因此,您执行一个pom的发布,然后更新从它们继承的pom,并将它们签入版本控制。让开发人员知道他们需要进行更新,然后从那里开始。

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


    我删除了最新/发布的关键字IDEA,因为它们不适用于父pom。它们只适用于依赖项或插件。问题区域位于DefaultMavenProjectBuilder中。从本质上讲,它难以确定要查找父级以确定最新版本或发布版本的存储库。但不确定为什么依赖项或插件的情况不同。


    听起来,比起每次修改父pom时都要更新pom,这样做的痛苦要小得多。

        2
  •  27
  •   Vineet Jain    7 年前

    你能做的就是使用 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的情况下修改这些属性的值。
        3
  •  7
  •   Rich Seller    15 年前

    我认为属性maven插件是长期正确的方法,但是当您对这个答案做出响应时,它不允许继承属性。Maven共享IO中有一些工具允许您发现项目类路径上的资源。我在下面包含了一些扩展属性插件的代码,以便在插件的依赖项中查找属性文件。

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

    下面的配置假定有另一个名为name.seller.rich的JAR项目:测试属性描述符:0.0.1,它有一个名为external.properties的文件打包到JAR中(即它是在src/main/resources中定义的)。

    <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”属性,允许您定义类路径中外部属性文件的相对路径,它使file s属性成为可选的,并添加readPropertyFiles()和getLocation()方法来定位文件并将任何文件路径合并到FI中。LES数组,然后继续。我已经评论了我的变化以使它们更清晰。

    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
    }