[MENFORCER-195] [MENFORCER-269] Added tests to validate wildcard support

diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..e00391c
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,33 @@
+# 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.
+
+language: java
+sudo: false
+
+cache:
+  directories:
+  - "$HOME/.m2"
+
+jobs:
+  include:
+  - stage: test
+    jdk: openjdk11
+    script: mvn verify -Prun-its
+
+branches:
+  only:
+  - master
diff --git a/README.md b/README.md
index bfaf549..c6732c0 100644
--- a/README.md
+++ b/README.md
@@ -91,6 +91,6 @@
 [build-tests]: https://img.shields.io/jenkins/t/https/builds.apache.org/job/maven-box/job/maven-enforcer/job/master.svg?style=flat-square
 [enforcer-home]: https://maven.apache.org/enforcer/maven-enforcer-plugin/
 [enforcer-download]: https://maven.apache.org/enforcer/download.cgi
-[users-list]: https://maven.apache.org/mail-lists.html
+[users-list]: https://maven.apache.org/mailing-lists.html
 [dev-ml-list]: https://www.mail-archive.com/dev@maven.apache.org/
 [code-style]: https://maven.apache.org/developers/conventions/code.html
diff --git a/enforcer-api/pom.xml b/enforcer-api/pom.xml
index 5e9b2b7..c27ddd6 100644
--- a/enforcer-api/pom.xml
+++ b/enforcer-api/pom.xml
@@ -24,7 +24,7 @@
   <parent>
     <groupId>org.apache.maven.enforcer</groupId>
     <artifactId>enforcer</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>3.0.0-M4-SNAPSHOT</version>
   </parent>
 
   <artifactId>enforcer-api</artifactId>
diff --git a/enforcer-rules/pom.xml b/enforcer-rules/pom.xml
index abfd146..03077da 100644
--- a/enforcer-rules/pom.xml
+++ b/enforcer-rules/pom.xml
@@ -24,7 +24,7 @@
   <parent>
     <groupId>org.apache.maven.enforcer</groupId>
     <artifactId>enforcer</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>3.0.0-M4-SNAPSHOT</version>
   </parent>
 
   <artifactId>enforcer-rules</artifactId>
@@ -88,6 +88,16 @@
     <dependency>
       <groupId>org.apache.maven.shared</groupId>
       <artifactId>maven-dependency-tree</artifactId>
+      <exclusions>
+        <exclusion>
+          <groupId>org.eclipse.aether</groupId>
+          <artifactId>aether-util</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.resolver</groupId>
+      <artifactId>maven-resolver-util</artifactId>
     </dependency>
     <dependency>
       <groupId>org.apache.maven</groupId>
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AbstractBanDependencies.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AbstractBanDependencies.java
index 5d3acb1..2888a61 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AbstractBanDependencies.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AbstractBanDependencies.java
@@ -23,6 +23,7 @@
 import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
 import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
 import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.plugins.enforcer.utils.ArtifactUtils;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
@@ -95,7 +96,7 @@
             StringBuilder buf = new StringBuilder();
             if ( message != null )
             {
-                buf.append( message + "\n" );
+                buf.append( message + System.lineSeparator() );
             }
             for ( Artifact artifact : foundExcludes )
             {
@@ -110,7 +111,7 @@
 
     protected CharSequence getErrorMessage( Artifact artifact )
     {
-        return "Found Banned Dependency: " + artifact.getId() + "\n";
+        return "Found Banned Dependency: " + artifact.getId() + System.lineSeparator();
     }
 
     protected Set<Artifact> getDependenciesToCheck( MavenProject project )
@@ -121,7 +122,7 @@
             DependencyNode node = graphBuilder.buildDependencyGraph( project, null );
             if ( searchTransitive )
             {
-                dependencies = getAllDescendants( node );
+                dependencies = ArtifactUtils.getAllDescendants( node );
             }
             else if ( node.getChildren() != null )
             {
@@ -140,25 +141,6 @@
         return dependencies;
     }
 
-    private Set<Artifact> getAllDescendants( DependencyNode node )
-    {
-        Set<Artifact> children = null;
-        if ( node.getChildren() != null )
-        {
-            children = new HashSet<Artifact>();
-            for ( DependencyNode depNode : node.getChildren() )
-            {
-                children.add( depNode.getArtifact() );
-                Set<Artifact> subNodes = getAllDescendants( depNode );
-                if ( subNodes != null )
-                {
-                    children.addAll( subNodes );
-                }
-            }
-        }
-        return children;
-    }
-
     /**
      * Checks the set of dependencies against the list of excludes.
      *
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AbstractRequireFiles.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AbstractRequireFiles.java
index 893917d..90379bd 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AbstractRequireFiles.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AbstractRequireFiles.java
@@ -90,7 +90,7 @@
             StringBuilder buf = new StringBuilder();
             if ( message != null )
             {
-                buf.append( message + "\n" );
+                buf.append( message + System.lineSeparator() );
             }
             buf.append( getErrorMsg() );
 
@@ -98,11 +98,11 @@
             {
                 if ( file != null )
                 {
-                    buf.append( file.getAbsolutePath() + "\n" );
+                    buf.append( file.getAbsolutePath() + System.lineSeparator() );
                 }
                 else
                 {
-                    buf.append( "(an empty filename was given and allowNulls is false)\n" );
+                    buf.append( "(an empty filename was given and allowNulls is false)" + System.lineSeparator() );
                 }
             }
 
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AlwaysFail.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AlwaysFail.java
index 9488eb1..9b0045f 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AlwaysFail.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AlwaysFail.java
@@ -40,7 +40,7 @@
         StringBuffer buf = new StringBuffer();
         if ( message != null )
         {
-            buf.append( message ).append( '\n' );
+            buf.append( message ).append( System.lineSeparator() );
         }
         buf.append( "Always fails!" );
         throw new EnforcerRuleException( buf.toString() );
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AlwaysPass.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AlwaysPass.java
index 018360e..862138c 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AlwaysPass.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/AlwaysPass.java
@@ -42,7 +42,7 @@
         StringBuffer buf = new StringBuffer();
         if ( message != null )
         {
-            buf.append( message ).append( '\n' );
+            buf.append( message ).append( System.lineSeparator() );
         }
         buf.append( "Always pass!" );
         log.info( buf.toString() );
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BanDuplicatePomDependencyVersions.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BanDuplicatePomDependencyVersions.java
index 2b177c3..36a94ed 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BanDuplicatePomDependencyVersions.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BanDuplicatePomDependencyVersions.java
@@ -19,8 +19,8 @@
  * under the License.

  */

 

+import java.io.FileInputStream;

 import java.io.FileNotFoundException;

-import java.io.FileReader;

 import java.io.IOException;

 import java.util.HashMap;

 import java.util.HashSet;

@@ -66,13 +66,13 @@
 

         // re-read model, because M3 uses optimized model

         MavenXpp3Reader modelReader = new MavenXpp3Reader();

-        FileReader pomReader = null;

+        FileInputStream pomInputStream = null;

         Model model;

         try

         {

-            pomReader = new FileReader( project.getFile() );

+            pomInputStream = new FileInputStream( project.getFile() );

 

-            model = modelReader.read( pomReader );

+            model = modelReader.read( pomInputStream, false );

         }

         catch ( FileNotFoundException e )

         {

@@ -88,7 +88,7 @@
         }

         finally

         {

-            IOUtil.close( pomReader );

+            IOUtil.close( pomInputStream );

         }

 

         // @todo reuse ModelValidator when possible

@@ -165,7 +165,7 @@
                 .append( duplicates )

                 .append( " duplicate dependency " );

             message.append( duplicateDependencies.size() == 1 ? "declaration" : "declarations" )

-                .append( " in this project:\n" );

+                .append( " in this project:" + System.lineSeparator() );

             message.append( summary );

             throw new EnforcerRuleException( message.toString() );

         }

@@ -183,7 +183,7 @@
                     .append( entry.getKey() )

                     .append( "] ( " )

                     .append( entry.getValue() )

-                    .append( " times )\n" );

+                    .append( " times )" + System.lineSeparator() );

             }

         }

     }

diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BanTransitiveDependencies.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BanTransitiveDependencies.java
index 0d4db57..6e1dcd4 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BanTransitiveDependencies.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BanTransitiveDependencies.java
@@ -119,7 +119,7 @@
 
             if ( excluded )
             {
-                message.append( " [excluded]\n" );
+                message.append( " [excluded]" + System.lineSeparator() );
             }
 
             if ( hasTransitiveDependencies )
@@ -129,7 +129,7 @@
                     message.append( " has transitive dependencies:" );
                 }
 
-                message.append( "\n" ).append( messageFromChildren );
+                message.append( System.lineSeparator() ).append( messageFromChildren );
             }
         }
 
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedDependencies.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedDependencies.java
index 5a41912..29785fa 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedDependencies.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedDependencies.java
@@ -19,17 +19,13 @@
  * under the License.
  */
 
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
-import org.apache.commons.lang3.StringUtils;
 import org.apache.maven.artifact.Artifact;
-import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
 import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
 import org.apache.maven.plugin.logging.Log;
