Coverage Report - org.apache.maven.plugin.dependency.AnalyzeDepMgt
 
Classes in this File Line Coverage Branch Coverage Complexity
AnalyzeDepMgt
96%
73/76
87%
35/40
2.467
 
 1  
 package org.apache.maven.plugin.dependency;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *  http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import org.apache.maven.artifact.Artifact;
 23  
 import org.apache.maven.model.Dependency;
 24  
 import org.apache.maven.model.DependencyManagement;
 25  
 import org.apache.maven.model.Exclusion;
 26  
 import org.apache.maven.plugin.AbstractMojo;
 27  
 import org.apache.maven.plugin.MojoExecutionException;
 28  
 import org.apache.maven.plugin.MojoFailureException;
 29  
 import org.apache.maven.plugins.annotations.Component;
 30  
 import org.apache.maven.plugins.annotations.Mojo;
 31  
 import org.apache.maven.plugins.annotations.Parameter;
 32  
 import org.apache.maven.plugins.annotations.ResolutionScope;
 33  
 import org.apache.maven.project.MavenProject;
 34  
 import org.codehaus.plexus.util.StringUtils;
 35  
 
 36  
 import java.util.ArrayList;
 37  
 import java.util.HashMap;
 38  
 import java.util.HashSet;
 39  
 import java.util.List;
 40  
 import java.util.Map;
 41  
 import java.util.Set;
 42  
 
 43  
 /**
 44  
  * This mojo looks at the dependencies after final resolution and looks for
 45  
  * mismatches in your dependencyManagement section. In versions of maven prior
 46  
  * to 2.0.6, it was possible to inherit versions that didn't match your
 47  
  * dependencyManagement. See <a
 48  
  * href="http://jira.codehaus.org/browse/MNG-1577">MNG-1577</a> for more info.
 49  
  * This mojo is also useful for just detecting projects that override the
 50  
  * dependencyManagement directly. Set ignoreDirect to false to detect these
 51  
  * otherwise normal conditions.
 52  
  *
 53  
  * @author <a href="mailto:brianefox@gmail.com">Brian Fox</a>
 54  
  * @version $Id: AnalyzeDepMgt.java 1400739 2012-10-21 23:05:22Z hboutemy $
 55  
  * @since 2.0-alpha-3
 56  
  */
 57  
 @Mojo( name = "analyze-dep-mgt", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true )
 58  5
 public class AnalyzeDepMgt
 59  
     extends AbstractMojo
 60  
 {
 61  
     // fields -----------------------------------------------------------------
 62  
 
 63  
     /**
 64  
      *
 65  
      */
 66  
     @Component
 67  
     private MavenProject project;
 68  
 
 69  
     /**
 70  
      * Fail the build if a problem is detected.
 71  
      */
 72  5
     @Parameter( property = "mdep.analyze.failBuild", defaultValue = "false" )
 73  
     private boolean failBuild = false;
 74  
 
 75  
     /**
 76  
      * Ignore Direct Dependency Overrides of dependencyManagement section.
 77  
      */
 78  5
     @Parameter( property = "mdep.analyze.ignore.direct", defaultValue = "true" )
 79  
     private boolean ignoreDirect = true;
 80  
 
 81  
     // Mojo methods -----------------------------------------------------------
 82  
 
 83  
     /*
 84  
      * @see org.apache.maven.plugin.Mojo#execute()
 85  
      */
 86  
     public void execute()
 87  
         throws MojoExecutionException, MojoFailureException
 88  
     {
 89  4
         boolean result = checkDependencyManagement();
 90  4
         if ( result )
 91  
         {
 92  2
             if ( this.failBuild )
 93  
 
 94  
             {
 95  1
                 throw new MojoExecutionException( "Found Dependency errors." );
 96  
             }
 97  
             else
 98  
             {
 99  1
                 getLog().warn( "Potential problems found in Dependency Management " );
 100  
             }
 101  
         }
 102  3
     }
 103  
 
 104  
     /**
 105  
      * Does the work of checking the DependencyManagement Section.
 106  
      *
 107  
      * @return true if errors are found.
 108  
      * @throws MojoExecutionException
 109  
      */
 110  
     private boolean checkDependencyManagement()
 111  
         throws MojoExecutionException
 112  
     {
 113  4
         boolean foundError = false;
 114  
 
 115  4
         getLog().info( "Found Resolved Dependency / DependencyManagement mismatches:" );
 116  
 
 117  4
         List<Dependency> depMgtDependencies = null;
 118  
 
 119  4
         DependencyManagement depMgt = project.getDependencyManagement();
 120  4
         if ( depMgt != null )
 121  
         {
 122  4
             depMgtDependencies = depMgt.getDependencies();
 123  
         }
 124  
 
 125  4
         if ( depMgtDependencies != null && !depMgtDependencies.isEmpty() )
 126  
         {
 127  
             // put all the dependencies from depMgt into a map for quick lookup
 128  3
             Map<String, Dependency> depMgtMap = new HashMap<String, Dependency>();
 129  3
             Map<String, Exclusion> exclusions = new HashMap<String, Exclusion>();
 130  3
             for ( Dependency depMgtDependency : depMgtDependencies )
 131  
             {
 132  3
                 depMgtMap.put( depMgtDependency.getManagementKey(), depMgtDependency );
 133  
 
 134  
                 // now put all the exclusions into a map for quick lookup
 135  3
                 exclusions.putAll( addExclusions( depMgtDependency.getExclusions() ) );
 136  
             }
 137  
 
 138  
             // get dependencies for the project (including transitive)
 139  3
             @SuppressWarnings( "unchecked" ) Set<Artifact> allDependencyArtifacts =
 140  
                 new HashSet<Artifact>( project.getArtifacts() );
 141  
 
 142  
             // don't warn if a dependency that is directly listed overrides
 143  
             // depMgt. That's ok.
 144  3
             if ( this.ignoreDirect )
 145  
             {
 146  1
                 getLog().info( "\tIgnoring Direct Dependencies." );
 147  1
                 @SuppressWarnings( "unchecked" ) Set<Artifact> directDependencies = project.getDependencyArtifacts();
 148  1
                 allDependencyArtifacts.removeAll( directDependencies );
 149  
             }
 150  
 
 151  
             // log exclusion errors
 152  3
             List<Artifact> exclusionErrors = getExclusionErrors( exclusions, allDependencyArtifacts );
 153  3
             for ( Artifact exclusion : exclusionErrors )
 154  
             {
 155  2
                 getLog().info( StringUtils.stripEnd( getArtifactManagementKey( exclusion ), ":" )
 156  
                                    + " was excluded in DepMgt, but version " + exclusion.getVersion()
 157  
                                    + " has been found in the dependency tree." );
 158  2
                 foundError = true;
 159  
             }
 160  
 
 161  
             // find and log version mismatches
 162  3
             Map<Artifact, Dependency> mismatch = getMismatch( depMgtMap, allDependencyArtifacts );
 163  3
             for ( Map.Entry<Artifact, Dependency> entry : mismatch.entrySet() )
 164  
             {
 165  2
                 logMismatch( entry.getKey(), entry.getValue() );
 166  2
                 foundError = true;
 167  
             }
 168  3
             if ( !foundError )
 169  
             {
 170  1
                 getLog().info( "   None" );
 171  
             }
 172  3
         }
 173  
         else
 174  
         {
 175  1
             getLog().info( "   Nothing in DepMgt." );
 176  
         }
 177  
 
 178  4
         return foundError;
 179  
     }
 180  
 
 181  
     /**
 182  
      * Returns a map of the exclusions using the Dependency ManagementKey as the
 183  
      * keyset.
 184  
      *
 185  
      * @param exclusionList to be added to the map.
 186  
      * @return a map of the exclusions using the Dependency ManagementKey as the
 187  
      *         keyset.
 188  
      */
 189  
     public Map<String, Exclusion> addExclusions( List<Exclusion> exclusionList )
 190  
     {
 191  6
         Map<String, Exclusion> exclusions = new HashMap<String, Exclusion>();
 192  6
         if ( exclusionList != null )
 193  
         {
 194  5
             for ( Exclusion exclusion : exclusionList )
 195  
             {
 196  5
                 exclusions.put( getExclusionKey( exclusion ), exclusion );
 197  
             }
 198  
         }
 199  6
         return exclusions;
 200  
     }
 201  
 
 202  
     /**
 203  
      * Returns a List of the artifacts that should have been excluded, but were
 204  
      * found in the dependency tree.
 205  
      *
 206  
      * @param exclusions             a map of the DependencyManagement exclusions, with the
 207  
      *                               ManagementKey as the key and Dependency as the value.
 208  
      * @param allDependencyArtifacts resolved artifacts to be compared.
 209  
      * @return list of artifacts that should have been excluded.
 210  
      */
 211  
     public List<Artifact> getExclusionErrors( Map<String, Exclusion> exclusions, Set<Artifact> allDependencyArtifacts )
 212  
     {
 213  4
         List<Artifact> list = new ArrayList<Artifact>();
 214  
 
 215  4
         for ( Artifact artifact : allDependencyArtifacts )
 216  
         {
 217  47
             if ( exclusions.containsKey( getExclusionKey( artifact ) ) )
 218  
             {
 219  3
                 list.add( artifact );
 220  
             }
 221  
         }
 222  
 
 223  4
         return list;
 224  
     }
 225  
 
 226  
     public String getExclusionKey( Artifact artifact )
 227  
     {
 228  48
         return artifact.getGroupId() + ":" + artifact.getArtifactId();
 229  
     }
 230  
 
 231  
     public String getExclusionKey( Exclusion ex )
 232  
     {
 233  8
         return ex.getGroupId() + ":" + ex.getArtifactId();
 234  
     }
 235  
 
 236  
     /**
 237  
      * Calculate the mismatches between the DependencyManagement and resolved
 238  
      * artifacts
 239  
      *
 240  
      * @param depMgtMap              contains the Dependency.GetManagementKey as the keyset for
 241  
      *                               quick lookup.
 242  
      * @param allDependencyArtifacts contains the set of all artifacts to compare.
 243  
      * @return a map containing the resolved artifact as the key and the listed
 244  
      *         dependency as the value.
 245  
      */
 246  
     public Map<Artifact, Dependency> getMismatch( Map<String, Dependency> depMgtMap,
 247  
                                                   Set<Artifact> allDependencyArtifacts )
 248  
     {
 249  4
         Map<Artifact, Dependency> mismatchMap = new HashMap<Artifact, Dependency>();
 250  
 
 251  4
         for ( Artifact dependencyArtifact : allDependencyArtifacts )
 252  
         {
 253  47
             Dependency depFromDepMgt = depMgtMap.get( getArtifactManagementKey( dependencyArtifact ) );
 254  47
             if ( depFromDepMgt != null )
 255  
             {
 256  
                 //workaround for MNG-2961
 257  3
                 dependencyArtifact.isSnapshot();
 258  
 
 259  3
                 if ( !depFromDepMgt.getVersion().equals( dependencyArtifact.getBaseVersion() ) )
 260  
                 {
 261  3
                     mismatchMap.put( dependencyArtifact, depFromDepMgt );
 262  
                 }
 263  
             }
 264  47
         }
 265  4
         return mismatchMap;
 266  
     }
 267  
 
 268  
     /**
 269  
      * This function displays the log to the screen showing the versions and
 270  
      * information about the artifacts that don't match.
 271  
      *
 272  
      * @param dependencyArtifact   the artifact that was resolved.
 273  
      * @param dependencyFromDepMgt the dependency listed in the DependencyManagement section.
 274  
      * @throws MojoExecutionException
 275  
      */
 276  
     public void logMismatch( Artifact dependencyArtifact, Dependency dependencyFromDepMgt )
 277  
         throws MojoExecutionException
 278  
     {
 279  2
         if ( dependencyArtifact == null || dependencyFromDepMgt == null )
 280  
         {
 281  0
             throw new MojoExecutionException(
 282  
                 "Invalid params: Artifact:" + dependencyArtifact + " Dependency:" + dependencyFromDepMgt );
 283  
         }
 284  
 
 285  2
         getLog().info( "\tDependency: " + StringUtils.stripEnd( dependencyFromDepMgt.getManagementKey(), ":" ) );
 286  2
         getLog().info( "\t\tDepMgt  : " + dependencyFromDepMgt.getVersion() );
 287  2
         getLog().info( "\t\tResolved: " + dependencyArtifact.getBaseVersion() );
 288  2
     }
 289  
 
 290  
     /**
 291  
      * This function returns a string comparable with Dependency.GetManagementKey.
 292  
      *
 293  
      * @param artifact to gen the key for
 294  
      * @return a string in the form: groupId:ArtifactId:Type[:Classifier]
 295  
      */
 296  
     public String getArtifactManagementKey( Artifact artifact )
 297  
     {
 298  59
         return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getType() + ( (
 299  
             artifact.getClassifier() != null ) ? ":" + artifact.getClassifier() : "" );
 300  
     }
 301  
 
 302  
     /**
 303  
      * @return the failBuild
 304  
      */
 305  
     public boolean isFailBuild()
 306  
     {
 307  0
         return this.failBuild;
 308  
     }
 309  
 
 310  
     /**
 311  
      * @param theFailBuild the failBuild to set
 312  
      */
 313  
     public void setFailBuild( boolean theFailBuild )
 314  
     {
 315  2
         this.failBuild = theFailBuild;
 316  2
     }
 317  
 
 318  
     /**
 319  
      * @return the project
 320  
      */
 321  
     public MavenProject getProject()
 322  
     {
 323  5
         return this.project;
 324  
     }
 325  
 
 326  
     /**
 327  
      * @param theProject the project to set
 328  
      */
 329  
     public void setProject( MavenProject theProject )
 330  
     {
 331  5
         this.project = theProject;
 332  5
     }
 333  
 
 334  
     /**
 335  
      * @return the ignoreDirect
 336  
      */
 337  
     public boolean isIgnoreDirect()
 338  
     {
 339  0
         return this.ignoreDirect;
 340  
     }
 341  
 
 342  
     /**
 343  
      * @param theIgnoreDirect the ignoreDirect to set
 344  
      */
 345  
     public void setIgnoreDirect( boolean theIgnoreDirect )
 346  
     {
 347  2
         this.ignoreDirect = theIgnoreDirect;
 348  2
     }
 349  
 }