You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by rf...@apache.org on 2019/01/11 19:30:39 UTC

[maven-invoker-plugin] 01/01: [MINVOKER-246] Support hierarchical invoker.properties

This is an automated email from the ASF dual-hosted git repository.

rfscholte pushed a commit to branch MINVOKER-246
in repository https://gitbox.apache.org/repos/asf/maven-invoker-plugin.git

commit 4382d3afdc3b97ff06627909c6762f8dc39e7798
Author: rfscholte <rf...@apache.org>
AuthorDate: Fri Jan 11 20:30:30 2019 +0100

    [MINVOKER-246] Support hierarchical invoker.properties
---
 src/it/invocation-group-properties/pom.xml         |  69 ++++++
 .../src/it/projects/group-1/invoker.properties     |  18 ++
 .../it/projects/group-1/sub-1/invoker.properties   |  19 ++
 .../src/it/projects/group-1/sub-1/pom.xml          |  52 +++++
 .../src/it/projects/group-1/sub-2/pom.xml          |  52 +++++
 .../it/projects/group-2/sub-1/invoker.properties   |  18 ++
 .../src/it/projects/group-2/sub-1/pom.xml          |  52 +++++
 .../src/it/projects/group-2/sub-2/pom.xml          |  52 +++++
 .../src/it/projects/invoker.properties             |  18 ++
 src/it/invocation-group-properties/verify.groovy   |  20 ++
 .../maven/plugins/invoker/AbstractInvokerMojo.java | 239 ++++++++++++++++-----
 src/site/apt/examples/invoker-properties.apt.vm    |  14 +-
 12 files changed, 567 insertions(+), 56 deletions(-)

diff --git a/src/it/invocation-group-properties/pom.xml b/src/it/invocation-group-properties/pom.xml
new file mode 100644
index 0000000..86b60d1
--- /dev/null
+++ b/src/it/invocation-group-properties/pom.xml
@@ -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
index 0000000..c19c711
--- /dev/null
+++ b/src/it/invocation-group-properties/src/it/projects/group-1/invoker.properties
@@ -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
index 0000000..fe3dca2
--- /dev/null
+++ b/src/it/invocation-group-properties/src/it/projects/group-1/sub-1/invoker.properties
@@ -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
index 0000000..a509339
--- /dev/null
+++ b/src/it/invocation-group-properties/src/it/projects/group-1/sub-1/pom.xml
@@ -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
index 0000000..faebe94
--- /dev/null
+++ b/src/it/invocation-group-properties/src/it/projects/group-1/sub-2/pom.xml
@@ -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
index 0000000..c19c711
--- /dev/null
+++ b/src/it/invocation-group-properties/src/it/projects/group-2/sub-1/invoker.properties
@@ -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
index 0000000..a509339
--- /dev/null
+++ b/src/it/invocation-group-properties/src/it/projects/group-2/sub-1/pom.xml
@@ -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
index 0000000..0424a64
--- /dev/null
+++ b/src/it/invocation-group-properties/src/it/projects/group-2/sub-2/pom.xml
@@ -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
index 0000000..d163069
--- /dev/null
+++ b/src/it/invocation-group-properties/src/it/projects/invoker.properties
@@ -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
index 0000000..7ba59ce
--- /dev/null
+++ b/src/it/invocation-group-properties/verify.groovy
@@ -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
diff --git a/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java b/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java
index 13ab1a8..f835d7b 100644
--- a/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java
+++ b/src/main/java/org/apache/maven/plugins/invoker/AbstractInvokerMojo.java
@@ -82,6 +82,9 @@ import java.io.InputStream;
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.text.DecimalFormat;
 import java.text.DecimalFormatSymbols;
 import java.util.ArrayList;
@@ -89,6 +92,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
@@ -97,6 +101,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Properties;
+import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.TreeSet;
 import java.util.concurrent.ExecutorService;
@@ -460,9 +465,14 @@ public abstract class AbstractInvokerMojo
      * Maven invocation. Any property present in the file will override the corresponding setting from the plugin
      * configuration. The values of the properties are filtered and may use expressions like
      * <code>${project.version}</code> to reference project properties or values from the parameter