-import org.apache.maven.plugins.enforcer.utils.ArtifactMatcher;
-import org.apache.maven.plugins.enforcer.utils.ArtifactMatcher.Pattern;
+import org.apache.maven.plugins.enforcer.utils.ArtifactUtils;
 
 /**
  * This rule checks that lists of dependencies are not included.
@@ -71,13 +67,13 @@
         throws EnforcerRuleException
     {
 
-        Set<Artifact> excluded = checkDependencies( theDependencies, excludes );
+        Set<Artifact> excluded = ArtifactUtils.checkDependencies( theDependencies, excludes );
 
         // anything specifically included should be removed
         // from the ban list.
         if ( excluded != null )
         {
-            Set<Artifact> included = checkDependencies( theDependencies, includes );
+            Set<Artifact> included = ArtifactUtils.checkDependencies( theDependencies, includes );
 
             if ( included != null )
             {
@@ -89,72 +85,6 @@
     }
 
     /**
-     * Checks the set of dependencies against the list of patterns.
-     * 
-     * @param thePatterns the patterns
-     * @param dependencies the dependencies
-     * @return a set containing artifacts matching one of the patterns or <code>null</code>
-     * @throws EnforcerRuleException the enforcer rule exception
-     */
-    private Set<Artifact> checkDependencies( Set<Artifact> dependencies, List<String> thePatterns )
-        throws EnforcerRuleException
-    {
-        Set<Artifact> foundMatches = null;
-
-        if ( thePatterns != null && thePatterns.size() > 0 )
-        {
-
-            for ( String pattern : thePatterns )
-            {
-                String[] subStrings = pattern.split( ":" );
-                subStrings = StringUtils.stripAll( subStrings );
-                String resultPattern = StringUtils.join( subStrings, ":" );
-
-                for ( Artifact artifact : dependencies )
-                {
-                    if ( compareDependency( resultPattern, artifact ) )
-                    {
-                        // only create if needed
-                        if ( foundMatches == null )
-                        {
-                            foundMatches = new HashSet<Artifact>();
-                        }
-                        foundMatches.add( artifact );
-                    }
-                }
-            }
-        }
-        return foundMatches;
-    }
-
-    /**
-     * Compares the given pattern against the given artifact. The pattern should follow the format
-     * <code>groupId:artifactId:version:type:scope:classifier</code>.
-     * 
-     * @param pattern The pattern to compare the artifact with.
-     * @param artifact the artifact
-     * @return <code>true</code> if the artifact matches one of the patterns
-     * @throws EnforcerRuleException the enforcer rule exception
-     */
-    protected boolean compareDependency( String pattern, Artifact artifact )
-        throws EnforcerRuleException
-    {
-
-        ArtifactMatcher.Pattern am = new Pattern( pattern );
-        boolean result;
-        try
-        {
-            result = am.match( artifact );
-        }
-        catch ( InvalidVersionSpecificationException e )
-        {
-            throw new EnforcerRuleException( "Invalid Version Range: ", e );
-        }
-
-        return result;
-    }
-
-    /**
      * Gets the excludes.
      * 
      * @return the excludes
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedPlugins.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedPlugins.java
index ddcfb26..03201fd 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedPlugins.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedPlugins.java
@@ -41,7 +41,7 @@
     @Override

     protected CharSequence getErrorMessage( Artifact artifact )

     {

-        return "Found Banned Plugin: " + artifact.getId() + "\n";

+        return "Found Banned Plugin: " + artifact.getId() + System.lineSeparator();

     }

 

 }

diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedRepositories.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedRepositories.java
index dfa7757..c76a70f 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedRepositories.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/BannedRepositories.java
@@ -196,8 +196,8 @@
         if ( !resultBannedRepos.isEmpty() )
         {
             errMsg.append( "Current maven session contains banned" + errorMessagePrefix
-                + "repository urls, please double check your pom or settings.xml:\n"
-                + getRepositoryUrlString( resultBannedRepos ) + "\n\n" );
+                + "repository urls, please double check your pom or settings.xml:" + System.lineSeparator()
+                + getRepositoryUrlString( resultBannedRepos ) + System.lineSeparator() + System.lineSeparator() );
         }
 
         return errMsg.toString();
@@ -208,7 +208,7 @@
         StringBuffer urls = new StringBuffer( "" );
         for ( ArtifactRepository repo : resultBannedRepos )
         {
-            urls.append( repo.getId() + " - " + repo.getUrl() + "\n" );
+            urls.append( repo.getId() + " - " + repo.getUrl() + System.lineSeparator() );
         }
         return urls.toString();
     }
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/DependencyConvergence.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/DependencyConvergence.java
index ed5d300..684f984 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/DependencyConvergence.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/DependencyConvergence.java
@@ -156,7 +156,7 @@
                 builder.append( "  " );
             }
             builder.append( "+-" + loc.get( i ) );
-            builder.append( "\n" );
+            builder.append( System.lineSeparator() );
         }
         return builder;
     }
@@ -174,15 +174,16 @@
     private String buildConvergenceErrorMsg( List<DependencyNode> nodeList )
     {
         StringBuilder builder = new StringBuilder();
-        builder.append( "\nDependency convergence error for " + getFullArtifactName( nodeList.get( 0 ).getArtifact() )
-            + " paths to dependency are:\n" );
+        builder.append( System.lineSeparator() + "Dependency convergence error for "
+            + getFullArtifactName( nodeList.get( 0 ).getArtifact() )
+            + " paths to dependency are:" + System.lineSeparator() );
         if ( nodeList.size() > 0 )
         {
             builder.append( buildTreeString( nodeList.get( 0 ) ) );
         }
         for ( DependencyNode node : nodeList.subList( 1, nodeList.size() ) )
         {
-            builder.append( "and\n" );
+            builder.append( "and" + System.lineSeparator() );
             builder.append( buildTreeString( node ) );
         }
         return builder.toString();
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireActiveProfile.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireActiveProfile.java
index 79c1e67..919f304 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireActiveProfile.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireActiveProfile.java
@@ -106,12 +106,12 @@
                     StringBuilder buf = new StringBuilder();
                     if ( message != null )
                     {
-                        buf.append( message + "\n" );
+                        buf.append( message + System.lineSeparator() );
                     }
 
                     for ( String profile : missingProfiles )
                     {
-                        buf.append( "Profile \"" + profile + "\" is not activated.\n" );
+                        buf.append( "Profile \"" + profile + "\" is not activated." + System.lineSeparator() );
                     }
 
                     throw new EnforcerRuleException( buf.toString() );
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesDontExist.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesDontExist.java
index 2389d34..7ad08f5 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesDontExist.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesDontExist.java
@@ -37,7 +37,7 @@
     @Override
     String getErrorMsg()
     {
-        return "Some files should not exist:\n";
+        return "Some files should not exist:" + System.lineSeparator();
     }
 
 }
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesExist.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesExist.java
index df4252b..5727bd8 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesExist.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesExist.java
@@ -37,7 +37,7 @@
     @Override
     String getErrorMsg()
     {
-        return "Some required files are missing:\n";
+        return "Some required files are missing:" + System.lineSeparator();
     }
 
 }
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireNoRepositories.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireNoRepositories.java
index 6419b73..c4a25a4 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireNoRepositories.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireNoRepositories.java
@@ -141,7 +141,7 @@
             List<Model> badModels = new ArrayList<Model>();
 
             StringBuilder newMsg = new StringBuilder();
-            newMsg.append( "Some poms have repositories defined:\n" );
+            newMsg.append( "Some poms have repositories defined:" + System.lineSeparator() );
 
             for ( Model model : models )
             {
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequirePluginVersions.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequirePluginVersions.java
index f3df358..34f0f20 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequirePluginVersions.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequirePluginVersions.java
@@ -20,7 +20,6 @@
  */
 
 import java.io.IOException;
