[MINVOKER-246] Support hierarchical invoker.properties
authorrfscholte <rfscholte@apache.org>
Fri, 11 Jan 2019 19:30:30 +0000 (20:30 +0100)
committerrfscholte <rfscholte@apache.org>
Fri, 11 Jan 2019 19:30:30 +0000 (20:30 +0100)
12 files changed:
src/it/invocation-group-properties/pom.xml [new file with mode: 0644]
src/it/invocation-group-properties/src/it/projects/group-1/invoker.properties [new file with mode: 0644]
src/it/invocation-group-properties/src/it/projects/group-1/sub-1/invoker.properties [new file with mode: 0644]
src/it/invocation-group-properties/src/it/projects/group-1/sub-1/pom.xml [new file with mode: 0644]
src/it/invocation-group-properties/src/it/projects/group-1/sub-2/pom.xml [new file with mode: 0644]
src/it/invocation-group-properties/src/it/projects/group-2/sub-1/invoker.properties [new file with mode: 0644]
src/it/invocation-group-properties/src/it/projects/group-2/sub-1/pom.xml [new file with mode: 0644]
src/it/invocation-group-properties/src/it/projects/group-2/sub-2/pom.xml [new file with mode: 0644]
src/it/invocation-group-properties/src/it/projects/invoker.properties [new file with mode: 0644]
src/it/invocation-group-properties/verify.groovy [new file with mode: 0644]
src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java
src/site/apt/examples/invoker-properties.apt.vm

