我有几个 maven 构建的项目,我想在它们之间分享一些共同的属性——spring 版本、mysql 驱动程序版本、svn base url 等——这样我可以更新它们一次,它将反映在所有项目中。
我想拥有一个具有所有属性的超级 pom,但是如果我更改其中一个问题,我需要增加它的版本(并更新从它继承的所有 pom)或从所有开发人员的机器中删除它我不想做。
可以在 pom 外部指定这些参数吗?我仍然希望在父 pom.xml 中有外部位置定义。
我有几个 maven 构建的项目,我想在它们之间分享一些共同的属性——spring 版本、mysql 驱动程序版本、svn base url 等——这样我可以更新它们一次,它将反映在所有项目中。
我想拥有一个具有所有属性的超级 pom,但是如果我更改其中一个问题,我需要增加它的版本(并更新从它继承的所有 pom)或从所有开发人员的机器中删除它我不想做。
可以在 pom 外部指定这些参数吗?我仍然希望在父 pom.xml 中有外部位置定义。
您可以做的是使用Properties Maven 插件。这将允许您在外部文件中定义您的属性,并且插件将读取此文件。
使用此配置:
<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 解决这个问题,但你需要有一个 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 更痛苦。
我认为 properties-maven-plugin 是长期的正确方法,但是当您回复该答案时,它不允许继承属性。maven-shared-io 中有一些工具可以让您发现项目类路径上的资源。我在下面包含了一些代码,这些代码扩展了属性插件以在插件的依赖项中查找属性文件。
配置声明了属性文件的路径,因为描述符项目是在插件配置上声明的,ClasspathResourceLocatorStrategy 可以访问它。配置可以在父项目中定义,并将被所有子项目继承(如果这样做,请避免声明任何文件,因为它们不会被发现,只需设置 filePaths 属性)。
下面的配置假设有另一个名为 name.seller.rich:test-properties-descriptor:0.0.1 的 jar 项目有一个名为 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”属性,允许您在类路径中定义外部属性文件的相对路径,它使 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
}