-import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -79,7 +78,7 @@
 public class RequirePluginVersions
     extends AbstractNonCacheableEnforcerRule
 {
-    
+
     private EnforcerRuleHelper helper;
 
     /**
@@ -162,25 +161,25 @@
     private Collection<Lifecycle> lifecycles;
 
     /** The factory. */
-    ArtifactFactory factory;
+    private ArtifactFactory factory;
 
     /** The resolver. */
-    ArtifactResolver resolver;
+    private ArtifactResolver resolver;
 
     /** The local. */
-    ArtifactRepository local;
+    private ArtifactRepository local;
 
     /** The remote repositories. */
-    List<ArtifactRepository> remoteRepositories;
+    private List<ArtifactRepository> remoteRepositories;
 
     /** The log. */
-    Log log;
+    private Log log;
 
     /** The session. */
-    MavenSession session;
+    private MavenSession session;
 
     /** The utils. */
-    EnforcerRuleUtils utils;
+    private EnforcerRuleUtils utils;
 
     @Override
     public void execute( EnforcerRuleHelper helper )
@@ -198,21 +197,9 @@
             LifecycleExecutor life;
             life = (LifecycleExecutor) helper.getComponent( LifecycleExecutor.class );
 
-            // The lifecycle API changed from Maven 2 to 3 so we have to do a hack to figure
-            // out which one we're using.
-            Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( "defaultLifeCycles", life.getClass() );
-            if ( field != null ) // Using Maven 3
-            {
-                Object defaultLifeCycles = ReflectionUtils.getValueIncludingSuperclasses( "defaultLifeCycles", life );
-                Map lifecyclesMap =
-                    (Map) ReflectionUtils.getValueIncludingSuperclasses( "lifecycles", defaultLifeCycles );
-                lifecycles = lifecyclesMap.values();
-            }
-            else
-            // Using Maven 2
-            {
-                lifecycles = (Collection) ReflectionUtils.getValueIncludingSuperclasses( "lifecycles", life );
-            }
+            Object defaultLifeCycles = ReflectionUtils.getValueIncludingSuperclasses( "defaultLifeCycles", life );
+            Map lifecyclesMap = (Map) ReflectionUtils.getValueIncludingSuperclasses( "lifecycles", defaultLifeCycles );
+            lifecycles = lifecyclesMap.values();
 
             session = (MavenSession) helper.evaluate( "${session}" );
             pluginManager = (PluginManager) helper.getComponent( PluginManager.class );
@@ -248,8 +235,14 @@
             // get all the plugins that are mentioned in the pom (and parents)
             List<PluginWrapper> pluginWrappers = getAllPluginEntries( project );
 
+            for ( PluginWrapper pluginWrapper : pluginWrappers )
+            {
+                log.debug( "pluginWrappers: " + pluginWrapper.getGroupId() + ":" + pluginWrapper.getArtifactId() + ":"
+                    + pluginWrapper.getVersion() + " source:" + pluginWrapper.getSource() );
+            }
             // now look for the versions that aren't valid and add to a list.
             List<Plugin> failures = new ArrayList<Plugin>();
+
             for ( Plugin plugin : allPlugins )
             {
                 if ( !hasValidVersionSpecified( helper, plugin, pluginWrappers ) )
@@ -261,62 +254,7 @@
             // if anything was found, log it then append the optional message.
             if ( !failures.isEmpty() )
             {
-                StringBuilder newMsg = new StringBuilder();
-                newMsg.append( "Some plugins are missing valid versions:" );
-                if ( banLatest || banRelease || banSnapshots || banTimestamps )
-                {
-                    newMsg.append( "(" );
-                    if ( banLatest )
-                    {
-                        newMsg.append( "LATEST " );
-                    }
-                    if ( banRelease )
-                    {
-                        newMsg.append( "RELEASE " );
-                    }
-                    if ( banSnapshots || banTimestamps )
-                    {
-                        newMsg.append( "SNAPSHOT " );
-                    }
-                    newMsg.append( "are not allowed )\n" );
-                }
-                for ( Plugin plugin : failures )
-                {
-                    newMsg.append( plugin.getGroupId() );
-                    newMsg.append( ":" );
-                    newMsg.append( plugin.getArtifactId() );
-
-                    try
-                    {
-                        newMsg.append( ". \tThe version currently in use is " );
-
-                        Plugin currentPlugin = findCurrentPlugin( plugin, project );
-
-                        if ( currentPlugin != null )
-                        {
-                            newMsg.append( currentPlugin.getVersion() );
-                        }
-                        else
-                        {
-                            newMsg.append( "unknown" );
-                        }
-                    }
-                    catch ( Exception e )
-                    {
-                        // lots can go wrong here. Don't allow any issues trying to
-                        // determine the issue stop me
-                        log.debug( "Exception while determining plugin Version.", e );
-                        newMsg.append( ". Unable to determine the plugin version." );
-                    }
-                    newMsg.append( "\n" );
-                }
-                String message = getMessage();
-                if ( StringUtils.isNotEmpty( message ) )
-                {
-                    newMsg.append( message );
-                }
-
-                throw new EnforcerRuleException( newMsg.toString() );
+                handleMessagesToTheUser( project, failures );
             }
         }
         catch ( ExpressionEvaluationException e )
@@ -361,6 +299,73 @@
         }
     }
 
+    private void handleMessagesToTheUser( MavenProject project, List<Plugin> failures )
+        throws EnforcerRuleException
+    {
+        StringBuilder newMsg = new StringBuilder();
+        newMsg.append( "Some plugins are missing valid versions:" );
+        handleBanMessages( newMsg );
+        newMsg.append( "\n" );
+        for ( Plugin plugin : failures )
+        {
+            newMsg.append( plugin.getGroupId() );
+            newMsg.append( ":" );
+            newMsg.append( plugin.getArtifactId() );
+
+            try
+            {
+                newMsg.append( ". \tThe version currently in use is " );
+
+                Plugin currentPlugin = findCurrentPlugin( plugin, project );
+
+                if ( currentPlugin != null )
+                {
+                    newMsg.append( currentPlugin.getVersion() );
+                }
+                else
+                {
+                    newMsg.append( "unknown" );
+                }
+            }
+            catch ( Exception e )
+            {
+                // lots can go wrong here. Don't allow any issues trying to
+                // determine the issue stop me
+                log.debug( "Exception while determining plugin Version.", e );
+                newMsg.append( ". Unable to determine the plugin version." );
+            }
+            newMsg.append( "\n" );
+        }
+        String message = getMessage();
+        if ( StringUtils.isNotEmpty( message ) )
+        {
+            newMsg.append( message );
+        }
+
+        throw new EnforcerRuleException( newMsg.toString() );
+    }
+
+    private void handleBanMessages( StringBuilder newMsg )
+    {
+        if ( banLatest || banRelease || banSnapshots || banTimestamps )
+        {
+            newMsg.append( " (" );
+            if ( banLatest )
+            {
+                newMsg.append( "LATEST " );
+            }
+            if ( banRelease )
+            {
+                newMsg.append( "RELEASE " );
+            }
+            if ( banSnapshots || banTimestamps )
+            {
+                newMsg.append( "SNAPSHOT " );
+            }
+            newMsg.append( "are not allowed)" );
+        }
+    }
+
     /**
      * Remove the plugins that the user doesn't want to check.
      *
@@ -391,7 +396,8 @@
      * @return List of unchecked plugins.
      */
     // CHECKSTYLE_OFF: LineLength
