Как разделить общие свойства среди нескольких проектов Maven? - PullRequest
49 голосов
/ 05 августа 2009

У меня есть несколько проектов, созданных maven, и я хочу поделиться некоторыми общими свойствами - весенняя версия, версия драйвера mysql, базовый URL svn и т. Д., Чтобы я мог обновить их один раз, и это будет отражено во всех проектах. ,

Я подумал о том, чтобы иметь один супер-помп со всеми свойствами, но если я изменяю одну из проблем, мне нужно либо увеличить ее версию (и обновить все наследственные помпы от нее), либо удалить ее от всех разработчиков. машины, которые я не хочу делать.

Можете указать эти параметры внешне пом? Я все еще хочу иметь определение внешнего местоположения в родительском пом.

Ответы [ 3 ]

27 голосов
/ 05 августа 2009

Что вы можете сделать, это использовать плагин 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.xml.
14 голосов
/ 11 августа 2009

Обратите внимание, что первоначальная идея, которую я здесь привожу, - это то, чем я занимаюсь, но что я, возможно, нашел гораздо лучшую идею, которую я также перечислил ниже. Я хотел сохранить обе идеи для полноты на случай, если новая идея не сработает.


Я думаю, что вы можете решить эту проблему, используя родительский pom, но вам нужно иметь репозиторий maven и инструмент для сборки CI.

У меня есть несколько проектов, которые все наследуют базовые свойства от родительского POM. Мы используем Java 1.5, поэтому там настраивается свойство сборки. Все в UTF-8. Все отчеты, которые я хочу запустить, настройки сонара и т. Д. Находятся внутри родительского POM.

Предполагая, что ваш проект находится в управлении версиями, и у вас есть инструмент CI, при регистрации ваш инструмент CI может перейти в проект POM и развернуть SNAPSHOT в репозиториях Maven. Если ваши проекты указывают на версию родительского POM SNAPSHOT, они проверят репозиторий, чтобы убедиться, что у них последняя версия ... если нет, то они загрузят последнюю версию. Поэтому, если вы обновите родительский объект, обновятся все остальные проекты.

Уловка, я полагаю, заключается в том, чтобы выпустить СНАПШОТА. Я бы сказал, что ваши релизы будут появляться гораздо реже, чем ваши изменения. Таким образом, вы выполняете выпуск своего POM, затем обновляете свои POM, которые наследуются от них, и проверяете их в управлении версиями. Пусть разработчики знают, что им нужно сделать обновление и идти оттуда.

Вы можете просто запускать там сборки, заставляя новые POM-ы в хранилище, и затем все разработчики автоматически получат изменения при сборке.


Я удалил идею ключевых слов LATEST / RELEASE, потому что они не работают для родительских POM. Они работают только для зависимостей или плагинов. Проблемная область находится в DefaultMavenProjectBuilder. По сути, у него возникают проблемы с определением, в каком репозитории искать родителя, чтобы определить, какая версия последняя или последняя. Не уверен, почему это отличается для зависимостей или плагинов.


Похоже, это будет менее болезненно, чем обновлять POM при каждом изменении родительского POM.

7 голосов
/ 12 августа 2009

Я думаю, что properties-maven-plugin - это правильный подход в долгосрочной перспективе, но, как вы ответили на этот ответ, он не позволяет наследовать свойства. В maven-shared-io есть некоторые средства, которые позволяют вам находить ресурсы на пути к классам проекта. Ниже приведен код, расширяющий плагин свойств для поиска файлов свойств в зависимостях плагина.

Конфигурация объявляет путь к файлу свойств, поскольку проект дескриптора объявлен в конфигурации плагина, он доступен для ClasspathResourceLocatorStrategy. Конфигурация может быть определена в родительском проекте и будет унаследована всеми дочерними проектами (если вы делаете это, избегайте объявления каких-либо файлов, поскольку они не будут обнаружены, только устанавливайте свойство filePaths).

В приведенной ниже конфигурации предполагается, что существует другой проект jar с именем name.seller.rich:test-properties-descriptor: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>

Пом для проекта плагина выглядит так:

<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, позволяющим вам определять относительный путь к внешнему файлу свойств в classpath, оно делает свойство files необязательным и добавляет readPropertyFiles ( ) и getLocation (), чтобы найти файлы и объединить любые filePaths в массив файлов перед продолжением. Я прокомментировал свои изменения, чтобы сделать их более понятными.

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