You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by kh...@apache.org on 2014/03/17 21:56:06 UTC

svn commit: r1578567 - in /maven/enforcer/trunk/enforcer-rules/src: main/java/org/apache/maven/plugins/enforcer/ site/apt/ test/java/org/apache/maven/plugins/enforcer/

Author: khmarbaise
Date: Mon Mar 17 20:56:06 2014
New Revision: 1578567

URL: http://svn.apache.org/r1578567
Log:
[MENFORCER-186]
 - Renamed rule into ReactorModuleConvergence
   in accordance with the discussion on the mailing list.
 - Added ignoreModuleDependencies option.

Added:
    maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/ReactorModuleConvergence.java
    maven/enforcer/trunk/enforcer-rules/src/site/apt/reactorModuleConvergence.apt.vm
    maven/enforcer/trunk/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/ReactorModuleConvergenceTest.java
Removed:
    maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactor.java
    maven/enforcer/trunk/enforcer-rules/src/site/apt/requireSameVersionsReactor.apt.vm
    maven/enforcer/trunk/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactorTest.java
Modified:
    maven/enforcer/trunk/enforcer-rules/src/site/apt/index.apt

Added: maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/ReactorModuleConvergence.java
URL: http://svn.apache.org/viewvc/maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/ReactorModuleConvergence.java?rev=1578567&view=auto
==============================================================================
--- maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/ReactorModuleConvergence.java (added)
+++ maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/ReactorModuleConvergence.java Mon Mar 17 20:56:06 2014
@@ -0,0 +1,427 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.commons.lang.SystemUtils;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+
+/**
+ * This rule will check if a multi module build will follow the best practices.
+ * 
+ * @author Karl-Heinz Marbaise
+ * @since 1.3.2
+ */
+public class ReactorModuleConvergence
+    extends AbstractNonCacheableEnforcerRule
+{
+    private boolean ignoreModuleDependencies = false;
+
+    private Log logger;
+
+    public void execute( EnforcerRuleHelper helper )
+        throws EnforcerRuleException
+    {
+        logger = helper.getLog();
+
+        MavenSession session;
+        try
+        {
+            session = (MavenSession) helper.evaluate( "${session}" );
+        }
+        catch ( ExpressionEvaluationException eee )
+        {
+            throw new EnforcerRuleException( "Unable to retrieve the MavenSession: ", eee );
+        }
+
+        @SuppressWarnings( "unchecked" )
+        List<MavenProject> sortedProjects = session.getSortedProjects();
+        if ( sortedProjects != null && !sortedProjects.isEmpty() )
+        {
+            checkReactor( sortedProjects );
+            checkParentsInReactor( sortedProjects );
+            checkMissingParentsInReactor( sortedProjects );
+            checkParentsPartOfTheReactor( sortedProjects );
+            if ( !isIgnoreModuleDependencies() )
+            {
+                checkDependenciesWithinReactor( sortedProjects );
+            }
+        }
+
+    }
+
+    private void checkParentsPartOfTheReactor( List<MavenProject> sortedProjects )
+        throws EnforcerRuleException
+    {
+        List<MavenProject> parentsWhichAreNotPartOfTheReactor =
+            existParentsWhichAreNotPartOfTheReactor( sortedProjects );
+        if ( !parentsWhichAreNotPartOfTheReactor.isEmpty() )
+        {
+            StringBuilder sb = new StringBuilder().append( SystemUtils.LINE_SEPARATOR );
+            for ( MavenProject mavenProject : parentsWhichAreNotPartOfTheReactor )
+            {
+                sb.append( " module: " );
+                sb.append( mavenProject.getId() );
+                sb.append( SystemUtils.LINE_SEPARATOR );
+            }
+            throw new EnforcerRuleException( "Module parents have been found which could not be found in the reactor."
+                + sb.toString() );
+        }
+    }
+
+    /**
+     * Convenience method to create a user readable message.
+     * 
+     * @param sortedProjects The list of reactor projects.
+     * @throws EnforcerRuleException In case of a violation.
+     */
+    private void checkMissingParentsInReactor( List<MavenProject> sortedProjects )
+        throws EnforcerRuleException
+    {
+        List<MavenProject> modulesWithoutParentsInReactory = existModulesWithoutParentsInReactor( sortedProjects );
+        if ( !modulesWithoutParentsInReactory.isEmpty() )
+        {
+            StringBuilder sb = new StringBuilder().append( SystemUtils.LINE_SEPARATOR );
+            for ( MavenProject mavenProject : modulesWithoutParentsInReactory )
+            {
+                sb.append( " module: " );
+                sb.append( mavenProject.getId() );
+                sb.append( SystemUtils.LINE_SEPARATOR );
+            }
+            throw new EnforcerRuleException( "Reactor contains modules without parents." + sb.toString() );
+        }
+    }
+
+    private void checkDependenciesWithinReactor( List<MavenProject> sortedProjects )
+        throws EnforcerRuleException
+    {
+        //TODO: After we are sure having consistent version we can simply use the first one?
+        String reactorVersion = sortedProjects.get( 0 ).getVersion();
+
+        Map<MavenProject, List<Dependency>> areThereDependenciesWhichAreNotPartOfTheReactor =
+            areThereDependenciesWhichAreNotPartOfTheReactor( reactorVersion, sortedProjects );
+        if ( !areThereDependenciesWhichAreNotPartOfTheReactor.isEmpty() )
+        {
+            StringBuilder sb = new StringBuilder().append( SystemUtils.LINE_SEPARATOR );
+            for ( Entry<MavenProject, List<Dependency>> item : areThereDependenciesWhichAreNotPartOfTheReactor.entrySet() )
+            {
+                sb.append( " module: " );
+                sb.append( item.getKey().getId() );
+                sb.append( SystemUtils.LINE_SEPARATOR );
+                for ( Dependency dependency : item.getValue() )
+                {
+                    String id =
+                        dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getVersion();
+                    sb.append( "    dependency: " );
+                    sb.append( id );
+                    sb.append( SystemUtils.LINE_SEPARATOR );
+                }
+            }
+            throw new EnforcerRuleException(
+                                             "Reactor modules contains dependencies which do not reference the reactor."
+                                                 + sb.toString() );
+        }
+    }
+
+    /**
+     * Convenience method to create a user readable message.
+     * 
+     * @param sortedProjects The list of reactor projects.
+     * @throws EnforcerRuleException In case of a violation.
+     */
+    private void checkParentsInReactor( List<MavenProject> sortedProjects )
+        throws EnforcerRuleException
+    {
+        //TODO: After we are sure having consistent version we can simply use the first one?
+        String reactorVersion = sortedProjects.get( 0 ).getVersion();
+
+        List<MavenProject> areParentsFromTheReactor = areParentsFromTheReactor( reactorVersion, sortedProjects );
+        if ( !areParentsFromTheReactor.isEmpty() )
+        {
+            StringBuilder sb = new StringBuilder().append( SystemUtils.LINE_SEPARATOR );
+            for ( MavenProject mavenProject : areParentsFromTheReactor )
+            {
+                sb.append( " --> " );
+                sb.append( mavenProject.getId() + " parent:" + mavenProject.getParent().getId() );
+                sb.append( SystemUtils.LINE_SEPARATOR );
+            }
+            throw new EnforcerRuleException( "Reactor modules have parents which contain a wrong version."
+                + sb.toString() );
+        }
+    }
+
+    /**
+     * Convenience method to create user readable message.
+     * 
+     * @param sortedProjects The list of reactor projects.
+     * @throws EnforcerRuleException In case of a violation.
+     */
+    private void checkReactor( List<MavenProject> sortedProjects )
+        throws EnforcerRuleException
+    {
+        List<MavenProject> consistenceCheckResult = isReactorVersionConsistent( sortedProjects );
+        if ( !consistenceCheckResult.isEmpty() )
+        {
+            StringBuilder sb = new StringBuilder().append( SystemUtils.LINE_SEPARATOR );
+            for ( MavenProject mavenProject : consistenceCheckResult )
+            {
+                sb.append( " --> " );
+                sb.append( mavenProject.getId() );
+                sb.append( SystemUtils.LINE_SEPARATOR );
+            }
+            throw new EnforcerRuleException( "The reactor contains different versions." + sb.toString() );
+        }
+    }
+
+    private List<MavenProject> areParentsFromTheReactor( String reactorVersion, List<MavenProject> sortedProjects )
+    {
+        List<MavenProject> result = new ArrayList<MavenProject>();
+
+        for ( MavenProject mavenProject : sortedProjects )
+        {
+            logger.debug( "Project: " + mavenProject.getId() );
+            if ( hasParent( mavenProject ) )
+            {
+                if ( !mavenProject.isExecutionRoot() )
+                {
+                    MavenProject parent = mavenProject.getParent();
+                    if ( !reactorVersion.equals( parent.getVersion() ) )
+                    {
+                        logger.debug( "The project: " + mavenProject.getId()
+                            + " has a parent which version does not match the other elements in reactor" );
+                        result.add( mavenProject );
+                    }
+                }
+            }
+            else
+            {
+                //This situation is currently ignored, cause it's handled by existModulesWithoutParentsInReactor()
+            }
+        }
+
+        return result;
+    }
+
+    private List<MavenProject> existParentsWhichAreNotPartOfTheReactor( List<MavenProject> sortedProjects )
+    {
+        List<MavenProject> result = new ArrayList<MavenProject>();
+
+        for ( MavenProject mavenProject : sortedProjects )
+        {
+            logger.debug( "Project: " + mavenProject.getId() );
+            if ( hasParent( mavenProject ) )
+            {
+                if ( !mavenProject.isExecutionRoot() )
+                {
+                    MavenProject parent = mavenProject.getParent();
+                    if ( !isProjectPartOfTheReactor( parent, sortedProjects ) )
+                    {
+                        result.add( mavenProject );
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * This will check of the groupId/artifactId can be found in any reactor project. The version will be ignored cause
+     * versions are checked before.
+     * 
+     * @param project The project which should be checked if it is contained in the sortedProjects.
+     * @param sortedProjects The list of existing projects.
+     * @return true if the project has been found within the list false otherwise.
+     */
+    private boolean isProjectPartOfTheReactor( MavenProject project, List<MavenProject> sortedProjects )
+    {
+        return isGAPartOfTheReactor( project.getGroupId(), project.getArtifactId(), sortedProjects );
+    }
+
+    private boolean isDependencyPartOfTheReactor( Dependency dependency, List<MavenProject> sortedProjects )
+    {
+        return isGAPartOfTheReactor( dependency.getGroupId(), dependency.getArtifactId(), sortedProjects );
+    }
+
+    private boolean isGAPartOfTheReactor( String groupId, String artifactId, List<MavenProject> sortedProjects )
+    {
+        boolean result = false;
+        for ( MavenProject mavenProject : sortedProjects )
+        {
+            String parentId = groupId + ":" + artifactId;
+            String projectId = mavenProject.getGroupId() + ":" + mavenProject.getArtifactId();
+            if ( parentId.equals( projectId ) )
+            {
+                result = true;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Assume we have a module which is which is a child of a multi module build but this child does not have a parent.
+     * This method will exactly search for such cases.
+     * 
+     * @param projectList The sorted list of the reactor modules.
+     * @return The resulting list will contain the modules in the reactor which do not have a parent. The list will
+     *         never null. If the list is empty no violation have happened.
+     */
+    private List<MavenProject> existModulesWithoutParentsInReactor( List<MavenProject> sortedProjects )
+    {
+        List<MavenProject> result = new ArrayList<MavenProject>();
+
+        for ( MavenProject mavenProject : sortedProjects )
+        {
+            logger.debug( "Project: " + mavenProject.getId() );
+            if ( !hasParent( mavenProject ) )
+            {
+                if ( mavenProject.isExecutionRoot() )
+                {
+                    logger.debug( "The root does not need having a parent." );
+                }
+                else
+                {
+                    logger.debug( "The module: " + mavenProject.getId() + " has no parent." );
+                    result.add( mavenProject );
+                }
+            }
+        }
+
+        return result;
+    }
+
+    private void addDep( Map<MavenProject, List<Dependency>> result, MavenProject project, Dependency dependency )
+    {
+        if ( result.containsKey( project ) )
+        {
+            List<Dependency> list = result.get( project );
+            if ( list == null )
+            {
+                list = new ArrayList<Dependency>();
+            }
+            list.add( dependency );
+            result.put( project, list );
+        }
+        else
+        {
+            List<Dependency> list = new ArrayList<Dependency>();
+            list.add( dependency );
+            result.put( project, list );
+        }
+    }
+
+    private Map<MavenProject, List<Dependency>> areThereDependenciesWhichAreNotPartOfTheReactor( String reactorVersion,
+                                                                                                 List<MavenProject> sortedProjects )
+    {
+        Map<MavenProject, List<Dependency>> result = new HashMap<MavenProject, List<Dependency>>();
+        for ( MavenProject mavenProject : sortedProjects )
+        {
+            logger.debug( "Project: " + mavenProject.getId() );
+
+            @SuppressWarnings( "unchecked" )
+            List<Dependency> dependencies = mavenProject.getDependencies();
+            if ( hasDependencies( dependencies ) )
+            {
+                for ( Dependency dependency : dependencies )
+                {
+                    if ( isDependencyPartOfTheReactor( dependency, sortedProjects ) )
+                    {
+                        if ( !dependency.getVersion().equals( reactorVersion ) )
+                        {
+                            addDep( result, mavenProject, dependency );
+                        }
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * This method will check the following situation within a multi-module build.
+     * 
+     * <pre>
+     *  &lt;parent&gt;
+     *    &lt;groupId&gt;...&lt;/groupId&gt;
+     *    &lt;artifactId&gt;...&lt;/artifactId&gt;
+     *    &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
+     *  &lt;/parent&gt;
+     *  
+     *  &lt;version&gt;1.1-SNAPSHOT&lt;/version&gt;
+     * </pre>
+     * 
+     * @param projectList The sorted list of the reactor modules.
+     * @return The resulting list will contain the modules in the reactor which do the thing in the example above. The
+     *         list will never null. If the list is empty no violation have happened.
+     */
+    private List<MavenProject> isReactorVersionConsistent( List<MavenProject> projectList )
+    {
+        List<MavenProject> result = new ArrayList<MavenProject>();
+
+        if ( projectList != null && !projectList.isEmpty() )
+        {
+            //TODO: Check if this the right choice ?
+            String version = projectList.get( 0 ).getVersion();
+            logger.debug( "First version:" + version );
+            for ( MavenProject mavenProject : projectList )
+            {
+                logger.debug( " -> checking " + mavenProject.getId() );
+                if ( !version.equals( mavenProject.getVersion() ) )
+                {
+                    result.add( mavenProject );
+                }
+            }
+        }
+        return result;
+    }
+
+    private boolean hasDependencies( List<Dependency> dependencies )
+    {
+        return dependencies != null && !dependencies.isEmpty();
+    }
+
+    private boolean hasParent( MavenProject mavenProject )
+    {
+        return mavenProject.getParent() != null;
+    }
+
+    public boolean isIgnoreModuleDependencies()
+    {
+        return ignoreModuleDependencies;
+    }
+
+    public void setIgnoreModuleDependencies( boolean ignoreModuleDependencies )
+    {
+        this.ignoreModuleDependencies = ignoreModuleDependencies;
+    }
+
+}
\ No newline at end of file

Modified: maven/enforcer/trunk/enforcer-rules/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/maven/enforcer/trunk/enforcer-rules/src/site/apt/index.apt?rev=1578567&r1=1578566&r2=1578567&view=diff
==============================================================================
--- maven/enforcer/trunk/enforcer-rules/src/site/apt/index.apt (original)
+++ maven/enforcer/trunk/enforcer-rules/src/site/apt/index.apt Mon Mar 17 20:56:06 2014
@@ -71,10 +71,10 @@ Standard Rules
 
   * {{{./requireSameVersions.html}requireSameVersions}} - enforces that specific dependencies and/or plugins have the same version.
 
-  * {{{./requireSameVersionsReactor.html}requireSameVersionsReactor}} - enforces that a multi module build follows best practice.
-
   * {{{./requireUpperBoundDeps.html}requireUpperBoundDeps}} - ensures that every (transitive) dependency is resolved to it's specified version or higher.
 
+  * {{{./reactorModuleConvergence.html}reactorModuleConvergence}} - enforces that a multi module build follows best practice.
+
   []
 
   You may also create and inject your own custom rules by following the {{{http://maven.apache.org/enforcer/enforcer-api/writing-a-custom-rule.html}maven-enforcer-rule-api}} instructions.

Added: maven/enforcer/trunk/enforcer-rules/src/site/apt/reactorModuleConvergence.apt.vm
URL: http://svn.apache.org/viewvc/maven/enforcer/trunk/enforcer-rules/src/site/apt/reactorModuleConvergence.apt.vm?rev=1578567&view=auto
==============================================================================
--- maven/enforcer/trunk/enforcer-rules/src/site/apt/reactorModuleConvergence.apt.vm (added)
+++ maven/enforcer/trunk/enforcer-rules/src/site/apt/reactorModuleConvergence.apt.vm Mon Mar 17 20:56:06 2014
@@ -0,0 +1,254 @@
+~~ 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.    
+ 
+  ------
+  Reactor Module Convergence
+  ------
+  Karl-Heinz Marbaise
+  ------
+  March 2014
+  ------
+
+Reactor Module Convergence
+
+  This rule checks that the versions within the reactor are consistent furthermore
+  it will check that every module within the project contains a parent and that the 
+  parent is part of the reactor build. Furthermore it will be checked if dependencies
+  are intermodule dependencies that they using the same version as given by the reactor. 
+
+  The following parameters are supported by this rule:
+   
+  * message - an optional message to the user if the rule fails.
+  
+  * ignoreModuleDependencies - Ignore module dependencies which references modules within the 
+    the reactor (default: false). 
+    
+   []
+
+   
+  Sample Plugin Configuration:
+  
++---+
+<project>
+  [...]
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-enforcer-plugin</artifactId>
+        <version>${project.version}</version>
+        <executions>
+          <execution>
+            <id>enforce-no-snapshots</id>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <configuration>
+              <rules>
+                <reactorModuleConvergence>
+                  <message>The reactor is not valid</message>
+                  <ignoreModuleDependencies>true</ignoreModuleDependencies>
+                </reactorModuleConvergence>
+              </rules>
+              <fail>true</fail>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+  [...]
+</project>
++---+
+
+  There are different situations within a multi module build which can lead to problems
+  (for example not working with maven-release-plugin etc.).
+  This rule is intended to prevent such problems.
+  
+  Let us assume we have the following (simple) project structure for a multi module setup.
+
++-----
+   root (pom.xml)
+     +--- module1 (pom.xml)
+     +--- module2 (pom.xml)
++-----
+
+  The root <<pom.xml>> looks like this:
+  
++-----
+  <groupId>com.mycompany.project</groupId>
+  <artifactId>parent</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  (..)
++-----
+
+  The best practice in Maven is that all childs inherit the version from their parent 
+  and don't define a new version which looks like this:
+  
++-----
+  <parent>
+    <groupId>...</groupId>
+    <artifactId>...</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>module1</artifactId>
+  (..)
++-----  
+
+  But sometimes people mistaken things or violate the best-practice which  
+  looks like this:
+
++-----
+  <parent>
+    <groupId>...</groupId>
+    <artifactId>...</artifactId>
+    <version>1.0-SNAPSHOT</version>
+  </parent>
+  
+  <artifactId>module1</artifactId>
+  <version>1.1-SNAPSHOT</version>
++-----
+
+  By using this rule you would get a message during the build 
+  with the following resulting output:
+
++-----
+[WARNING] Rule 0: org.apache.maven.plugins.enforcer.ReactorModuleConvergence failed with message:
+The reactor contains different versions.
+ --> com.mycompany.project:myproject:pom:1.1-SNAPSHOT
++-----
+
+  The next which happens is that the parent in a reactor is sometimes 
+  the wrong one like the following situation:
+  
++-----
+  <parent>
+    <groupId>...</groupId>
+    <artifactId>...</artifactId>
+    <version>1.1-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>module1</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  (..)
++-----  
+
+  This will prompted by the following message:
+  
++-----
+[WARNING] Rule 0: org.apache.maven.plugins.enforcer.ReactorModuleConvergence failed with message:
+Reactor modules have parents which contain a wrong version.
+ --> com.mycompany.project:myproject:pom:1.1-SNAPSHOT parent:com.mycompany.project:myproject:pom:1.0-SNAPSHOT
++-----
+
+  If you have only changed a parent by accident with the wrong version 
+  like this:
+  
++-----
+  <parent>
+    <groupId>...</groupId>
+    <artifactId>...</artifactId>
+    <version>1.1-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>module1</artifactId>
+  (..)
++-----  
+  
+  you will get the same message as above:
+  
++-----
+[WARNING] Rule 0: org.apache.maven.plugins.enforcer.ReactorModuleConvergence failed with message:
+The reactor contains different versions.
+ --> com.mycompany.project:myproject:pom:1.1-SNAPSHOT
++-----
+
+  An other things which happens that simply the parent will be forgotten which
+  produces a message like this:
+
++----
+[WARNING] Rule 0: org.apache.maven.plugins.enforcer.ReactorModuleConvergence failed with message:
+Reactor contains modules without parents.
+ module: com.mycompany.project:myproject:pom:1.2-SNAPSHOT
++----
+
+  In larger mutli-module builds it happens also that the defined parent is given
+  but does not belong to the reactor like this:
+  
++-----
+  <parent>
+    <groupId>org.apache.enforcer</groupId>
+    <artifactId>something-different</artifactId>
+    <version>1.1</version>
+  </parent>
+
+  <artifactId>module1</artifactId>
+  (..)
++-----  
+  
+  Usually already the Maven warning like this should be paid attention to:
+  
++-----
+[WARNING] 
+[WARNING] Some problems were encountered while building the effective model for org.apache.enforcer:pom:1.0.4-SNAPSHOT
+[WARNING] 'parent.relativePath' points at org.apache.enforcer:something-different instead of org.apache.enforcer:something-different, please verify your project structure @ line 7, column 11
+[WARNING] 
++-----
+
+  but this will oversight often. So the enforcer rule will break simply
+  such mail formed build via the message (This required that the parent
+  has the same version as the rest of the build which happens):
+   
++-----
+[WARNING] Rule 0: org.apache.maven.plugins.enforcer.ReactorModuleConvergence failed with message:
+Module parents have been found which could not be found in the reactor.
+ module: org.apache.enforcer:something-different:pom:1.0.4-SNAPSHOT
++-----
+
+
+  An other case which happens (for example by merging from a branch into trunk/master)
+  is that an intermodule dependency contains the wrong version like this:
+
++-----
+  <parent>
+    <groupId>...</groupId>
+    <artifactId>...</artifactId>
+    <version>1.2-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>module1</artifactId>
+  
+  <dependencies>
+    <dependency>
+      <groupId>com.mycompany.project</groupId>
+      <artifactId>myproject</artifactId>
+      <version>1.1</version>
+    </dependency>
+  (..)
+  </dependencies>
+  (..)
++-----
+ 
+  This will result in the following message:
+
++-----
+[WARNING] Rule 0: org.apache.maven.plugins.enforcer.ReactorModuleConvergence failed with message:
+Reactor modules contains dependencies which do not reference the reactor.
+ module: com.mycompany.project:myproject-x:jar:1.2-SNAPSHOT
+    dependency: com.mycompany.project:myproject:1.1
++-----

Added: maven/enforcer/trunk/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/ReactorModuleConvergenceTest.java
URL: http://svn.apache.org/viewvc/maven/enforcer/trunk/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/ReactorModuleConvergenceTest.java?rev=1578567&view=auto
==============================================================================
--- maven/enforcer/trunk/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/ReactorModuleConvergenceTest.java (added)
+++ maven/enforcer/trunk/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/ReactorModuleConvergenceTest.java Mon Mar 17 20:56:06 2014
@@ -0,0 +1,341 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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 org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Check requireSameVersionsReactor rule.
+ * 
+ * @author <a href="mailto:khmarbaise@apache.org">Karl Heinz Marbaise</a>
+ */
+public class ReactorModuleConvergenceTest
+{
+    private MavenProject project;
+
+    private MavenSession session;
+
+    private EnforcerRuleHelper helper;
+
+    private ReactorModuleConvergence rule;
+
+    @Before
+    public void before()
+        throws ExpressionEvaluationException
+    {
+        project = mock( MavenProject.class );
+        session = mock( MavenSession.class );
+        helper = mock( EnforcerRuleHelper.class );
+        when( helper.evaluate( "${project}" ) ).thenReturn( project );
+        when( helper.evaluate( "${session}" ) ).thenReturn( session );
+        when( helper.getLog() ).thenReturn( mock( Log.class ) );
+
+        rule = new ReactorModuleConvergence();
+    }
+
+    @Test
+    public void shouldNotFailWithNoProject()
+        throws EnforcerRuleException
+    {
+        when( session.getSortedProjects() ).thenReturn( Collections.<MavenProject> emptyList() );
+
+        rule.execute( helper );
+
+        //intentionally only assertTrue cause we don't expect an exception.
+        assertTrue( true );
+    }
+
+    @Test
+    public void shouldNotFailWithAValidProject()
+        throws EnforcerRuleException, ExpressionEvaluationException
+    {
+        MavenProject mp1 = createProjectParent();
+        MavenProject mp2 = createProjectChild1( mp1 );
+        MavenProject mp3 = createProjectChild2( mp1 );
+
+        assertTrue( mp2.getParent() == mp1 );
+        assertTrue( mp3.getParent() == mp1 );
+
+        List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 );
+        when( session.getSortedProjects() ).thenReturn( theList );
+
+        rule.execute( helper );
+
+        //intentionally only assertTrue cause we don't expect an exception.
+        assertTrue( true );
+    }
+
+    @Test( expected = EnforcerRuleException.class )
+    public void shouldFailWithWrongVersionInOneChild()
+        throws EnforcerRuleException, ExpressionEvaluationException
+    {
+        MavenProject mp1 = createProjectParent();
+        MavenProject mp2 = createProjectChild1( mp1 );
+        MavenProject mp3 = createProjectChild2WithWrongVersion( mp1 );
+
+        List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 );
+        when( session.getSortedProjects() ).thenReturn( theList );
+
+        rule.execute( helper );
+
+        //intentionally no assertTrue() cause we expect getting an exception.
+    }
+
+    @Test( expected = EnforcerRuleException.class )
+    public void shouldFailWithWrongParent()
+        throws EnforcerRuleException, ExpressionEvaluationException
+    {
+        MavenProject mp1 = createProjectParent();
+
+        MavenProject wrongParentVerison = mock( MavenProject.class );
+        when( wrongParentVerison.getGroupId() ).thenReturn( "org.apache.enforcer" );
+        when( wrongParentVerison.getArtifactId() ).thenReturn( "m1" );
+        when( wrongParentVerison.getVersion() ).thenReturn( "1.1-SNAPSHOT" );
+        when( wrongParentVerison.getId() ).thenReturn( "org.apache.enforcer:m1:jar:1.1-SNAPSHOT" );
+        when( wrongParentVerison.getDependencies() ).thenReturn( Collections.emptyList() );
+
+        MavenProject mp2 = createProjectChild2( wrongParentVerison );
+        MavenProject mp3 = createProjectChild2( mp1 );
+
+        List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 );
+        when( session.getSortedProjects() ).thenReturn( theList );
+
+        rule.execute( helper );
+
+        //intentionally no assertTrue() cause we expect getting an exception.
+    }
+
+    @Test
+    public void shouldNotFailWithACompanyParent()
+        throws EnforcerRuleException, ExpressionEvaluationException
+    {
+        MavenProject companyParent = createCompanyParent();
+        MavenProject mp1 = createProjectParent( companyParent );
+
+        MavenProject mp2 = createProjectChild1( mp1 );
+        MavenProject mp3 = createProjectChild2( mp1 );
+
+        List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 );
+        when( session.getSortedProjects() ).thenReturn( theList );
+
+        rule.execute( helper );
+
+        //intentionally only assertTrue cause we don't expect an exception.
+        assertTrue( true );
+    }
+
+    @Test( expected = EnforcerRuleException.class )
+    public void shouldFailWithMissingParentsInReactory()
+        throws EnforcerRuleException, ExpressionEvaluationException
+    {
+        MavenProject mp1 = createProjectParent();
+        MavenProject mp2 = createProjectChild1( mp1 );
+        MavenProject mp3 = createProjectChild2( null );
+
+        List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 );
+        when( session.getSortedProjects() ).thenReturn( theList );
+
+        rule.execute( helper );
+
+        //intentionally only assertTrue cause we don't expect an exception.
+        assertTrue( true );
+    }
+
+    @Test( expected = EnforcerRuleException.class )
+    public void shouldFailWithAParentWhichIsNotPartOfTheReactory()
+        throws EnforcerRuleException, ExpressionEvaluationException
+    {
+        MavenProject mp1 = createProjectParent();
+
+        MavenProject wrongParentVerison = mock( MavenProject.class );
+        when( wrongParentVerison.getGroupId() ).thenReturn( "org.apache" );
+        when( wrongParentVerison.getArtifactId() ).thenReturn( "m1" );
+        when( wrongParentVerison.getVersion() ).thenReturn( "1.0-SNAPSHOT" );
+        when( wrongParentVerison.getId() ).thenReturn( "org.apache.enforcer:m1:jar:1.0-SNAPSHOT" );
+        when( wrongParentVerison.getDependencies() ).thenReturn( Collections.emptyList() );
+
+        MavenProject mp2 = createProjectChild2( wrongParentVerison );
+        MavenProject mp3 = createProjectChild2( mp1 );
+
+        List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 );
+        when( session.getSortedProjects() ).thenReturn( theList );
+
+        rule.execute( helper );
+
+        //intentionally no assertTrue() cause we expect getting an exception.
+    }
+
+    @Test
+    public void shouldNotFailWithDependencyInReactory()
+        throws EnforcerRuleException, ExpressionEvaluationException
+    {
+        MavenProject mp1 = createProjectParent();
+        MavenProject mp2 = createProjectChild1( mp1 );
+
+        Dependency goodDependency = createDependency( "org.junit", "junit", "2.0" );
+        List<Dependency> depListMP2 = Arrays.asList( goodDependency );
+        when( mp2.getDependencies() ).thenReturn( depListMP2 );
+
+        MavenProject mp3 = createProjectChild2( mp1 );
+        Dependency dep1_MP3 = createDependency( "org.apache.commons", "commons-io", "1.0.4" );
+        List<Dependency> depListMP3 = Arrays.asList( dep1_MP3 );
+        when( mp3.getDependencies() ).thenReturn( depListMP3 );
+
+        List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 );
+        when( session.getSortedProjects() ).thenReturn( theList );
+
+        rule.execute( helper );
+
+        //intentionally no assertTrue() cause we expect getting an exception.
+        assertTrue( true );
+    }
+
+    @Test( expected = EnforcerRuleException.class )
+    public void shouldFailWithWrongDependencyInReactory()
+        throws EnforcerRuleException, ExpressionEvaluationException
+    {
+        MavenProject mp1 = createProjectParent();
+        MavenProject mp2 = createProjectChild1( mp1 );
+
+        Dependency goodDependency = createDependency( "org.junit", "junit", "2.0" );
+        
+        Dependency wrongDepFromReactory = createDependency( "org.apache.enforcer", "m2", "1.1-SNAPSHOT" );
+        List<Dependency> depList = Arrays.asList( goodDependency, wrongDepFromReactory );
+        when( mp2.getDependencies() ).thenReturn( depList );
+
+        MavenProject mp3 = createProjectChild2( mp1 );
+
+        List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 );
+        when( session.getSortedProjects() ).thenReturn( theList );
+
+        rule.execute( helper );
+
+        //intentionally no assertTrue() cause we expect getting an exception.
+    }
+
+    /**
+     * This small setup is equivalent to the following situation:
+     * 
+     * <pre>
+     *  &lt;parent&gt;
+     *    &lt;groupId&gt;...&lt;/groupId&gt;
+     *    &lt;artifactId&gt;...&lt;/artifactId&gt;
+     *    &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
+     *  &lt;/parent&gt;
+     *  
+     *  &lt;version&gt;1.1-SNAPSHOT&lt;/version&gt;
+     * </pre>
+     * 
+     * @param parent
+     * @return Create MavenProject mock.
+     */
+    private MavenProject createProjectChild2WithWrongVersion( MavenProject parent )
+    {
+        MavenProject mp2 = mock( MavenProject.class );
+        when( mp2.getParent() ).thenReturn( parent );
+        when( mp2.getGroupId() ).thenReturn( "org.apache.enforcer" );
+        when( mp2.getArtifactId() ).thenReturn( "m1" );
+        when( mp2.getVersion() ).thenReturn( "1.1-SNAPSHOT" );
+        when( mp2.getId() ).thenReturn( "org.apache.enforcer:m1:jar:1.1-SNAPSHOT" );
+        when( mp2.getDependencies() ).thenReturn( Collections.emptyList() );
+        return mp2;
+    }
+
+    private MavenProject createProjectChild2( MavenProject parent )
+    {
+        MavenProject mp3 = mock( MavenProject.class );
+        when( mp3.getParent() ).thenReturn( parent );
+        when( mp3.getGroupId() ).thenReturn( "org.apache.enforcer" );
+        when( mp3.getArtifactId() ).thenReturn( "m2" );
+        when( mp3.getVersion() ).thenReturn( "1.0-SNAPSHOT" );
+        when( mp3.getId() ).thenReturn( "org.apache.enforcer:m2:jar:1.0-SNAPSHOT" );
+        when( mp3.getDependencies() ).thenReturn( Collections.emptyList() );
+        return mp3;
+    }
+
+    private MavenProject createProjectChild1( MavenProject parent )
+    {
+        MavenProject mp2 = mock( MavenProject.class );
+        when( mp2.getParent() ).thenReturn( parent );
+        when( mp2.getGroupId() ).thenReturn( "org.apache.enforcer" );
+        when( mp2.getArtifactId() ).thenReturn( "m1" );
+        when( mp2.getVersion() ).thenReturn( "1.0-SNAPSHOT" );
+        when( mp2.getId() ).thenReturn( "org.apache.enforcer:m1:jar:1.0-SNAPSHOT" );
+        when( mp2.getDependencies() ).thenReturn( Collections.emptyList() );
+        return mp2;
+    }
+
+    private MavenProject createCompanyParent()
+    {
+        MavenProject nonReactoryParent = mock( MavenProject.class );
+        when( nonReactoryParent.getGroupId() ).thenReturn( "org.apache.enforcer.parent" );
+        when( nonReactoryParent.getArtifactId() ).thenReturn( "parent" );
+        when( nonReactoryParent.getVersion() ).thenReturn( "1.1" );
+        when( nonReactoryParent.getId() ).thenReturn( "org.apache.enforcer.parent:parent:jar:1.1" );
+        when( nonReactoryParent.getDependencies() ).thenReturn( Collections.emptyList() );
+        return nonReactoryParent;
+    }
+
+    private MavenProject createProjectParent( MavenProject nonReactorParent )
+    {
+        MavenProject m = createProjectParent();
+        when( m.isExecutionRoot() ).thenReturn( true );
+        when( m.getParent() ).thenReturn( nonReactorParent );
+        return m;
+    }
+
+    private MavenProject createProjectParent()
+    {
+        MavenProject mp1 = mock( MavenProject.class );
+        when( mp1.isExecutionRoot() ).thenReturn( true );
+        when( mp1.getParent() ).thenReturn( null );
+        when( mp1.getGroupId() ).thenReturn( "org.apache.enforcer" );
+        when( mp1.getArtifactId() ).thenReturn( "parent" );
+        when( mp1.getVersion() ).thenReturn( "1.0-SNAPSHOT" );
+        when( mp1.getId() ).thenReturn( "org.apache.enforcer:parent:pom:1.0-SNAPSHOT" );
+        when( mp1.getDependencies() ).thenReturn( Collections.emptyList() );
+        return mp1;
+    }
+
+    private Dependency createDependency( String groupId, String artifactId, String version )
+    {
+        Dependency dep = mock( Dependency.class );
+        when( dep.getGroupId() ).thenReturn( groupId );
+        when( dep.getArtifactId() ).thenReturn( artifactId );
+        when( dep.getVersion() ).thenReturn( version );
+        return dep;
+    }
+}