2009-08-05 147 views
47

我有几个项目由maven构建,我想分享它们之间的一些共同属性 - 弹簧版本,mysql驱动程序版本,svn base url等 - 所以我可以更新一次,它会反映在所有项目上。如何在几个Maven项目中共享公共属性?

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

可以在pom外部指定这些参数吗?我仍然想在父pom中使用外部位置定义。

+1

转到超级pom路由,并且当您更新超级pom版本号时,请执行:`mvn -N versions:update-child-modules` http://stackoverflow.com/questions/30571/# 1172805 – Tim 2009-08-05 08:18:23

+0

@Tim问题是我有几个超级poms(通用定义 - >春季项目 - > webapps/services - >实际模块)的层次结构。AFAIK版本插件不会执行此级联更新。 – 2009-08-05 14:06:37

回答

13

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


我想你可以使用解决父POM这个问题,但是你需要有一个Maven仓库和一个CI构建工具。

我有几个项目都从父POM继承基础属性。我们使用Java 1.5,以便在那里设置构建属性。一切都是UTF-8。我希望运行的所有报告,Sonar设置等位于父POM中。

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

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

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


我删除了最新/发布关键字的想法,因为它们不适用于父POM。他们只为依赖或插件工作。问题区域在DefaultMavenProjectBuilder中。从本质上讲,它很难确定哪个存储库查找父级以确定最新版本或发行版本。不知道为什么这是不同的依赖或插件。


听起来好像这些会比每次更改父POM时更新POM都要痛苦。

22

你可以做的是使用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中。
+0

此插件考虑它的成熟程度是一个alpha-1版本吗? – 2009-08-05 07:53:18

7

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

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

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

<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> 

的魔力是性能插件的ReadPropertiesMojo,有一个额外的“文件路径”属性的副本,让你定义的相对路径到类路径中的外部属性文件,它使文件属性为可选,并在继续之前添加readPropertyFiles()和getLocation()方法来定位文件并将任何filePath合并到文件数组中。我已经评论了我的修改,以使它们更清晰。

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:[email protected]">Zarar Siddiqi</a> 
* @author <a href="mailto:[email protected]">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 
}