Coverage Report - org.apache.maven.plugins.enforcer.RequireUpperBoundDeps
 
Classes in this File Line Coverage Branch Coverage Complexity
RequireUpperBoundDeps
0%
0/59
0%
0/18
3
RequireUpperBoundDeps$1
N/A
N/A
3
RequireUpperBoundDeps$DependencyNodeHopCountPair
0%
0/23
0%
0/4
3
RequireUpperBoundDeps$RequireUpperBoundDepsVisitor
0%
0/28
0%
0/12
3
 
 1  
 package org.apache.maven.plugins.enforcer;
 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  
 import java.util.ArrayList;
 22  
 import java.util.Collections;
 23  
 import java.util.LinkedHashMap;
 24  
 import java.util.List;
 25  
 import java.util.Map;
 26  
 
 27  
 import org.apache.maven.artifact.Artifact;
 28  
 import org.apache.maven.artifact.factory.ArtifactFactory;
 29  
 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
 30  
 import org.apache.maven.artifact.repository.ArtifactRepository;
 31  
 import org.apache.maven.artifact.resolver.ArtifactCollector;
 32  
 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
 33  
 import org.apache.maven.artifact.versioning.ArtifactVersion;
 34  
 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
 35  
 import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
 36  
 import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
 37  
 import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
 38  
 import org.apache.maven.plugin.logging.Log;
 39  
 import org.apache.maven.project.MavenProject;
 40  
 import org.apache.maven.shared.dependency.tree.DependencyNode;
 41  
 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
 42  
 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
 43  
 import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
 44  
 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
 45  
 import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
 46  
 import org.codehaus.plexus.i18n.I18N;
 47  
 
 48  
 /**
 49  
  * Rule to enforce that the resolved dependency is also the most recent one of all transitive dependencies.
 50  
  * 
 51  
  * @author Geoffrey De Smet
 52  
  * @since 1.1
 53  
  */
 54  0
 public class RequireUpperBoundDeps
 55  
     extends AbstractNonCacheableEnforcerRule
 56  
 {
 57  
 
 58  
     private static Log log;
 59  
 
 60  
     private static I18N i18n;
 61  
 
 62  
     /**
 63  
      * Uses the {@link EnforcerRuleHelper} to populate the values of the
 64  
      * {@link DependencyTreeBuilder#buildDependencyTree(MavenProject, ArtifactRepository, ArtifactFactory, ArtifactMetadataSource, ArtifactFilter, ArtifactCollector)}
 65  
      * factory method. <br/>
 66  
      * This method simply exists to hide all the ugly lookup that the {@link EnforcerRuleHelper} has to do.
 67  
      * 
 68  
      * @param helper
 69  
      * @return a Dependency Node which is the root of the project's dependency tree
 70  
      * @throws EnforcerRuleException when the build should fail
 71  
      */
 72  
     private DependencyNode getNode( EnforcerRuleHelper helper )
 73  
         throws EnforcerRuleException
 74  
     {
 75  
         try
 76  
         {
 77  0
             MavenProject project = (MavenProject) helper.evaluate( "${project}" );
 78  0
             DependencyTreeBuilder dependencyTreeBuilder =
 79  
                 (DependencyTreeBuilder) helper.getComponent( DependencyTreeBuilder.class );
 80  0
             ArtifactRepository repository = (ArtifactRepository) helper.evaluate( "${localRepository}" );
 81  0
             ArtifactFactory factory = (ArtifactFactory) helper.getComponent( ArtifactFactory.class );
 82  0
             ArtifactMetadataSource metadataSource =
 83  
                 (ArtifactMetadataSource) helper.getComponent( ArtifactMetadataSource.class );
 84  0
             ArtifactCollector collector = (ArtifactCollector) helper.getComponent( ArtifactCollector.class );
 85  0
             ArtifactFilter filter = null; // we need to evaluate all scopes
 86  0
             DependencyNode node =
 87  
                 dependencyTreeBuilder.buildDependencyTree( project, repository, factory, metadataSource, filter,
 88  
                                                            collector );
 89  0
             return node;
 90  
         }
 91  0
         catch ( ExpressionEvaluationException e )
 92  
         {
 93  0
             throw new EnforcerRuleException( "Unable to lookup an expression " + e.getLocalizedMessage(), e );
 94  
         }
 95  0
         catch ( ComponentLookupException e )
 96  
         {
 97  0
             throw new EnforcerRuleException( "Unable to lookup a component " + e.getLocalizedMessage(), e );
 98  
         }
 99  0
         catch ( DependencyTreeBuilderException e )
 100  
         {
 101  0
             throw new EnforcerRuleException( "Could not build dependency tree " + e.getLocalizedMessage(), e );
 102  
         }
 103  
     }
 104  
 
 105  
     public void execute( EnforcerRuleHelper helper )
 106  
         throws EnforcerRuleException
 107  
     {
 108  0
         if ( log == null )
 109  
         {
 110  0
             log = helper.getLog();
 111  
         }
 112  
         try
 113  
         {
 114  0
             if ( i18n == null )
 115  
             {
 116  0
                 i18n = (I18N) helper.getComponent( I18N.class );
 117  
             }
 118  0
             DependencyNode node = getNode( helper );
 119  0
             RequireUpperBoundDepsVisitor visitor = new RequireUpperBoundDepsVisitor();
 120  0
             node.accept( visitor );
 121  0
             List<String> errorMessages = buildErrorMessages( visitor.getConflicts() );
 122  0
             if ( errorMessages.size() > 0 )
 123  
             {
 124  0
                 throw new EnforcerRuleException( "Failed while enforcing RequireUpperBoundDeps. The error(s) are "
 125  
                     + errorMessages );
 126  
             }
 127  
         }
 128  0
         catch ( ComponentLookupException e )
 129  
         {
 130  0
             throw new EnforcerRuleException( "Unable to lookup a component " + e.getLocalizedMessage(), e );
 131  
         }
 132  0
         catch ( Exception e )
 133  
         {
 134  0
             throw new EnforcerRuleException( e.getLocalizedMessage(), e );
 135  0
         }
 136  0
     }
 137  
 
 138  
     private List<String> buildErrorMessages( List<List<DependencyNode>> conflicts )
 139  
     {
 140  0
         List<String> errorMessages = new ArrayList<String>( conflicts.size() );
 141  0
         for ( List<DependencyNode> conflict : conflicts )
 142  
         {
 143  0
             errorMessages.add( buildErrorMessage( conflict ) );
 144  
         }
 145  0
         return errorMessages;
 146  
     }
 147  
 
 148  
     private String buildErrorMessage( List<DependencyNode> conflict )
 149  
     {
 150  0
         StringBuilder errorMessage = new StringBuilder();
 151  0
         errorMessage.append( "\nRequire upper bound dependencies error for "
 152  
             + getFullArtifactName( conflict.get( 0 ).getArtifact() ) + " paths to dependency are:\n" );
 153  0
         if ( conflict.size() > 0 )
 154  
         {
 155  0
             errorMessage.append( buildTreeString( conflict.get( 0 ) ) );
 156  
         }
 157  0
         for ( DependencyNode node : conflict.subList( 1, conflict.size() ) )
 158  
         {
 159  0
             errorMessage.append( "and\n" );
 160  0
             errorMessage.append( buildTreeString( node ) );
 161  
         }
 162  0
         return errorMessage.toString();
 163  
     }
 164  
 
 165  
     private StringBuilder buildTreeString( DependencyNode node )
 166  
     {
 167  0
         List<String> loc = new ArrayList<String>();
 168  0
         DependencyNode currentNode = node;
 169  0
         while ( currentNode != null )
 170  
         {
 171  0
             loc.add( getFullArtifactName( currentNode.getArtifact() ) );
 172  0
             currentNode = currentNode.getParent();
 173  
         }
 174  0
         Collections.reverse( loc );
 175  0
         StringBuilder builder = new StringBuilder();
 176  0
         for ( int i = 0; i < loc.size(); i++ )
 177  
         {
 178  0
             for ( int j = 0; j < i; j++ )
 179  
             {
 180  0
                 builder.append( "  " );
 181  
             }
 182  0
             builder.append( "+-" ).append( loc.get( i ) );
 183  0
             builder.append( "\n" );
 184  
         }
 185  0
         return builder;
 186  
     }
 187  
 
 188  
     private String getFullArtifactName( Artifact artifact )
 189  
     {
 190  0
         return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion();
 191  
     }
 192  
 
 193  0
     private static class RequireUpperBoundDepsVisitor
 194  
         implements DependencyNodeVisitor
 195  
     {
 196  
 
 197  0
         private Map<String, List<DependencyNodeHopCountPair>> keyToPairsMap =
 198  
             new LinkedHashMap<String, List<DependencyNodeHopCountPair>>();
 199  
 
 200  
         public boolean visit( DependencyNode node )
 201  
         {
 202  0
             DependencyNodeHopCountPair pair = new DependencyNodeHopCountPair( node );
 203  0
             String key = pair.constructKey();
 204  0
             List<DependencyNodeHopCountPair> pairs = keyToPairsMap.get( key );
 205  0
             if ( pairs == null )
 206  
             {
 207  0
                 pairs = new ArrayList<DependencyNodeHopCountPair>();
 208  0
                 keyToPairsMap.put( key, pairs );
 209  
             }
 210  0
             pairs.add( pair );
 211  0
             Collections.sort( pairs );
 212  0
             return true;
 213  
         }
 214  
 
 215  
         public boolean endVisit( DependencyNode node )
 216  
         {
 217  0
             return true;
 218  
         }
 219  
 
 220  
         public List<List<DependencyNode>> getConflicts()
 221  
         {
 222  0
             List<List<DependencyNode>> output = new ArrayList<List<DependencyNode>>();
 223  0
             for ( List<DependencyNodeHopCountPair> pairs : keyToPairsMap.values() )
 224  
             {
 225  0
                 if ( containsConflicts( pairs ) )
 226  
                 {
 227  0
                     List<DependencyNode> outputSubList = new ArrayList<DependencyNode>( pairs.size() );
 228  0
                     for ( DependencyNodeHopCountPair pair : pairs )
 229  
                     {
 230  0
                         outputSubList.add( pair.getNode() );
 231  
                     }
 232  0
                     output.add( outputSubList );
 233  0
                 }
 234  
             }
 235  0
             return output;
 236  
         }
 237  
 
 238  
         @SuppressWarnings( "unchecked" )
 239  
         private boolean containsConflicts( List<DependencyNodeHopCountPair> pairs )
 240  
         {
 241  0
             ArtifactVersion resolvedVersion = pairs.get( 0 ).extractArtifactVersion();
 242  0
             for ( DependencyNodeHopCountPair pair : pairs )
 243  
             {
 244  0
                 ArtifactVersion version = pair.extractArtifactVersion();
 245  0
                 if ( resolvedVersion.compareTo( version ) < 0 )
 246  
                 {
 247  0
                     return true;
 248  
                 }
 249  0
             }
 250  0
             return false;
 251  
         }
 252  
 
 253  
     }
 254  
 
 255  0
     private static class DependencyNodeHopCountPair
 256  
         implements Comparable<DependencyNodeHopCountPair>
 257  
     {
 258  
 
 259  
         private DependencyNode node;
 260  
 
 261  
         private int hopCount;
 262  
 
 263  
         private DependencyNodeHopCountPair( DependencyNode node )
 264  0
         {
 265  0
             this.node = node;
 266  0
             countHops();
 267  0
         }
 268  
 
 269  
         private void countHops()
 270  
         {
 271  0
             hopCount = 0;
 272  0
             DependencyNode parent = node.getParent();
 273  0
             while ( parent != null )
 274  
             {
 275  0
                 hopCount++;
 276  0
                 parent = parent.getParent();
 277  
             }
 278  0
         }
 279  
 
 280  
         private String constructKey()
 281  
         {
 282  0
             Artifact artifact = node.getArtifact();
 283  0
             return artifact.getGroupId() + ":" + artifact.getArtifactId();
 284  
         }
 285  
 
 286  
         public DependencyNode getNode()
 287  
         {
 288  0
             return node;
 289  
         }
 290  
 
 291  
         private ArtifactVersion extractArtifactVersion()
 292  
         {
 293  0
             Artifact artifact = node.getArtifact();
 294  0
             String version = artifact.getVersion();
 295  0
             if ( version != null )
 296  
             {
 297  0
                 return new DefaultArtifactVersion( version );
 298  
             }
 299  
             try
 300  
             {
 301  0
                 return artifact.getSelectedVersion();
 302  
             }
 303  0
             catch ( OverConstrainedVersionException e )
 304  
             {
 305  0
                 throw new RuntimeException( "Version ranges problem with " + node.getArtifact(), e );
 306  
             }
 307  
         }
 308  
 
 309  
         public int getHopCount()
 310  
         {
 311  0
             return hopCount;
 312  
         }
 313  
 
 314  
         public int compareTo( DependencyNodeHopCountPair other )
 315  
         {
 316  0
             return Integer.valueOf( hopCount ).compareTo( Integer.valueOf( other.getHopCount() ) );
 317  
         }
 318  
 
 319  
     }
 320  
 
 321  
 }