-    public Collection<String> combineUncheckedPlugins( Collection<String> uncheckedPlugins, String uncheckedPluginsList )
+    public Collection<String> combineUncheckedPlugins( Collection<String> uncheckedPlugins,
+                                                       String uncheckedPluginsList )
     // CHECKSTYLE_ON: LineLength
     {
         // if the comma list is empty, then there's nothing to do here.
@@ -545,9 +551,8 @@
     {
 
         List<ArtifactRepository> pluginRepositories = project.getPluginArtifactRepositories();
-        Artifact artifact =
-            factory.createPluginArtifact( plugin.getGroupId(), plugin.getArtifactId(),
-                                          VersionRange.createFromVersion( "LATEST" ) );
+        Artifact artifact = factory.createPluginArtifact( plugin.getGroupId(), plugin.getArtifactId(),
+                                                          VersionRange.createFromVersion( "LATEST" ) );
 
         try
         {
@@ -556,11 +561,11 @@
         }
         catch ( ArtifactResolutionException e )
         {
-            //What does this mean?
+            // What does this mean?
         }
         catch ( ArtifactNotFoundException e )
         {
-            //What does this mean?
+            // What does this mean?
         }
 
         return plugin;
@@ -594,6 +599,8 @@
                 try
                 {
                     Lifecycle lifecycle = getLifecycleForPhase( lifecyclePhase );
+                    log.debug( "getBoundPlugins(): " + project.getId() + " " + lifecyclePhase + " "
+                        + lifecycle.getId() );
                     allPlugins.addAll( getAllPlugins( project, lifecycle ) );
                 }
                 catch ( BuildFailureException e )
@@ -607,12 +614,9 @@
         return allPlugins;
     }
 
-    /*
-     * Checks to see if the version is specified for the plugin. Can optionally ban "RELEASE" or "LATEST" even if
-     * specified.
-     */
     /**
-     * Checks for valid version specified.
+     * Checks for valid version specified. Checks to see if the version is specified for the plugin. Can optionally ban
+     * "RELEASE" or "LATEST" even if specified.
      *
      * @param helper the helper
      * @param source the source
@@ -627,8 +631,7 @@
         for ( PluginWrapper plugin : pluginWrappers )
         {
             // find the matching plugin entry
-            if ( source.getArtifactId().equals( plugin.getArtifactId() )
-                && source.getGroupId().equals( plugin.getGroupId() ) )
+            if ( isMatchingPlugin( source, plugin ) )
             {
                 found = true;
                 // found the entry. now see if the version is specified
@@ -642,9 +645,9 @@
                     return false;
                 }
 
-                if ( StringUtils.isNotEmpty( version ) && !StringUtils.isWhitespace( version ) )
+                if ( isValidVersion( version ) )
                 {
-
+                    helper.getLog().debug( "checking for notEmpty and notIsWhiespace(): " + version );
                     if ( banRelease && version.equals( "RELEASE" ) )
                     {
                         return false;
@@ -675,11 +678,22 @@
         }
         if ( !found )
         {
-            log.debug( "plugin " + source.getGroupId() + ":" + source.getArtifactId() + " not found" );
+            helper.getLog().debug( "plugin " + source.getGroupId() + ":" + source.getArtifactId() + " not found" );
         }
         return status;
     }
 
+    private boolean isValidVersion( String version )
+    {
+        return StringUtils.isNotEmpty( version ) && !StringUtils.isWhitespace( version );
+    }
+
+    private boolean isMatchingPlugin( Plugin source, PluginWrapper plugin )
+    {
+        return source.getArtifactId().equals( plugin.getArtifactId() )
+            && source.getGroupId().equals( plugin.getGroupId() );
+    }
+
     /**
      * Checks if is snapshot.
      *
@@ -782,6 +796,7 @@
                 List<String> phases = lifecycle.getPhases();
                 for ( String phase : phases )
                 {
+                    log.debug( "getPhaseToLifecycleMap(): phase: " + phase );
                     if ( phaseToLifecycleMap.containsKey( phase ) )
                     {
                         Lifecycle prevLifecycle = (Lifecycle) phaseToLifecycleMap.get( phase );
@@ -834,9 +849,8 @@
         String packaging = project.getPackaging();
         Map<String, String> mappings = null;
 
-        LifecycleMapping m =
-            (LifecycleMapping) findExtension( project, LifecycleMapping.ROLE, packaging, session.getSettings(),
-                                              session.getLocalRepository() );
+        LifecycleMapping m = (LifecycleMapping) findExtension( project, LifecycleMapping.ROLE, packaging,
+                                                               session.getSettings(), session.getLocalRepository() );
         if ( m != null )
         {
             mappings = m.getPhases( lifecycle.getId() );
@@ -855,8 +869,8 @@
             {
                 if ( defaultMappings == null )
                 {
-                    throw new LifecycleExecutionException( "Cannot find lifecycle mapping for packaging: \'"
-                        + packaging + "\'.", e );
+                    throw new LifecycleExecutionException( "Cannot find lifecycle mapping for packaging: \'" + packaging
+                        + "\'.", e );
                 }
             }
         }
@@ -892,9 +906,8 @@
         String packaging = project.getPackaging();
         List<String> optionalMojos = null;
 
-        LifecycleMapping m =
-            (LifecycleMapping) findExtension( project, LifecycleMapping.ROLE, packaging, session.getSettings(),
-                                              session.getLocalRepository() );
+        LifecycleMapping m = (LifecycleMapping) findExtension( project, LifecycleMapping.ROLE, packaging,
+                                                               session.getSettings(), session.getLocalRepository() );
 
         if ( m != null )
         {
@@ -911,7 +924,7 @@
             catch ( ComponentLookupException e )
             {
                 log.debug( "Error looking up lifecycle mapping to retrieve optional mojos. Lifecycle ID: "
-                               + lifecycle.getId() + ". Error: " + e.getMessage(), e );
+                    + lifecycle.getId() + ". Error: " + e.getMessage(), e );
             }
         }
 
@@ -1041,98 +1054,143 @@
     protected List<PluginWrapper> getAllPluginEntries( MavenProject project )
         throws ArtifactResolutionException, ArtifactNotFoundException, IOException, XmlPullParserException
     {
-        List<PluginWrapper> plugins = new ArrayList<PluginWrapper>();
-        // get all the pom models
-
         List<Model> models = new ArrayList<Model>();
-        
+
         List<MavenProject> sortedProjects = session.getProjectDependencyGraph().getSortedProjects();
+
+        if ( !sortedProjects.isEmpty() && sortedProjects.get( 0 ).getParent() != null )
+        {
+            getOriginalModelFromAllParents( models, sortedProjects );
+        }
+
         for ( MavenProject mavenProject : sortedProjects )
         {
             models.add( mavenProject.getOriginalModel() );
         }
-                        
+
+        List<PluginWrapper> plugins = new ArrayList<PluginWrapper>();
         // now find all the plugin entries, either in
         // build.plugins or build.pluginManagement.plugins, profiles.plugins and reporting
         for ( Model model : models )
         {
-            try
-            {
-                List<Plugin> modelPlugins = model.getBuild().getPlugins();
-                plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), model.getId()
-                    + ".build.plugins" ) );
-            }
-            catch ( NullPointerException e )
-            {
-                // guess there are no plugins here.
-            }
+            getPlugins( plugins, model );
+            getReportingPlugins( plugins, model );
+            getPluginManagementPlugins( plugins, model );
 
-            try
-            {
-                List<ReportPlugin> modelReportPlugins = model.getReporting().getPlugins();
-                // add the reporting plugins
-                plugins.addAll( PluginWrapper.addAll( utils.resolveReportPlugins( modelReportPlugins ), model.getId()
-                    + ".reporting" ) );
-            }
-            catch ( NullPointerException e )
-            {
-                // guess there are no plugins here.
-            }
-
-            try
-            {
-                List<Plugin> modelPlugins = model.getBuild().getPluginManagement().getPlugins();
-                plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), model.getId()
-                    + ".build.pluginManagement.plugins" ) );
-            }
-            catch ( NullPointerException e )
-            {
-                // guess there are no plugins here.
-            }
-
-            // Add plugins in profiles
-            List<Profile> profiles = model.getProfiles();
-            for ( Profile profile : profiles )
-            {
-                try
-                {
-                    List<Plugin> modelPlugins = profile.getBuild().getPlugins();
-                    plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), model.getId()
-                        + ".profiles.profile[" + profile.getId() + "].build.plugins" ) );
-                }
-                catch ( NullPointerException e )
-                {
-                    // guess there are no plugins here.
-                }
-
-                try
-                {
-                    List<ReportPlugin> modelReportPlugins = profile.getReporting().getPlugins();
-                    // add the reporting plugins
-                    plugins.addAll( PluginWrapper.addAll( utils.resolveReportPlugins( modelReportPlugins ),
-                                                          model.getId() + "profile[" + profile.getId()
-                                                              + "].reporting.plugins" ) );
-                }
-                catch ( NullPointerException e )
-                {
-                    // guess there are no plugins here.
-                }
-                try
-                {
-                    List<Plugin> modelPlugins = profile.getBuild().getPluginManagement().getPlugins();
-                    plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), model.getId()
-                        + "profile[" + profile.getId() + "].build.pluginManagement.plugins" ) );
-                }
-                catch ( NullPointerException e )
-                {
-                    // guess there are no plugins here.
-                }
-            }
+            addPluginsInProfiles( plugins, model );
         }
 
         return plugins;
     }
 
+    private void getOriginalModelFromAllParents( List<Model> models, List<MavenProject> sortedProjects )
+    {
+        MavenProject parent = sortedProjects.get( 0 ).getParent();
+        do
+        {
+            models.add( parent.getOriginalModel() );
+            parent = parent.getParent();
+        }
+        while ( parent != null );
+    }
+
+    private void addPluginsInProfiles( List<PluginWrapper> plugins, Model model )
+    {
+        List<Profile> profiles = model.getProfiles();
+        for ( Profile profile : profiles )
+        {
+            getProfilePlugins( plugins, model, profile );
+            getProfileReportingPlugins( plugins, model, profile );
+            getProfilePluginManagementPlugins( plugins, model, profile );
+        }
+    }
+
+    private void getProfilePluginManagementPlugins( List<PluginWrapper> plugins, Model model, Profile profile )
+    {
+        try
+        {
+            List<Plugin> modelPlugins = profile.getBuild().getPluginManagement().getPlugins();
+            plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), model.getId() + "profile["
+                + profile.getId() + "].build.pluginManagement.plugins" ) );
+        }
+        catch ( NullPointerException e )
+        {
+            // guess there are no plugins here.
+        }
+    }
+
+    private void getProfileReportingPlugins( List<PluginWrapper> plugins, Model model, Profile profile )
+    {
+        try
+        {
+            List<ReportPlugin> modelReportPlugins = profile.getReporting().getPlugins();
+            // add the reporting plugins
+            plugins.addAll( PluginWrapper.addAll( utils.resolveReportPlugins( modelReportPlugins ), model.getId()
+                + "profile[" + profile.getId() + "].reporting.plugins" ) );
+        }
+        catch ( NullPointerException e )
+        {
+            // guess there are no plugins here.
+        }
+    }
+
+    private void getProfilePlugins( List<PluginWrapper> plugins, Model model, Profile profile )
+    {
+        try
+        {
+            List<Plugin> modelPlugins = profile.getBuild().getPlugins();
+            plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), model.getId()
+                + ".profiles.profile[" + profile.getId() + "].build.plugins" ) );
+        }
+        catch ( NullPointerException e )
+        {
+            // guess there are no plugins here.
+        }
+    }
+
+    private void getPlugins( List<PluginWrapper> plugins, Model model )
+    {
+        try
+        {
+            List<Plugin> modelPlugins = model.getBuild().getPlugins();
+            plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ),
+                                                  model.getId() + ".build.plugins" ) );
+        }
+        catch ( NullPointerException e )
+        {
+            // guess there are no plugins here.
+        }
+    }
+
+    private void getPluginManagementPlugins( List<PluginWrapper> plugins, Model model )
+    {
+        try
+        {
+            List<Plugin> modelPlugins = model.getBuild().getPluginManagement().getPlugins();
+            plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ),
+                                                  model.getId() + ".build.pluginManagement.plugins" ) );
+        }
+        catch ( NullPointerException e )
+        {
+            // guess there are no plugins here.
+        }
+    }
+
+    private void getReportingPlugins( List<PluginWrapper> plugins, Model model )
+    {
+        try
+        {
+            List<ReportPlugin> modelReportPlugins = model.getReporting().getPlugins();
+            // add the reporting plugins
+            plugins.addAll( PluginWrapper.addAll( utils.resolveReportPlugins( modelReportPlugins ),
+                                                  model.getId() + ".reporting" ) );
+        }
+        catch ( NullPointerException e )
+        {
+            // guess there are no plugins here.
+        }
+    }
+
     /**
      * Checks if is ban latest.
      *
@@ -1262,4 +1320,4 @@
     {
         return additionalPlugins;
     }
-}
\ No newline at end of file
+}
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireProfileIdsExist.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireProfileIdsExist.java
index 770f644..d595497 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireProfileIdsExist.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireProfileIdsExist.java
@@ -33,6 +33,7 @@
  * Ensure that all profiles mentioned on the commandline do exist. 

  * 

  * @author Robert Scholte

+ * @author Gabriel Belingueres

  */

 public class RequireProfileIdsExist extends AbstractNonCacheableEnforcerRule

 {

@@ -43,24 +44,32 @@
         try

         {

             MavenSession session = (MavenSession) helper.evaluate( "${session}" );

-            

+

             List<String> profileIds = new ArrayList<String>();

             profileIds.addAll( session.getProjectBuildingRequest().getActiveProfileIds() );

             profileIds.addAll( session.getProjectBuildingRequest().getInactiveProfileIds() );

-            

+

             for ( MavenProject project : session.getProjects() )

             {

-                for ( org.apache.maven.model.Profile profile : project.getModel().getProfiles() )

+                // iterate over all parents

+                MavenProject currentProject = project;

+                do

                 {

-                    profileIds.remove( profile.getId() );

-                    

-                    if ( profileIds.isEmpty() )

+                    for ( org.apache.maven.model.Profile profile : currentProject.getModel().getProfiles() )

                     {

-                        return;

+                        profileIds.remove( profile.getId() );

+

+                        if ( profileIds.isEmpty() )

+                        {

+                            return;

+                        }

                     }

+

+                    currentProject = currentProject.getParent();

                 }

+                while ( currentProject != null );

             }

-            

+

             for ( org.apache.maven.settings.Profile profile : session.getSettings().getProfiles() )

             {

                 profileIds.remove( profile.getId() );

@@ -81,7 +90,7 @@
                 sb.append( "The requested profile doesn't exist: " );

             }

             sb.append( StringUtils.join( profileIds.iterator(), ", " ) );

-            

+

             throw new EnforcerRuleException( sb.toString() );

         }

         catch ( ExpressionEvaluationException e )

diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireReleaseVersion.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireReleaseVersion.java
index 590ad2c..2a682d5 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireReleaseVersion.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireReleaseVersion.java
@@ -57,7 +57,7 @@
             StringBuffer buf = new StringBuffer();
             if ( message != null )
             {
-                buf.append( message ).append( '\n' );
+                buf.append( message ).append( System.lineSeparator() );
             }
             buf.append( "This project cannot be a snapshot:" ).append( project.getArtifact().getId() );
             throw new EnforcerRuleException( buf.toString() );
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersions.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersions.java
index d4eab55..d247ec6 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersions.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersions.java
@@ -82,13 +82,14 @@
 

         if ( versionMembers.size() > 1 )

         {

-            StringBuilder builder = new StringBuilder( "Found entries with different versions\n" );

+            StringBuilder builder = new StringBuilder( "Found entries with different versions"

+                + System.lineSeparator() );

             for ( Map.Entry<String, List<String>> entry : versionMembers.entrySet() )

             {

-                builder.append( "Entries with version " ).append( entry.getKey() ).append( '\n' );

+                builder.append( "Entries with version " ).append( entry.getKey() ).append( System.lineSeparator() );

                 for ( String conflictId : entry.getValue() )

                 {

-                    builder.append( "- " ).append( conflictId ).append( '\n' );

+                    builder.append( "- " ).append( conflictId ).append( System.lineSeparator() );

                 }

             }

             throw new EnforcerRuleException( builder.toString() );

diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSnapshotVersion.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSnapshotVersion.java
index 91e826f..a7d9fd2 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSnapshotVersion.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSnapshotVersion.java
@@ -51,7 +51,7 @@
             StringBuilder sb = new StringBuilder();
             if ( message != null )
             {
-                sb.append( message ).append( '\n' );
+                sb.append( message ).append( System.lineSeparator() );
             }
             sb.append( "This project cannot be a release:" ).append( artifact.getId() );
             throw new EnforcerRuleException( sb.toString() );
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireUpperBoundDeps.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireUpperBoundDeps.java
index 9769553..458554a 100644
--- a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireUpperBoundDeps.java
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireUpperBoundDeps.java
@@ -182,15 +182,15 @@
     private String buildErrorMessage( List<DependencyNode> conflict )
     {
         StringBuilder errorMessage = new StringBuilder();
-        errorMessage.append( "\nRequire upper bound dependencies error for "
-            + getFullArtifactName( conflict.get( 0 ), false ) + " paths to dependency are:\n" );
+        errorMessage.append( System.lineSeparator() + "Require upper bound dependencies error for "
+            + getFullArtifactName( conflict.get( 0 ), false ) + " paths to dependency are:" + System.lineSeparator() );
         if ( conflict.size() > 0 )
         {
             errorMessage.append( buildTreeString( conflict.get( 0 ) ) );
         }
         for ( DependencyNode node : conflict.subList( 1, conflict.size() ) )
         {
-            errorMessage.append( "and\n" );
+            errorMessage.append( "and" + System.lineSeparator() );
             errorMessage.append( buildTreeString( node ) );
         }
         return errorMessage.toString();
@@ -222,7 +222,7 @@
                 builder.append( "  " );
             }
             builder.append( "+-" ).append( loc.get( i ) );
-            builder.append( "\n" );
+            builder.append( System.lineSeparator() );
         }
         return builder;
     }
diff --git a/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/ArtifactUtils.java b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/ArtifactUtils.java
new file mode 100644
index 0000000..a839613
--- /dev/null
+++ b/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/ArtifactUtils.java
@@ -0,0 +1,129 @@
+package org.apache.maven.plugins.enforcer.utils;
+
+/*
+ * 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.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.plugins.enforcer.utils.ArtifactMatcher.Pattern;
+import org.apache.maven.shared.dependency.graph.DependencyNode;
+
+/**
+ * 
+ * @author Robert Scholte
+ * @since 3.0.0
+ */
+public final class ArtifactUtils
+{
+    private ArtifactUtils()
+    {
+    }
+
+    public static Set<Artifact> getAllDescendants( DependencyNode node )
+    {
+        Set<Artifact> children = null;
+        if ( node.getChildren() != null )
+        {
+            children = new HashSet<Artifact>();
+            for ( DependencyNode depNode : node.getChildren() )
+            {
+                children.add( depNode.getArtifact() );
+                Set<Artifact> subNodes = getAllDescendants( depNode );
+                if ( subNodes != null )
+                {
+                    children.addAll( subNodes );
+                }
+            }
+        }
+        return children;
+    }
+
+    /**
+     * Checks the set of dependencies against the list of patterns.
+     * 
+     * @param thePatterns the patterns
+     * @param dependencies the dependencies
+     * @return a set containing artifacts matching one of the patterns or <code>null</code>
+     * @throws EnforcerRuleException the enforcer rule exception
+     */
+    public static Set<Artifact> checkDependencies( Set<Artifact> dependencies, List<String> thePatterns )
+        throws EnforcerRuleException
+    {
+        Set<Artifact> foundMatches = null;
+    
+        if ( thePatterns != null && thePatterns.size() > 0 )
+        {
+    
+            for ( String pattern : thePatterns )
+            {
+                String[] subStrings = pattern.split( ":" );
+                subStrings = StringUtils.stripAll( subStrings );
+                String resultPattern = StringUtils.join( subStrings, ":" );
+    
+                for ( Artifact artifact : dependencies )
+                {
+                    if ( compareDependency( resultPattern, artifact ) )
+                    {
+                        // only create if needed
+                        if ( foundMatches == null )
+                        {
+                            foundMatches = new HashSet<Artifact>();
+                        }
+                        foundMatches.add( artifact );
+                    }
+                }
+            }
+        }
+        return foundMatches;
+    }
+
+    /**
+     * Compares the given pattern against the given artifact. The pattern should follow the format
+     * <code>groupId:artifactId:version:type:scope:classifier</code>.
+     * 
+     * @param pattern The pattern to compare the artifact with.
+     * @param artifact the artifact
+     * @return <code>true</code> if the artifact matches one of the patterns
+     * @throws EnforcerRuleException the enforcer rule exception
+     */
+    private static boolean compareDependency( String pattern, Artifact artifact )
+        throws EnforcerRuleException
+    {
+    
+        ArtifactMatcher.Pattern am = new Pattern( pattern );
+        boolean result;
+        try
+        {
+            result = am.match( artifact );
+        }
+        catch ( InvalidVersionSpecificationException e )
+        {
+            throw new EnforcerRuleException( "Invalid Version Range: ", e );
+        }
+    
+        return result;
+    }
+
+}
diff --git a/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestRequirePluginVersions.java b/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestRequirePluginVersions.java
index 422967d..64290af 100644
--- a/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestRequirePluginVersions.java
+++ b/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/TestRequirePluginVersions.java
@@ -80,6 +80,7 @@
         rule.setBanRelease( false );
         rule.setBanSnapshots( false );
 
+
         EnforcerRuleHelper helper = EnforcerTestUtils.getHelper();
 
         assertTrue( rule.hasValidVersionSpecified( helper, source, pluginWrappers ) );
diff --git a/maven-enforcer-plugin/pom.xml b/maven-enforcer-plugin/pom.xml
index 6729d40..f2f6c34 100644
--- a/maven-enforcer-plugin/pom.xml
+++ b/maven-enforcer-plugin/pom.xml
@@ -24,7 +24,7 @@
   <parent>
     <groupId>org.apache.maven.enforcer</groupId>
     <artifactId>enforcer</artifactId>
-    <version>3.0.0-SNAPSHOT</version>
+    <version>3.0.0-M4-SNAPSHOT</version>
   </parent>
 
   <groupId>org.apache.maven.plugins</groupId>
@@ -151,6 +151,9 @@
   <profiles>
     <profile>
       <id>run-its</id>
+      <properties>
+        <maven.it.failure.ignore>false</maven.it.failure.ignore>
+      </properties>
       <build>
         <plugins>
           <plugin>
@@ -163,12 +166,15 @@
               <postBuildHookScript>verify</postBuildHookScript>
               <localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
               <settingsFile>src/it/mrm/settings.xml</settingsFile>
+              <ignoreFailures>${maven.it.failure.ignore}</ignoreFailures>
               <goals>
                 <goal>validate</goal>
               </goals>
               <properties>
                 <!-- e.g. ensure that Java7 picks up TLSv1.2 when connecting with Central -->
                 <https.protocols>${https.protocols}</https.protocols>
+                <maven.compiler.source>${maven.compiler.source}</maven.compiler.source>
+                <maven.compiler.target>${maven.compiler.target}</maven.compiler.target>
               </properties>
             </configuration>
             <executions>
diff --git a/maven-enforcer-plugin/src/it/pom.xml b/maven-enforcer-plugin/src/it/pom.xml
deleted file mode 100644
index 776da8e..0000000
--- a/maven-enforcer-plugin/src/it/pom.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-<?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/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-	<groupId>org.apache.maven.plugins</groupId>
-	<artifactId>maven-enforcer-plugin-it1</artifactId>
-	<version>1</version>
-	<packaging>jar</packaging>
-		  <dependencies>
-	    <dependency>
-		  <groupId>org.apache.maven.shared</groupId>
-		  <artifactId>maven-invoker</artifactId>
-		  <version>2.0.7-SNAPSHOT</version>
-		</dependency>
-	  </dependencies> 
-	
-	<build>
-		<pluginManagement>
-			<plugins>
-				<plugin>
-					<groupId>org.apache.maven.plugins</groupId>
-					<artifactId>maven-compiler-plugin</artifactId>
-					<version>2.0.2</version>
-				</plugin>
-			</plugins>
-		</pluginManagement>
-		<plugins>
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-dependency-plugin</artifactId>
-				<version>2.0-alpha-4</version>
-				<executions>
-					<execution>
-						<id>copy</id>
-						<goals>
-						<goal>copy-dependencies</goal>
-						</goals>
-					</execution>
-				</executions>
-			</plugin>
-			<plugin>
-				<groupId>org.apache.maven.plugins</groupId>
-				<artifactId>maven-enforcer-plugin</artifactId>
-                <version>1.0-SNAPSHOT</version>
-				<dependencies>
-				 <dependency>
-						<groupId>custom-rule</groupId>
-						<artifactId>custom-rule-sample</artifactId>
-						<version>1.0</version>
-					</dependency>
-				</dependencies>
-				<executions>
-					<execution>
-						<id>enforce</id>
-						<configuration>
-							<rules>
-								<requireJavaVersion>
-									<version>[1.3,1.6]</version>
-								</requireJavaVersion>
-								<requireMavenVersion>
-									<version>2.0.8</version>
-									<message>You need 2.0.8!</message>
-								</requireMavenVersion>
-								<myCustomRule implementation="org.apache.maven.enforcer.rule.MyCustomRule">
-									<shouldIfail>true</shouldIfail>
-								</myCustomRule>
-								<requireOS>
-									<family>!tandem</family>
-								</requireOS>
-								<evaluateBeanshell>
-									<condition>rti.getApplicationVersion().getMajorVersion() == 2;</condition>
-								</evaluateBeanshell>
-								<noSnapshots/>
-								<bannedDependencies>
-								<excludes>
-								<exclude>org.apache.maven</exclude>
-								</excludes>
-								</bannedDependencies>
-								<bannedPlugins>
-								<excludes>
-								<exclude>com.acme</exclude>
-								</excludes>
-								</bannedPlugins>
-								<requirePluginVersions/>
-							</rules>
-						</configuration>
-						<goals>
-							<goal>enforce</goal>
-						</goals>
-						<phase>validate</phase>
-
-					</execution>
-				</executions>
-			</plugin>
-		</plugins>
-	</build>
-</project>
diff --git a/maven-enforcer-plugin/src/it/projects/MENFORCER-306/parent/pom.xml b/maven-enforcer-plugin/src/it/projects/MENFORCER-306/parent/pom.xml
new file mode 100644
index 0000000..d66827e
--- /dev/null
+++ b/maven-enforcer-plugin/src/it/projects/MENFORCER-306/parent/pom.xml
@@ -0,0 +1,103 @@
+<?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/xsd/maven-4.0.0.xsd">

+  <modelVersion>4.0.0</modelVersion>

+  <groupId>mbenson.test</groupId>

+  <artifactId>test-require-plugin-versions</artifactId>

+  <version>0.0.1-SNAPSHOT</version>

+  <packaging>pom</packaging>

+  <properties>

+    <enforcer.version>3.0.0-SNAPSHOT</enforcer.version>

+  </properties>

+  <build>

+    <plugins>

+      <plugin>

+        <groupId>org.apache.maven.plugins</groupId>

+        <artifactId>maven-enforcer-plugin</artifactId>

+        <version>@project.version@</version>

+        <executions>

+          <execution>

+            <id>enforce-plugin-versions</id>

+            <goals>

+              <goal>enforce</goal>

+            </goals>

+            <configuration>

+              <rules>

+                <requirePluginVersions>

+                  <banSnapshots>false</banSnapshots>

+                </requirePluginVersions>

+              </rules>

+            </configuration>

+          </execution>

+        </executions>

+      </plugin>

+    </plugins>

+    <pluginManagement>

+      <plugins>

+        <plugin>

+          <groupId>org.apache.maven.plugins</groupId>

+          <artifactId>maven-clean-plugin</artifactId>

+          <version>3.1.0</version>

+        </plugin>

+        <plugin>

+          <groupId>org.apache.maven.plugins</groupId>

+          <artifactId>maven-install-plugin</artifactId>

+          <version>2.5.2</version>

+        </plugin>

+        <plugin>

+          <groupId>org.apache.maven.plugins</groupId>

+          <artifactId>maven-site-plugin</artifactId>

+          <version>3.7.1</version>

+        </plugin>

+        <plugin>

+          <groupId>org.apache.maven.plugins</groupId>

+          <artifactId>maven-deploy-plugin</artifactId>

+          <version>2.8.2</version>

+        </plugin>

+        <plugin>

+          <groupId>org.apache.maven.plugins</groupId>

+          <artifactId>maven-compiler-plugin</artifactId>

+          <version>3.7.0</version>

+        </plugin>

+        <plugin>

+          <groupId>org.apache.maven.plugins</groupId>

+          <artifactId>maven-plugin-plugin</artifactId>

+          <version>3.5.2</version>

+        </plugin>

+        <plugin>

+          <groupId>org.apache.maven.plugins</groupId>

+          <artifactId>maven-surefire-plugin</artifactId>

+          <version>2.21.0</version>

+        </plugin>

+        <plugin>

+          <groupId>org.apache.maven.plugins</groupId>

+          <artifactId>maven-jar-plugin</artifactId>

+          <version>3.1.0</version>

+        </plugin>

+        <plugin>

+          <groupId>org.apache.maven.plugins</groupId>

+          <artifactId>maven-resources-plugin</artifactId>

+          <version>3.1.0</version>

+        </plugin>

+      </plugins>

+    </pluginManagement>

+  </build>

+</project>

diff --git a/maven-enforcer-plugin/src/it/projects/MENFORCER-306/pom.xml b/maven-enforcer-plugin/src/it/projects/MENFORCER-306/pom.xml
new file mode 100644
index 0000000..9ad1574
--- /dev/null
+++ b/maven-enforcer-plugin/src/it/projects/MENFORCER-306/pom.xml
@@ -0,0 +1,31 @@
+<?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/xsd/maven-4.0.0.xsd">

+  <modelVersion>4.0.0</modelVersion>

+  <parent>

+    <groupId>mbenson.test</groupId>

+    <artifactId>test-require-plugin-versions</artifactId>

+    <version>0.0.1-SNAPSHOT</version>

+    <relativePath>parent/pom.xml</relativePath>

+  </parent>

+  <artifactId>pom</artifactId>

+  <packaging>pom</packaging>

+</project>

diff --git a/maven-enforcer-plugin/src/it/projects/ban-pom-dependency-version-utf8-with-bom/invoker.properties b/maven-enforcer-plugin/src/it/projects/ban-pom-dependency-version-utf8-with-bom/invoker.properties
new file mode 100644
index 0000000..c98ac4c
--- /dev/null
+++ b/maven-enforcer-plugin/src/it/projects/ban-pom-dependency-version-utf8-with-bom/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/maven-enforcer-plugin/src/it/projects/ban-pom-dependency-version-utf8-with-bom/pom.xml b/maven-enforcer-plugin/src/it/projects/ban-pom-dependency-version-utf8-with-bom/pom.xml
new file mode 100644
index 0000000..125f87e
--- /dev/null
+++ b/maven-enforcer-plugin/src/it/projects/ban-pom-dependency-version-utf8-with-bom/pom.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This file is intenitonally in UTF-8 BOM encoding. Keep it that way. -->
+<!--
+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>
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.maven.its.enforcer</groupId>
+  <artifactId>ban-pom-dependency-version-utf8-with-bom</artifactId>
+  <version>1.0-SNAPSHOT</version>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <version>@project.version@</version>
+        <executions>
+          <execution>
+            <id>test</id>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <configuration>
+              <rules>
+                <banDuplicatePomDependencyVersions/>
+              </rules>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.maven.plugins.enforcer.its</groupId>
+      <artifactId>menforcer152</artifactId>
+      <version>1.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.plugins.enforcer.its</groupId>
+      <artifactId>menforcer152</artifactId>
+      <version>1.0</version>
+    </dependency>
+  </dependencies>
+
+</project>
diff --git a/maven-enforcer-plugin/src/it/projects/ban-pom-dependency-version-utf8-with-bom/verify.groovy b/maven-enforcer-plugin/src/it/projects/ban-pom-dependency-version-utf8-with-bom/verify.groovy
new file mode 100644
index 0000000..eb917f9
--- /dev/null
+++ b/maven-enforcer-plugin/src/it/projects/ban-pom-dependency-version-utf8-with-bom/verify.groovy
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+File buildLog = new File( basedir, 'build.log' )
+assert buildLog.text.contains( '[WARNING] Rule 0: org.apache.maven.plugins.enforcer.BanDuplicatePomDependencyVersions failed with message:' )
+assert buildLog.text.contains( 'Found 1 duplicate dependency declaration in this project:' )
+assert buildLog.text.contains( '- dependencies.dependency[org.apache.maven.plugins.enforcer.its:menforcer152:jar] ( 2 times )' )
+
+
+
+ 
\ No newline at end of file
diff --git a/maven-enforcer-plugin/src/it/projects/require-plugin-versions-ci/verify.groovy b/maven-enforcer-plugin/src/it/projects/require-plugin-versions-ci/verify.groovy
index 865bfeb..d098917 100644
--- a/maven-enforcer-plugin/src/it/projects/require-plugin-versions-ci/verify.groovy
+++ b/maven-enforcer-plugin/src/it/projects/require-plugin-versions-ci/verify.groovy
@@ -18,4 +18,4 @@
  */
 File buildLog = new File( basedir, 'build.log' )
 assert buildLog.text.contains( '[WARNING] Rule 0: org.apache.maven.plugins.enforcer.RequirePluginVersions failed with message:' )
-assert buildLog.text.contains( 'Some plugins are missing valid versions:(LATEST RELEASE SNAPSHOT are not allowed )' )
+assert buildLog.text.contains( 'Some plugins are missing valid versions: (LATEST RELEASE SNAPSHOT are not allowed)' )
diff --git a/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/MENFORCER-322-module/pom.xml b/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/MENFORCER-322-module/pom.xml
new file mode 100644
index 0000000..a0540d5
--- /dev/null
+++ b/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/MENFORCER-322-module/pom.xml
@@ -0,0 +1,65 @@
+<?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.

+-->

+

+<!-- MENFORCER-322 -->

+<project>

+  <modelVersion>4.0.0</modelVersion>

+  

+  <parent>

+    <groupId>org.apache.maven.its.enforcer</groupId>

+    <artifactId>test</artifactId>

+    <version>1.0</version>

+    <relativePath>..</relativePath>

+  </parent>

+  

+  <groupId>org.apache.maven.its.enforcer</groupId>

+  <artifactId>menforcer-322-module</artifactId>

+  <version>1.0</version>

+

+  <build>

+    <plugins>

+      <plugin>

+        <groupId>org.apache.maven.plugins</groupId>

+        <artifactId>maven-enforcer-plugin</artifactId>

+        <version>@project.version@</version>

+        <executions>

+          <execution>

+            <id>test</id>

+            <goals>

+              <goal>enforce</goal>

+            </goals>

+            <configuration>

+              <rules>

+                <RequireProfileIdsExist/>

+              </rules>

+            </configuration>

+          </execution>

+        </executions>

+      </plugin>

+    </plugins>

+  </build>

+  

+  <profiles>

+    <profile>

+      <id>b</id>

+    </profile>

+  </profiles>

+</project>

diff --git a/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/invoker.properties b/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/invoker.properties
new file mode 100644
index 0000000..8d46b16
--- /dev/null
+++ b/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/invoker.properties
@@ -0,0 +1,27 @@
+# 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.

+

+# MENFORCER-322

+

+# test building the top level project 

+invoker.goals.1 = clean compile

+invoker.profiles.1 = a,b

+

+# test building the module

+invoker.project.2 = MENFORCER-322-module

+invoker.goals.2 = clean compile

+invoker.profiles.2 = a,b

diff --git a/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/pom.xml b/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/pom.xml
new file mode 100644
index 0000000..21adc5c
--- /dev/null
+++ b/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/pom.xml
@@ -0,0 +1,63 @@
+<?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.

+-->

+

+<!-- MENFORCER-322 -->

+<project>

+  <modelVersion>4.0.0</modelVersion>

+

+  <groupId>org.apache.maven.its.enforcer</groupId>

+  <artifactId>test</artifactId>

+  <version>1.0</version>

+  <packaging>pom</packaging>

+

+  <build>

+    <plugins>

+      <plugin>

+        <groupId>org.apache.maven.plugins</groupId>

+        <artifactId>maven-enforcer-plugin</artifactId>

+        <version>@project.version@</version>

+        <executions>

+          <execution>

+            <id>test</id>

+            <goals>

+              <goal>enforce</goal>

+            </goals>

+            <configuration>

+              <rules>

+                <RequireProfileIdsExist/>

+              </rules>

+            </configuration>

+          </execution>

+        </executions>

+      </plugin>

+    </plugins>

+  </build>

+  

+  <modules>

+    <module>MENFORCER-322-module</module>

+  </modules>

+  

+  <profiles>

+    <profile>

+      <id>a</id>

+    </profile>

+  </profiles>

+</project>

diff --git a/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/verify.groovy b/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/verify.groovy
new file mode 100644
index 0000000..4e58239
--- /dev/null
+++ b/maven-enforcer-plugin/src/it/projects/require-profile-id-exist_defined_in_parent/verify.groovy
@@ -0,0 +1,22 @@
+/*

+ * 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.

+ */

+

+def buildLog = new File( basedir, 'build.log' )

+assert !( buildLog.text.contains( "The requested profile doesn't exist: a" ) )

+assert !( buildLog.text.contains( "The requested profile doesn't exist: b" ) )

diff --git a/maven-enforcer-plugin/src/main/java/org/apache/maven/plugins/enforcer/EnforceMojo.java b/maven-enforcer-plugin/src/main/java/org/apache/maven/plugins/enforcer/EnforceMojo.java
index 3fa2b3b..19bea99 100644
--- a/maven-enforcer-plugin/src/main/java/org/apache/maven/plugins/enforcer/EnforceMojo.java
+++ b/maven-enforcer-plugin/src/main/java/org/apache/maven/plugins/enforcer/EnforceMojo.java
@@ -213,20 +213,34 @@
                     // false if fail is false.
                     if ( failFast && level == EnforcerLevel.ERROR )
                     {
-                        throw new MojoExecutionException( currentRule + " failed with message:\n" + e.getMessage(), e );
+                        throw new MojoExecutionException( currentRule + " failed with message:"
+                            + System.lineSeparator() + e.getMessage(), e );
                     }
                     else
                     {
-                        if ( level == EnforcerLevel.ERROR )
+                        // log a warning in case the exception message is missing
+                        // so that the user can figure out what is going on
+                        final String exceptionMessage = e.getMessage();
+                        if ( exceptionMessage != null )
                         {
-                            hasErrors = true;
-                            list.add( "Rule " + i + ": " + currentRule + " failed with message:\n" + e.getMessage() );
-                            log.debug( "Adding failure due to exception", e );
+                            log.debug( "Adding " + level + " message due to exception", e );
                         }
                         else
                         {
-                            list.add( "Rule " + i + ": " + currentRule + " warned with message:\n" + e.getMessage() );
-                            log.debug( "Adding warning due to exception", e );
+                            log.warn( "Rule " + i + ": " + currentRule + " failed without a message", e );
+                        }
+                        // add the 'failed/warned' message including exceptionMessage
+                        // which might be null in rare cases
+                        if ( level == EnforcerLevel.ERROR )
+                        {
+                            hasErrors = true;
+                            list.add( "Rule " + i + ": " + currentRule + " failed with message:"
+                                 + System.lineSeparator() + exceptionMessage );
+                        }
+                        else
+                        {
+                            list.add( "Rule " + i + ": " + currentRule + " warned with message:"
+                                 + System.lineSeparator() + exceptionMessage );
                         }
                     }
                 }