-     * {@link #filterProperties}. The snippet below describes the supported properties:
-     * <p/>
+     * {@link #filterProperties}.<p/>
+     * 
+     * <p>
+     * As of 3.2.0 it is possible to put this folder in any of the ancestor folders, where properties will be inherited.
+     * This way you can provide a single properties file for a group of projects
+     * </p>
      *
+     * The snippet below describes the supported properties:
      * <pre>
      * # A comma or space separated list of goals/phases to execute, may
      * # specify an empty list to execute the default goal of the IT project.
@@ -829,10 +839,8 @@ public abstract class AbstractInvokerMojo
 
         File summaryReportFile = new File( reportsDirectory, "invoker-summary.txt" );
 
-        try
+        try ( Writer writer = new BufferedWriter( new FileWriter( summaryReportFile ) ) )
         {
-            Writer writer = new BufferedWriter( new FileWriter( summaryReportFile ) );
-
             for ( int i = 0; i < buildJobs.length; i++ )
             {
                 BuildJob buildJob = buildJobs[i];
@@ -850,8 +858,6 @@ public abstract class AbstractInvokerMojo
                     writer.append( "\n" );
                 }
             }
-
-            writer.close();
         }
         catch ( IOException e )
         {
@@ -1009,7 +1015,7 @@ public abstract class AbstractInvokerMojo
         }
 
         // determine project directories to clone
-        Collection<String> dirs = new LinkedHashSet<String>();
+        Collection<String> dirs = new LinkedHashSet<>();
         for ( String projectPath : projectPaths )
         {
             if ( !new File( projectsDirectory, projectPath ).isDirectory() )
@@ -1222,6 +1228,78 @@ public abstract class AbstractInvokerMojo
         {
             actualJreVersion = SelectorUtils.getJreVersion();
         }
+        
+        final Path projectsPath = this.projectsDirectory.toPath();
+        
+        Set<Path> folderGroupSet = new HashSet<>();
+        folderGroupSet.add( Paths.get( "." ) );
+        for ( BuildJob buildJob : buildJobs )
+        {
+            Path p = Paths.get( buildJob.getProject() );
+            
+            if ( Files.isRegularFile( projectsPath.resolve( p ) ) )
+            {
+                p = p.getParent();
+            }
+            
+            if ( p != null )
+            {
+                p = p.getParent();
+            }
+            
+            while ( p != null && folderGroupSet.add( p ) )
+            {
+                p = p.getParent();
+            }
+        }
+        
+        List<Path> folderGroup = new ArrayList<>( folderGroupSet );
+        Collections.sort( folderGroup );
+
+        final Map<Path, Properties> globalInvokerProperties = new HashMap<>();
+        
+        for ( Path path : folderGroup )
+        {
+            Properties ancestorProperties = globalInvokerProperties.get( projectsPath.resolve( path ).getParent() );
+
+            Path currentInvokerProperties = projectsPath.resolve( path ).resolve( invokerPropertiesFile );
+
+            Properties currentProperties;
+            if ( Files.isRegularFile( currentInvokerProperties ) )
+            {
+                if ( ancestorProperties != null )
+                {
+                    currentProperties = new Properties( ancestorProperties );
+                    
+                }
+                else
+                {
+                    currentProperties = new Properties();
+                }
+            }
+            else
+            {
+                currentProperties = ancestorProperties;
+            }
+
+            if ( Files.isRegularFile( currentInvokerProperties ) )
+            {
+                try ( InputStream in = new FileInputStream( currentInvokerProperties.toFile() ) )
+                {
+                    currentProperties.load( in );
+                }
+                catch ( IOException e )
+                {
+                    throw new MojoExecutionException( "Failed to read invoker properties: "
+                        + currentInvokerProperties );
+                }
+            }
+            
+            if ( currentProperties != null )
+            {
+                globalInvokerProperties.put( projectsPath.resolve( path ).normalize(), currentProperties );
+            }
+        }
 
         try
         {
@@ -1238,7 +1316,10 @@ public abstract class AbstractInvokerMojo
                         {
                             try
                             {
-                                runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion );
+                                Path ancestorFolder = getAncestorFolder( projectsPath.resolve( job.getProject() ) );
+
+                                runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion,
+                                          globalInvokerProperties.get( ancestorFolder ) );
                             }
                             catch ( MojoExecutionException e )
                             {
@@ -1263,7 +1344,10 @@ public abstract class AbstractInvokerMojo
             {
                 for ( BuildJob job : buildJobs )
                 {
-                    runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion );
+                    Path ancestorFolder = getAncestorFolder( projectsPath.resolve( job.getProject() ) );
+                    
+                    runBuild( projectsDir, job, mergedSettingsFile, javaHome, actualJreVersion,
+                              globalInvokerProperties.get( ancestorFolder ) );
                 }
             }
         }
@@ -1279,6 +1363,20 @@ public abstract class AbstractInvokerMojo
             }
         }
     }