diff --git a/src/it/invocation-group-properties/pom.xml b/src/it/invocation-group-properties/pom.xml
new file mode 100644 (file)
index 0000000..86b60d1
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<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.apache.maven.plugins.invoker</groupId>
+  <artifactId>invocation-project</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>pom</packaging>
+
+  <description>
+    Test to check that invocations can use a different POM than the one that was selected via the pomInclude
+    parameter. This allows fine-grained control over the build of a multi-module project where the modules are
+    (intentionally) not collectively build by the reactor.
+  </description>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-invoker-plugin</artifactId>
+        <version>@project.version@</version>
+        <configuration>
+          <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
+          <projectsDirectory>src/it/projects</projectsDirectory>
+          <pomIncludes>
+            <include>*/*/pom.xml</include>
+          </pomIncludes>
+          <goals>
+            <goal>compile</goal>
+          </goals>
+        </configuration>
+        <executions>
+          <execution>
+            <id>integration-test</id>
+            <phase>initialize</phase>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/src/it/invocation-group-properties/src/it/projects/group-1/invoker.properties b/src/it/invocation-group-properties/src/it/projects/group-1/invoker.properties
new file mode 100644 (file)
index 0000000..c19c711
--- /dev/null
@@ -0,0 +1,18 @@
+# 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.
+
+invoker.buildResult = failure
\ No newline at end of file
diff --git a/src/it/invocation-group-properties/src/it/projects/group-1/sub-1/invoker.properties b/src/it/invocation-group-properties/src/it/projects/group-1/sub-1/invoker.properties
new file mode 100644 (file)
index 0000000..fe3dca2
--- /dev/null
@@ -0,0 +1,19 @@
+# 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.
+
+invoker.goals = validate
+invoker.buildResult = success
\ No newline at end of file
diff --git a/src/it/invocation-group-properties/src/it/projects/group-1/sub-1/pom.xml b/src/it/invocation-group-properties/src/it/projects/group-1/sub-1/pom.xml
new file mode 100644 (file)
index 0000000..a509339
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<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>test</groupId>
+  <artifactId>sub-1</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <version>3.0.0-M2</version>
+        <executions>
+          <execution>
+            <id>enforce</id>
+            <phase>initialize</phase>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <configuration>
+              <rules>
+                <AlwaysFail/>
+              </rules>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/invocation-group-properties/src/it/projects/group-1/sub-2/pom.xml b/src/it/invocation-group-properties/src/it/projects/group-1/sub-2/pom.xml
new file mode 100644 (file)
index 0000000..faebe94
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<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>test</groupId>
+  <artifactId>sub-2</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <version>3.0.0-M2</version>
+        <executions>
+          <execution>
+            <id>enforce</id>
+            <phase>initialize</phase>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <configuration>
+              <rules>
+                <AlwaysFail/>
+              </rules>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/invocation-group-properties/src/it/projects/group-2/sub-1/invoker.properties b/src/it/invocation-group-properties/src/it/projects/group-2/sub-1/invoker.properties
new file mode 100644 (file)
index 0000000..c19c711
--- /dev/null
@@ -0,0 +1,18 @@
+# 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.
+
+invoker.buildResult = failure
\ No newline at end of file
diff --git a/src/it/invocation-group-properties/src/it/projects/group-2/sub-1/pom.xml b/src/it/invocation-group-properties/src/it/projects/group-2/sub-1/pom.xml
new file mode 100644 (file)
index 0000000..a509339
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<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>test</groupId>
+  <artifactId>sub-1</artifactId>
+  <version>0.1-SNAPSHOT</version>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <version>3.0.0-M2</version>
+        <executions>
+          <execution>
+            <id>enforce</id>
+            <phase>initialize</phase>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <configuration>
+              <rules>
+                <AlwaysFail/>
+              </rules>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/invocation-group-properties/src/it/projects/group-2/sub-2/pom.xml b/src/it/invocation-group-properties/src/it/projects/group-2/sub-2/pom.xml
new file mode 100644 (file)
index 0000000..0424a64
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<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>test</groupId>
+  <artifactId>sub-2</artifactId>
+  <version>0.1-SNAPSHOT</version>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <version>3.0.0-M2</version>
+        <executions>
+          <execution>
+            <id>enforce</id>
+            <phase>compile</phase>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <configuration>
+              <rules>
+                <AlwaysFail/>
+              </rules>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/invocation-group-properties/src/it/projects/invoker.properties b/src/it/invocation-group-properties/src/it/projects/invoker.properties
new file mode 100644 (file)
index 0000000..d163069
--- /dev/null
@@ -0,0 +1,18 @@
+# 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.
+
+invoker.goals = initialize
\ No newline at end of file
diff --git a/src/it/invocation-group-properties/verify.groovy b/src/it/invocation-group-properties/verify.groovy
new file mode 100644 (file)
index 0000000..7ba59ce
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+return true
\ No newline at end of file
index 13ab1a8..f835d7b 100644 (file)
@@ -82,6 +82,9 @@ import java.io.InputStream;
 import java.io.OutputStreamWriter;\r
 import java.io.Reader;\r
 import java.io.Writer;\r
+import java.nio.file.Files;\r
+import java.nio.file.Path;\r
+import java.nio.file.Paths;\r
 import java.text.DecimalFormat;\r
 import java.text.DecimalFormatSymbols;\r
 import java.util.ArrayList;\r
@@ -89,6 +92,7 @@ import java.util.Arrays;
 import java.util.Collection;\r
 import java.util.Collections;\r
 import java.util.HashMap;\r
+import java.util.HashSet;\r
 import java.util.LinkedHashMap;\r
 import java.util.LinkedHashSet;\r
 import java.util.LinkedList;\r
@@ -97,6 +101,7 @@ import java.util.Locale;
 import java.util.Map;\r
 import java.util.Map.Entry;\r
 import java.util.Properties;\r
+import java.util.Set;\r
 import java.util.StringTokenizer;\r
 import java.util.TreeSet;\r
 import java.util.concurrent.ExecutorService;\r
@@ -460,9 +465,14 @@ public abstract class AbstractInvokerMojo
      * Maven invocation. Any property present in the file will override the corresponding setting from the plugin\r
      * configuration. The values of the properties are filtered and may use expressions like\r
      * <code>${project.version}</code> to reference project properties or values from the parameter\r
-     * {@link #filterProperties}. The snippet below describes the supported properties:\r
-     * <p/>\r
+     * {@link #filterProperties}.<p/>\r
+     * \r
+     * <p>\r
+     * As of 3.2.0 it is possible to put this folder in any of the ancestor folders, where properties will be inherited.\r
+     * This way you can provide a single properties file for a group of projects\r
+     * </p>\r
      *\r
+     * The snippet below describes the supported properties:\r
      * <pre>\r
      * # A comma or space separated list of goals/phases to execute, may\r
      * # specify an empty list to execute the default goal of the IT project.\r
@@ -829,10 +839,8 @@ public abstract class AbstractInvokerMojo
 \r
         File summaryReportFile = new File( reportsDirectory, "invoker-summary.txt" );\r
 \r
-        try\r
+        try ( Writer writer = new BufferedWriter( new FileWriter( summaryReportFile ) ) )\r
         {\r
-            Writer writer = new BufferedWriter( new FileWriter( summaryReportFile ) );\r
-\r
             for ( int i = 0; i < buildJobs.length; i++ )\r
             {\r
                 BuildJob buildJob = buildJobs[i];\r
@@ -850,8 +858,6 @@ public abstract class AbstractInvokerMojo
                     writer.append( "\n" );\r
                 }\r
             }\r
-\r
-            writer.close();\r
         }\r
         catch ( IOException e )\r
         {\r
@@ -1009,7 +1015,7 @@ public abstract class AbstractInvokerMojo
         }\r
 \r
         // determine project directories to clone\r
-        Collection<String> dirs = new LinkedHashSet<String>();\r
+        Collection<String> dirs = new LinkedHashSet<>();\r
         for ( String projectPath : projectPaths )\r
         {\r
             if ( !new File( projectsDirectory, projectPath ).isDirectory() )\r
@@ -1222,6 +1228,78 @@ public abstract class AbstractInvokerMojo
         {\r
             actualJreVersion = SelectorUtils.getJreVersion();\r
         }\r
+        \r
+        final Path projectsPath = this.projectsDirectory.toPath();\r
+        \r
+        Set<Path> folderGroupSet = new HashSet<>();\r
+        folderGroupSet.add( Paths.get( "." ) );\r
+        for ( BuildJob buildJob : buildJobs )\r
+        {\r
+            Path p = Paths.get( buildJob.getProject() );\r
+            \r
+            if ( Files.isRegularFile( projectsPath.resolve( p ) ) )\r
+            {\r
+                p = p.getParent();\r
+            }\r
+            \r
+            if ( p != null )\r
+            {\r
+                p = p.getParent();\r
+            }\r
+            \r
+            while ( p != null && folderGroupSet.add( p ) )\r
+            {\r
+                p = p.getParent();\r
+            }\r
+        }\r
+        \r
+        List<Path> folderGroup = new ArrayList<>( folderGroupSet );\r
+        Collections.sort( folderGroup );\r
+\r
+        final Map<Path, Properties> globalInvokerProperties = new HashMap<>();\r
+        \r
+        for ( Path path : folderGroup )\r
+        {\r
+            Properties ancestorProperties = globalInvokerProperties.get( projectsPath.resolve( path ).getParent() );\r
+\r
+            Path currentInvokerProperties = projectsPath.resolve( path ).resolve( invokerPropertiesFile );\r
+\r
+            Properties currentProperties;\r
+            if ( Files.isRegularFile( currentInvokerProperties ) )\r
+            {\r
+                if ( ancestorProperties != null )\r
+                {\r
+                    currentProperties = new Properties( ancestorProperties );\r
+                    \r
+                }\r
+                else\r
+                {\r
+                    currentProperties = new Properties();\r
+                }\r
+            }\r
+            else\r
+            {\r
+                currentProperties = ancestorProperties;\r
+            }\r
+\r
+            if ( Files.isRegularFile( currentInvokerProperties ) )\r
+            {\r
+                try ( InputStream in = new FileInputStream( currentInvokerProperties.toFile() ) )\r
+                {\r
+                    currentProperties.load( in );\r
+                }\r
+                catch ( IOException e )\r
+                {\r
+                    throw new MojoExecutionException( "Failed to read invoker properties: "\r
+                        + currentInvokerProperties );\r
+                }\r
+            }\r
+            \r
+            if ( currentProperties != null )\r
+            {\r
+                globalInvokerProperties.put( projectsPath.resolve( path ).normalize(), currentProperties );\r
+            }\r
+        }\r
 \r
         try\r
         {\r
@@ -1238,7 +1316,10 @@ public abstract class AbstractInvokerMojo
                         {\r
                             try\r
                             {\r
-                                runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion );\r
+                                Path ancestorFolder = getAncestorFolder( projectsPath.resolve( job.getProject() ) );\r
+\r
+                                runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion,\r
+                                          globalInvokerProperties.get( ancestorFolder ) );\r
                             }\r
                             catch ( MojoExecutionException e )\r
                             {\r
@@ -1263,7 +1344,10 @@ public abstract class AbstractInvokerMojo
             {\r
                 for ( BuildJob job : buildJobs )\r
                 {\r
-                    runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion );\r
+                    Path ancestorFolder = getAncestorFolder( projectsPath.resolve( job.getProject() ) );\r
+                    \r
+                    runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion,\r
+                              globalInvokerProperties.get( ancestorFolder ) );\r
                 }\r
             }\r
         }\r
@@ -1279,6 +1363,20 @@ public abstract class AbstractInvokerMojo
             }\r
         }\r
     }\r
+    \r
+    private Path getAncestorFolder( Path p )\r
+    {\r
+        Path ancestor = p;\r
+        if ( Files.isRegularFile( ancestor ) )\r
+        {\r
+            ancestor = ancestor.getParent();\r
+        }\r
+        if ( ancestor != null )\r
+        {\r
+            ancestor = ancestor.getParent();\r
+        }\r
+        return ancestor;\r
+    }\r
 \r
     /**\r
      * Interpolate settings.xml file.\r
@@ -1489,10 +1587,11 @@ public abstract class AbstractInvokerMojo
      * @param buildJob The build job to run, must not be <code>null</code>.\r
      * @param settingsFile The (already interpolated) user settings file for the build, may be <code>null</code> to use\r
      *            the current user settings.\r
+     * @param globalInvokerProperties \r
      * @throws org.apache.maven.plugin.MojoExecutionException If the project could not be launched.\r
      */\r
     private void runBuild( File projectsDir, BuildJob buildJob, File settingsFile, File actualJavaHome,\r
-                           CharSequence actualJreVersion )\r
+                           CharSequence actualJreVersion, Properties globalInvokerProperties )\r
         throws MojoExecutionException\r
     {\r
         // FIXME: Think about the following code part -- START\r
@@ -1521,7 +1620,7 @@ public abstract class AbstractInvokerMojo
 \r
         getLog().info( buffer().a( "Building: " ).strong( buildJob.getProject() ).toString() );\r
 \r
-        InvokerProperties invokerProperties = getInvokerProperties( basedir );\r
+        InvokerProperties invokerProperties = getInvokerProperties( basedir, globalInvokerProperties );\r
 \r
         // let's set what details we can\r
         buildJob.setName( invokerProperties.getJobName() );\r
@@ -1727,11 +1826,11 @@ public abstract class AbstractInvokerMojo
 \r
         File reportFile = new File( reportsDirectory, "BUILD-" + safeFileName + ".xml" );\r
         try ( FileOutputStream fos = new FileOutputStream( reportFile );\r
-             Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() ) )\r
+              Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() ) )\r
         {\r
             BuildJobXpp3Writer writer = new BuildJobXpp3Writer();\r
+\r
             writer.write( osw, buildJob );\r
-            osw.close();\r
         }\r
         catch ( IOException e )\r
         {\r
@@ -2536,51 +2635,89 @@ public abstract class AbstractInvokerMojo
      * @return The invoker properties, may be empty but never <code>null</code>.\r
      * @throws org.apache.maven.plugin.MojoExecutionException If an I/O error occurred during reading the properties.\r
      */\r
-    private InvokerProperties getInvokerProperties( final File projectDirectory )\r
+    private InvokerProperties getInvokerProperties( final File projectDirectory, Properties globalInvokerProperties )\r
         throws MojoExecutionException\r
     {\r
-        Properties props = new Properties();\r
-        if ( invokerPropertiesFile != null )\r
+        Properties props;\r
+        if ( globalInvokerProperties != null )\r
+        {\r
+            props = new Properties( globalInvokerProperties );\r
+        }\r
+        else\r
         {\r
-            File propertiesFile = new File( projectDirectory, invokerPropertiesFile );\r
-            if ( propertiesFile.isFile() )\r
+            props = new Properties();\r
+        }\r
+        \r
+//        Path projectsSourceFolder = this.projectsDirectory.toPath();\r
+//        Path projectsTargetFolder;\r
+//        if ( cloneProjectsTo != null )\r
+//        {\r
+//            projectsTargetFolder = cloneProjectsTo.toPath();\r
+//        }\r
+//        else\r
+//        {\r
+//            projectsTargetFolder = projectsSourceFolder;\r
+//        }\r
+//        \r
+//        Path projectDir = projectsTargetFolder.relativize( projectDirectory.toPath() );\r
+//        \r
+//        for ( int i = 0; i < projectDir.getNameCount(); i++ )\r
+//        {\r
+//            Path subInvokerProperties;\r
+//            if ( i == 0 )\r
+//            {\r
+//                subInvokerProperties = projectsSourceFolder.resolve( invokerPropertiesFile );\r
+//            }\r
+//            else\r
+//            {\r
+//                subInvokerProperties =\r
+//                    projectsSourceFolder.resolve( projectDir.subpath( 0, i ) ).resolve( invokerPropertiesFile );\r
+//            }\r
+//            \r
+//            getLog().debug( "Looking for " + subInvokerProperties );\r
+//                \r
+//            if ( Files.isRegularFile( subInvokerProperties ) )\r
+//            {\r
+//                try ( InputStream in = new FileInputStream( subInvokerProperties.toFile() ) )\r
+//                {\r
+//                    props.load( in );\r
+//                }\r
+//                catch ( IOException e )\r
+//                {\r
+//                    throw new MojoExecutionException( "Failed to read invoker properties: " + subInvokerProperties );\r
+//                }\r
+//            }\r
+//        }\r
+        \r
+        File propertiesFile = new File( projectDirectory, invokerPropertiesFile );\r
+        if ( propertiesFile.isFile() )\r
+        {\r
+            try ( InputStream in = new FileInputStream( propertiesFile ) )\r
             {\r
-                InputStream in = null;\r
-                try\r
-                {\r
-                    in = new FileInputStream( propertiesFile );\r
-                    props.load( in );\r
-                    in.close();\r
-                    in = null;\r
-                }\r
-                catch ( IOException e )\r
-                {\r
-                    throw new MojoExecutionException( "Failed to read invoker properties: " + propertiesFile, e );\r
-                }\r
-                finally\r
-                {\r
-                    IOUtil.close( in );\r
-                }\r
+                props.load( in );\r
+            }\r
+            catch ( IOException e )\r
+            {\r
+                throw new MojoExecutionException( "Failed to read invoker properties: " + propertiesFile, e );\r
             }\r
+        }\r
 \r
-            Interpolator interpolator = new RegexBasedInterpolator();\r
-            interpolator.addValueSource( new MapBasedValueSource( getInterpolationValueSource( false ) ) );\r
-            // CHECKSTYLE_OFF: LineLength\r
-            for ( String key : props.stringPropertyNames() )\r
+        Interpolator interpolator = new RegexBasedInterpolator();\r
+        interpolator.addValueSource( new MapBasedValueSource( getInterpolationValueSource( false ) ) );\r
+        // CHECKSTYLE_OFF: LineLength\r
+        for ( String key : props.stringPropertyNames() )\r
+        {\r
+            String value = props.getProperty( key );\r
+            try\r
             {\r
-                String value = props.getProperty( key );\r
-                try\r
-                {\r
-                    value = interpolator.interpolate( value, "" );\r
-                }\r
-                catch ( InterpolationException e )\r
-                {\r
-                    throw new MojoExecutionException( "Failed to interpolate invoker properties: " + propertiesFile,\r
-                                                      e );\r
-                }\r
-                props.setProperty( key, value );\r
+                value = interpolator.interpolate( value, "" );\r
+            }\r
+            catch ( InterpolationException e )\r
+            {\r
+                throw new MojoExecutionException( "Failed to interpolate invoker properties: " + propertiesFile,\r
+                                                  e );\r
             }\r
-            // CHECKSTYLE_ON: LineLength\r
+            props.setProperty( key, value );\r
         }\r
         return new InvokerProperties( props );\r
     }\r
index b3fb07e..f226aa9 100644 (file)
@@ -31,17 +31,21 @@ Invoker Properties
   The various parameters in the plugin configuration provide a means to globally configure the goals, profiles etc.
   used to run a Maven build on the projects. However, for certain projects you might want to specify different settings.
   To avoid the hassle of multiple plugin executions, you can simply use a file named <<<invoker.properties>>> to
-  control the build settings on a per project basis. The exact name of this properties file is configurable but it needs
-  to reside in the base directory of the respective project as shown below:
+  control the build settings on a per project basis or in one of its ancestor folders to apply it for a group of projects. 
+  The exact name of this properties file is configurable but it needs to reside in the base directory of the respective 
+  project as shown below:
+  
 
 +------------------
 ./
 +- src/
    +- it/
-      +- test-project/
-         +- pom.xml
+      +- group-1
          +- invoker.properties
-         +- src/
+         +- test-project/
+            +- pom.xml
+            +- invoker.properties
+            +- src/
 +------------------
 
   There are only a few keys supported in this file and their names typically match the corresponding parameters in the