@@ -349,7 +363,7 @@
 
     protected String createRuleMessage( int i, String currentRule, EnforcerRuleException e )
     {
-        return "Rule " + i + ": " + currentRule + " failed with message:\n" + e.getMessage();
+        return "Rule " + i + ": " + currentRule + " failed with message:" + System.lineSeparator() + e.getMessage();
     }
 
     /**
diff --git a/maven-enforcer-plugin/src/test/java/org/apache/maven/plugins/enforcer/TestEnforceMojo.java b/maven-enforcer-plugin/src/test/java/org/apache/maven/plugins/enforcer/TestEnforceMojo.java
index d3f5f36..c7677d3 100644
--- a/maven-enforcer-plugin/src/test/java/org/apache/maven/plugins/enforcer/TestEnforceMojo.java
+++ b/maven-enforcer-plugin/src/test/java/org/apache/maven/plugins/enforcer/TestEnforceMojo.java
@@ -24,10 +24,14 @@
 import static org.junit.Assert.fail;
 
 import org.apache.maven.enforcer.rule.api.EnforcerRule;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
 import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.logging.Log;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InjectMocks;
+import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
 
 /**
@@ -46,9 +50,7 @@
     public void testEnforceMojo()
         throws MojoExecutionException
     {
-        mojo.setFail( false );
-        mojo.setSession( EnforcerTestUtils.getMavenSession() );
-        mojo.setProject( new MockProject() );
+        setupBasics( false );
 
         try
         {
@@ -101,9 +103,7 @@
     public void testCaching()
         throws MojoExecutionException
     {
-        mojo.setFail( true );
-        mojo.setSession( EnforcerTestUtils.getMavenSession() );
-        mojo.setProject( new MockProject() );
+        setupBasics( true );
 
         MockEnforcerRule[] rules = new MockEnforcerRule[10];
 
@@ -175,9 +175,7 @@
     public void testCachePersistence1()
         throws MojoExecutionException
     {
-        mojo.setFail( true );
-        mojo.setSession( EnforcerTestUtils.getMavenSession() );
-        mojo.setProject( new MockProject() );
+        setupBasics( true );
 
         MockEnforcerRule[] rules = new MockEnforcerRule[10];
 
@@ -198,9 +196,7 @@
     public void testCachePersistence2()
         throws MojoExecutionException
     {
-        mojo.setFail( true );
-        mojo.setSession( EnforcerTestUtils.getMavenSession() );
-        mojo.setProject( new MockProject() );
+        setupBasics( true );
 
         MockEnforcerRule[] rules = new MockEnforcerRule[10];
 
@@ -230,9 +226,7 @@
         {
         }
 
-        mojo.setFail( true );
-        mojo.setSession( EnforcerTestUtils.getMavenSession() );
-        mojo.setProject( new MockProject() );
+        setupBasics( true );
 
         MockEnforcerRule[] rules = new MockEnforcerRule[10];
 
@@ -247,4 +241,72 @@
         assertFalse( "Expected this rule not to be executed.", rules[1].executed );
 
     }
+
+    @Test
+    public void testLoggingOnEnforcerRuleExceptionWithMessage()
+        throws MojoExecutionException, EnforcerRuleException
+    {
+        // fail=false because this is out of scope here (also allows for cleaner test code without catch block)
+        setupBasics( false );
+
+        // the regular kind of EnforcerRuleException:
+        EnforcerRuleException ruleException = new EnforcerRuleException( "testMessage" );
+
+        EnforcerRule ruleMock = Mockito.mock( EnforcerRule.class );
+        Mockito.doThrow( ruleException ).when( ruleMock ).execute( Mockito.any( EnforcerRuleHelper.class ) );
+
+        mojo.setRules( new EnforcerRule[] { ruleMock } );
+
+        Log logSpy = setupLogSpy();
+
+        mojo.execute();
+
+        Mockito.verify( logSpy ).debug(
+                Mockito.anyString() , Mockito.same( ruleException ) );
+
+        Mockito.verify( logSpy, Mockito.never() ).warn(
+                Mockito.anyString(), Mockito.any( Throwable.class ) );
+
+        Mockito.verify( logSpy ).warn(
+                Mockito.matches( ".* failed with message:" + System.lineSeparator() + ruleException.getMessage() ) );
+    }
+
+    @Test
+    public void testLoggingOnEnforcerRuleExceptionWithoutMessage()
+        throws MojoExecutionException, EnforcerRuleException
+    {
+        // fail=false because this is out of scope here (also allows for cleaner test code without catch block)
+        setupBasics( false );
+
+        // emulate behaviour of various rules that just catch Exception and wrap into EnforcerRuleException:
+        NullPointerException npe = new NullPointerException();
+        EnforcerRuleException enforcerRuleException = new EnforcerRuleException( npe.getLocalizedMessage(), npe );
+
+        EnforcerRule ruleMock = Mockito.mock( EnforcerRule.class );
+        Mockito.doThrow( enforcerRuleException ).when( ruleMock ).execute( Mockito.any( EnforcerRuleHelper.class ) );
+
+        mojo.setRules( new EnforcerRule[] { ruleMock } );
+
+        Log logSpy = setupLogSpy();
+
+        mojo.execute();
+
+        Mockito.verify( logSpy ).warn(
+                Mockito.contains("failed without a message"), Mockito.same( enforcerRuleException ) );
+
+        Mockito.verify( logSpy ).warn(
+                Mockito.matches( ".* failed with message:" + System.lineSeparator() + "null" ) );
+    }
+
+    private void setupBasics( boolean fail ) {
+        mojo.setFail( fail );
+        mojo.setSession( EnforcerTestUtils.getMavenSession() );
+        mojo.setProject( new MockProject() );
+    }
+
+    private Log setupLogSpy() {
+        Log spy = Mockito.spy( mojo.getLog() );
+        mojo.setLog( spy );
+        return spy;
+    }
 }
diff --git a/pom.xml b/pom.xml
index ce27fe1..f6c9ea8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
   </parent>
   <groupId>org.apache.maven.enforcer</groupId>
   <artifactId>enforcer</artifactId>
-  <version>3.0.0-SNAPSHOT</version>
+  <version>3.0.0-M4-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>Apache Maven Enforcer</name>
@@ -65,6 +65,7 @@
   <properties>
     <maven.version>3.0</maven.version>
     <maven.site.path>enforcer-archives/enforcer-LATEST</maven.site.path>
+    <javaVersion>7</javaVersion>
   </properties>
 
   <dependencyManagement>
@@ -110,12 +111,12 @@
       <dependency>
         <groupId>org.apache.maven.shared</groupId>
         <artifactId>maven-common-artifact-filters</artifactId>
-        <version>3.0.1</version>
+        <version>3.1.0</version>
       </dependency>
       <dependency>
         <groupId>org.codehaus.plexus</groupId>
         <artifactId>plexus-utils</artifactId>
-        <version>3.1.0</version>
+        <version>3.3.0</version>
       </dependency>
       <dependency>
         <groupId>junit</groupId>
@@ -125,18 +126,18 @@
       <dependency>
         <groupId>org.mockito</groupId>
         <artifactId>mockito-core</artifactId>
-        <version>2.18.3</version>
+        <version>2.28.2</version>
         <scope>test</scope>
       </dependency>
       <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-lang3</artifactId>
-        <version>3.5</version> <!-- commons-lang3 >= 3.6 require at least Java 7 -->
+        <version>3.8.1</version> <!-- commons-lang3 >= 3.8.1 require at least Java 8 -->
       </dependency>
       <dependency>
         <groupId>commons-codec</groupId>
         <artifactId>commons-codec</artifactId>
-        <version>1.6</version>
+        <version>1.12</version>
       </dependency>
       <dependency>
         <groupId>org.apache.maven.plugin-testing</groupId>
@@ -150,6 +151,11 @@
         <version>2.2</version>
       </dependency>
       <dependency>
+        <groupId>org.apache.maven.resolver</groupId>
+        <artifactId>maven-resolver-util</artifactId>
+        <version>1.4.1</version>
+      </dependency>
+      <dependency>
         <groupId>org.assertj</groupId>
         <artifactId>assertj-core</artifactId>
         <version>1.7.1</version>
@@ -191,7 +197,7 @@
           <dependency>
             <groupId>org.codehaus.mojo</groupId>
             <artifactId>extra-enforcer-rules</artifactId>
-            <version>1.0-beta-7</version>
+            <version>1.2</version>
           </dependency>
         </dependencies>
       </plugin>