+    
+    private Path getAncestorFolder( Path p )
+    {
+        Path ancestor = p;
+        if ( Files.isRegularFile( ancestor ) )
+        {
+            ancestor = ancestor.getParent();
+        }
+        if ( ancestor != null )
+        {
+            ancestor = ancestor.getParent();
+        }
+        return ancestor;
+    }
 
     /**
      * Interpolate settings.xml file.
@@ -1489,10 +1587,11 @@ public abstract class AbstractInvokerMojo
      * @param buildJob The build job to run, must not be <code>null</code>.
      * @param settingsFile The (already interpolated) user settings file for the build, may be <code>null</code> to use
      *            the current user settings.
+     * @param globalInvokerProperties 
      * @throws org.apache.maven.plugin.MojoExecutionException If the project could not be launched.
      */
     private void runBuild( File projectsDir, BuildJob buildJob, File settingsFile, File actualJavaHome,
-                           CharSequence actualJreVersion )
+                           CharSequence actualJreVersion, Properties globalInvokerProperties )
         throws MojoExecutionException
     {
         // FIXME: Think about the following code part -- START
@@ -1521,7 +1620,7 @@ public abstract class AbstractInvokerMojo
 
         getLog().info( buffer().a( "Building: " ).strong( buildJob.getProject() ).toString() );
 
-        InvokerProperties invokerProperties = getInvokerProperties( basedir );
+        InvokerProperties invokerProperties = getInvokerProperties( basedir, globalInvokerProperties );
 
         // let's set what details we can
         buildJob.setName( invokerProperties.getJobName() );
@@ -1727,11 +1826,11 @@ public abstract class AbstractInvokerMojo
 
         File reportFile = new File( reportsDirectory, "BUILD-" + safeFileName + ".xml" );
         try ( FileOutputStream fos = new FileOutputStream( reportFile );
-             Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() ) )
+              Writer osw = new OutputStreamWriter( fos, buildJob.getModelEncoding() ) )
         {
             BuildJobXpp3Writer writer = new BuildJobXpp3Writer();
+
             writer.write( osw, buildJob );
-            osw.close();
         }
         catch ( IOException e )
         {
@@ -2536,51 +2635,89 @@ public abstract class AbstractInvokerMojo
      * @return The invoker properties, may be empty but never <code>null</code>.
      * @throws org.apache.maven.plugin.MojoExecutionException If an I/O error occurred during reading the properties.
      */
-    private InvokerProperties getInvokerProperties( final File projectDirectory )
+    private InvokerProperties getInvokerProperties( final File projectDirectory, Properties globalInvokerProperties )
         throws MojoExecutionException
     {
-        Properties props = new Properties();
-        if ( invokerPropertiesFile != null )
+        Properties props;
+        if ( globalInvokerProperties != null )
+        {
+            props = new Properties( globalInvokerProperties );
+        }
+        else
         {
-            File propertiesFile = new File( projectDirectory, invokerPropertiesFile );
-            if ( propertiesFile.isFile() )
+            props = new Properties();
+        }
+        
+//        Path projectsSourceFolder = this.projectsDirectory.toPath();
+//        Path projectsTargetFolder;
+//        if ( cloneProjectsTo != null )
+//        {
+//            projectsTargetFolder = cloneProjectsTo.toPath();
+//        }
+//        else
+//        {
+//            projectsTargetFolder = projectsSourceFolder;
+//        }
+//        
+//        Path projectDir = projectsTargetFolder.relativize( projectDirectory.toPath() );
+//        
+//        for ( int i = 0; i < projectDir.getNameCount(); i++ )
+//        {
+//            Path subInvokerProperties;
+//            if ( i == 0 )
+//            {
+//                subInvokerProperties = projectsSourceFolder.resolve( invokerPropertiesFile );
+//            }
+//            else
+//            {
+//                subInvokerProperties =
+//                    projectsSourceFolder.resolve( projectDir.subpath( 0, i ) ).resolve( invokerPropertiesFile );
+//            }
+//            
+//            getLog().debug( "Looking for " + subInvokerProperties );
+//                
+//            if ( Files.isRegularFile( subInvokerProperties ) )
+//            {
+//                try ( InputStream in = new FileInputStream( subInvokerProperties.toFile() ) )
+//                {
+//                    props.load( in );
+//                }
+//                catch ( IOException e )
+//                {
+//                    throw new MojoExecutionException( "Failed to read invoker properties: " + subInvokerProperties );
+//                }
+//            }
+//        }
+        
+        File propertiesFile = new File( projectDirectory, invokerPropertiesFile );
+        if ( propertiesFile.isFile() )
+        {
+            try ( InputStream in = new FileInputStream( propertiesFile ) )
             {
-                InputStream in = null;
-                try
-                {
-                    in = new FileInputStream( propertiesFile );
-                    props.load( in );
-                    in.close();
-                    in = null;
-                }
-                catch ( IOException e )
-                {
-                    throw new MojoExecutionException( "Failed to read invoker properties: " + propertiesFile, e );
-                }
-                finally
-                {
-                    IOUtil.close( in );
-                }
+                props.load( in );
+            }
+            catch ( IOException e )
+            {
+                throw new MojoExecutionException( "Failed to read invoker properties: " + propertiesFile, e );
             }
+        }
 
-            Interpolator interpolator = new RegexBasedInterpolator();
-            interpolator.addValueSource( new MapBasedValueSource( getInterpolationValueSource( false ) ) );
-            // CHECKSTYLE_OFF: LineLength
-            for ( String key : props.stringPropertyNames() )
+        Interpolator interpolator = new RegexBasedInterpolator();
+        interpolator.addValueSource( new MapBasedValueSource( getInterpolationValueSource( false ) ) );
+        // CHECKSTYLE_OFF: LineLength
+        for ( String key : props.stringPropertyNames() )
+        {
+            String value = props.getProperty( key );
+            try
             {
-                String value = props.getProperty( key );
-                try
-                {
-                    value = interpolator.interpolate( value, "" );
-                }
-                catch ( InterpolationException e )
-                {
-                    throw new MojoExecutionException( "Failed to interpolate invoker properties: " + propertiesFile,
-                                                      e );
-                }
-                props.setProperty( key, value );
+                value = interpolator.interpolate( value, "" );
+            }
+            catch ( InterpolationException e )
+            {
+                throw new MojoExecutionException( "Failed to interpolate invoker properties: " + propertiesFile,
+                                                  e );
             }
-            // CHECKSTYLE_ON: LineLength
+            props.setProperty( key, value );
         }
         return new InvokerProperties( props );
     }
diff --git a/src/site/apt/examples/invoker-properties.apt.vm b/src/site/apt/examples/invoker-properties.apt.vm
index b3fb07e..f226aa9 100644
--- a/src/site/apt/examples/invoker-properties.apt.vm
+++ b/src/site/apt/examples/invoker-properties.apt.vm
@@ -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