001package org.apache.maven.plugins.enforcer; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.util.ArrayList; 023import java.util.Collections; 024import java.util.List; 025 026import org.apache.maven.artifact.Artifact; 027import org.apache.maven.artifact.factory.ArtifactFactory; 028import org.apache.maven.artifact.metadata.ArtifactMetadataSource; 029import org.apache.maven.artifact.repository.ArtifactRepository; 030import org.apache.maven.artifact.resolver.ArtifactCollector; 031import org.apache.maven.artifact.resolver.filter.ArtifactFilter; 032import org.apache.maven.enforcer.rule.api.EnforcerRule; 033import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 034import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; 035import org.apache.maven.plugin.logging.Log; 036import org.apache.maven.plugins.enforcer.utils.DependencyVersionMap; 037import org.apache.maven.project.MavenProject; 038import org.apache.maven.shared.dependency.tree.DependencyNode; 039import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder; 040import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException; 041import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 042import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 043 044/** 045 * @author <a href="mailto:rex@e-hoffman.org">Rex Hoffman</a> 046 */ 047public class DependencyConvergence 048 implements EnforcerRule 049{ 050 051 private static Log log; 052 053 private boolean uniqueVersions; 054 055 public void setUniqueVersions( boolean uniqueVersions ) 056 { 057 this.uniqueVersions = uniqueVersions; 058 } 059 060 // CHECKSTYLE_OFF: LineLength 061 /** 062 * Uses the {@link EnforcerRuleHelper} to populate the values of the 063 * {@link DependencyTreeBuilder#buildDependencyTree(MavenProject, ArtifactRepository, ArtifactFactory, ArtifactMetadataSource, ArtifactFilter, ArtifactCollector)} 064 * factory method. <br/> 065 * This method simply exists to hide all the ugly lookup that the {@link EnforcerRuleHelper} has to do. 066 * 067 * @param helper 068 * @return a Dependency Node which is the root of the project's dependency tree 069 * @throws EnforcerRuleException 070 */ 071 // CHECKSTYLE_ON: LineLength 072 private DependencyNode getNode( EnforcerRuleHelper helper ) 073 throws EnforcerRuleException 074 { 075 try 076 { 077 MavenProject project = (MavenProject) helper.evaluate( "${project}" ); 078 DependencyTreeBuilder dependencyTreeBuilder = 079 (DependencyTreeBuilder) helper.getComponent( DependencyTreeBuilder.class ); 080 ArtifactRepository repository = (ArtifactRepository) helper.evaluate( "${localRepository}" ); 081 ArtifactFactory factory = (ArtifactFactory) helper.getComponent( ArtifactFactory.class ); 082 ArtifactMetadataSource metadataSource = 083 (ArtifactMetadataSource) helper.getComponent( ArtifactMetadataSource.class ); 084 ArtifactCollector collector = (ArtifactCollector) helper.getComponent( ArtifactCollector.class ); 085 ArtifactFilter filter = null; // we need to evaluate all scopes 086 DependencyNode node = dependencyTreeBuilder.buildDependencyTree( project, repository, factory, 087 metadataSource, filter, collector ); 088 return node; 089 } 090 catch ( ExpressionEvaluationException e ) 091 { 092 throw new EnforcerRuleException( "Unable to lookup an expression " + e.getLocalizedMessage(), e ); 093 } 094 catch ( ComponentLookupException e ) 095 { 096 throw new EnforcerRuleException( "Unable to lookup a component " + e.getLocalizedMessage(), e ); 097 } 098 catch ( DependencyTreeBuilderException e ) 099 { 100 throw new EnforcerRuleException( "Could not build dependency tree " + e.getLocalizedMessage(), e ); 101 } 102 } 103 104 @Override 105 public void execute( EnforcerRuleHelper helper ) 106 throws EnforcerRuleException 107 { 108 if ( log == null ) 109 { 110 log = helper.getLog(); 111 } 112 try 113 { 114 DependencyNode node = getNode( helper ); 115 DependencyVersionMap visitor = new DependencyVersionMap( log ); 116 visitor.setUniqueVersions( uniqueVersions ); 117 node.accept( visitor ); 118 List<CharSequence> errorMsgs = new ArrayList<CharSequence>(); 119 errorMsgs.addAll( getConvergenceErrorMsgs( visitor.getConflictedVersionNumbers() ) ); 120 for ( CharSequence errorMsg : errorMsgs ) 121 { 122 log.warn( errorMsg ); 123 } 124 if ( errorMsgs.size() > 0 ) 125 { 126 throw new EnforcerRuleException( "Failed while enforcing releasability. " 127 + "See above detailed error message." ); 128 } 129 } 130 catch ( Exception e ) 131 { 132 throw new EnforcerRuleException( e.getLocalizedMessage(), e ); 133 } 134 } 135 136 private String getFullArtifactName( Artifact artifact ) 137 { 138 return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion(); 139 } 140 141 private StringBuilder buildTreeString( DependencyNode node ) 142 { 143 List<String> loc = new ArrayList<String>(); 144 DependencyNode currentNode = node; 145 while ( currentNode != null ) 146 { 147 loc.add( getFullArtifactName( currentNode.getArtifact() ) ); 148 currentNode = currentNode.getParent(); 149 } 150 Collections.reverse( loc ); 151 StringBuilder builder = new StringBuilder(); 152 for ( int i = 0; i < loc.size(); i++ ) 153 { 154 for ( int j = 0; j < i; j++ ) 155 { 156 builder.append( " " ); 157 } 158 builder.append( "+-" + loc.get( i ) ); 159 builder.append( "\n" ); 160 } 161 return builder; 162 } 163 164 private List<String> getConvergenceErrorMsgs( List<List<DependencyNode>> errors ) 165 { 166 List<String> errorMsgs = new ArrayList<String>(); 167 for ( List<DependencyNode> nodeList : errors ) 168 { 169 errorMsgs.add( buildConvergenceErrorMsg( nodeList ) ); 170 } 171 return errorMsgs; 172 } 173 174 private String buildConvergenceErrorMsg( List<DependencyNode> nodeList ) 175 { 176 StringBuilder builder = new StringBuilder(); 177 builder.append( "\nDependency convergence error for " + getFullArtifactName( nodeList.get( 0 ).getArtifact() ) 178 + " paths to dependency are:\n" ); 179 if ( nodeList.size() > 0 ) 180 { 181 builder.append( buildTreeString( nodeList.get( 0 ) ) ); 182 } 183 for ( DependencyNode node : nodeList.subList( 1, nodeList.size() ) ) 184 { 185 builder.append( "and\n" ); 186 builder.append( buildTreeString( node ) ); 187 } 188 return builder.toString(); 189 } 190 191 @Override 192 public String getCacheId() 193 { 194 return ""; 195 } 196 197 @Override 198 public boolean isCacheable() 199 { 200 return false; 201 } 202 203 @Override 204 public boolean isResultValid( EnforcerRule rule ) 205 { 206 return false; 207 } 208}