package org.apache.maven.plugin.dependency; | |
/* | |
* 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.HashSet; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import org.apache.maven.artifact.Artifact; | |
import org.apache.maven.artifact.versioning.ArtifactVersion; | |
import org.apache.maven.artifact.versioning.DefaultArtifactVersion; | |
import org.apache.maven.model.Dependency; | |
import org.apache.maven.model.DependencyManagement; | |
import org.apache.maven.plugin.AbstractMojo; | |
import org.apache.maven.plugin.MojoExecutionException; | |
import org.apache.maven.plugin.MojoFailureException; | |
import org.apache.maven.project.MavenProject; | |
import org.codehaus.plexus.util.StringUtils; | |
/** | |
* This mojo looks at the dependencies after final resolution and looks for | |
* mismatches in your dependencyManagement section. In versions of maven prior | |
* to 2.0.6, it was possible to inherit versions that didn't match your | |
* dependencyManagement. See <a | |
* href="http://jira.codehaus.org/browse/MNG-1577">MNG-1577</a> for more info. | |
* This mojo is also usefull for just detecting projects that override the | |
* dependencyManagement directly. Set ignoreDirect to false to detect these | |
* otherwise normal conditions. | |
* | |
* @author <a href="mailto:brianefox@gmail.com">Brian Fox</a> | |
* @version $Id: AnalyzeMojo.java 519377 2007-03-17 17:37:26Z brianf $ | |
* @goal analyze-dep-mgt | |
* @requiresDependencyResolution test | |
* @since 2.0-alpha-3 | |
*/ | |
public class AnalyzeDepMgt | |
extends AbstractMojo | |
{ | |
// fields ----------------------------------------------------------------- | |
/** | |
* | |
* | |
* @parameter expression="${project}" | |
* @required | |
* @readonly | |
*/ | |
private MavenProject project; | |
/** | |
* Fail the build if a problem is detected. | |
* | |
* @parameter expression="${mdep.analyze.failBuild}" | |
*/ | |
private boolean failBuild = false; | |
/** | |
* Ignore Direct Dependency Overrides of dependencyManagement section. | |
* | |
* @parameter expression="${mdep.analyze.ignore.direct}" | |
*/ | |
private boolean ignoreDirect = true; | |
// Mojo methods ----------------------------------------------------------- | |
/* | |
* @see org.apache.maven.plugin.Mojo#execute() | |
*/ | |
public void execute() | |
throws MojoExecutionException, MojoFailureException | |
{ | |
boolean result = checkDependencyManagement(); | |
if ( result ) | |
{ | |
if ( this.failBuild ) | |
{ | |
throw new MojoExecutionException( "Found Dependency errors." ); | |
} | |
else | |
{ | |
getLog().warn( "Potential problems found in Dependency Management " ); | |
} | |
} | |
} | |
/** | |
* Does the work of checking the DependencyManagement Section. | |
* @return true if errors are found. | |
* @throws MojoExecutionException | |
*/ | |
private boolean checkDependencyManagement() | |
throws MojoExecutionException | |
{ | |
boolean foundError = false; | |
getLog().info( "Found Resolved Dependency / DependencyManagement mismatches:" ); | |
List depMgtDependencies = null; | |
DependencyManagement depMgt = project.getDependencyManagement(); | |
if ( depMgt != null ) | |
{ | |
depMgtDependencies = depMgt.getDependencies(); | |
} | |
if ( depMgtDependencies != null && !depMgtDependencies.isEmpty() ) | |
{ | |
// put all the dependencies from depMgt into a map for quick lookup | |
Map depMgtMap = new HashMap(); | |
Map exclusions = new HashMap(); | |
Iterator iter = depMgtDependencies.iterator(); | |
while ( iter.hasNext() ) | |
{ | |
Dependency depMgtDependency = (Dependency) iter.next(); | |
depMgtMap.put( depMgtDependency.getManagementKey(), depMgtDependency ); | |
// now put all the exclusions into a map for quick lookup | |
exclusions.putAll( addExclusions( depMgtDependency.getExclusions() ) ); | |
} | |
// get dependencies for the project (including transitive) | |
Set allDependencyArtifacts = new HashSet( project.getArtifacts() ); | |
// don't warn if a dependency that is directly listed overrides | |
// depMgt. That's ok. | |
if ( this.ignoreDirect ) | |
{ | |
getLog().info( "\tIgnoring Direct Dependencies." ); | |
Set directDependencies = project.getDependencyArtifacts(); | |
allDependencyArtifacts.removeAll( directDependencies ); | |
} | |
// log exclusion errors | |
List exclusionErrors = getExclusionErrors( exclusions, allDependencyArtifacts ); | |
Iterator exclusionIter = exclusionErrors.iterator(); | |
while ( exclusionIter.hasNext() ) | |
{ | |
Artifact exclusion = (Artifact) iter.next(); | |
getLog().info( | |
getArtifactManagementKey( exclusion ) + " was excluded in DepMgt, but version " | |
+ exclusion.getVersion() + " has been found in the dependency tree." ); | |
foundError = true; | |
} | |
// find and log version mismatches | |
Map mismatch = getMismatch( depMgtMap, allDependencyArtifacts ); | |
Iterator mismatchIter = mismatch.keySet().iterator(); | |
while ( mismatchIter.hasNext() ) | |
{ | |
Artifact resolvedArtifact = (Artifact) mismatchIter.next(); | |
Dependency depMgtDependency = (Dependency) mismatch.get( resolvedArtifact ); | |
logMismatch( resolvedArtifact, depMgtDependency ); | |
} | |
} | |
else | |
{ | |
getLog().info( " Nothing in DepMgt." ); | |
} | |
if ( !foundError ) | |
{ | |
getLog().info( " None" ); | |
} | |
return foundError; | |
} | |
/** | |
* Returns a map of the exclusions using the Dependency ManagementKey as the | |
* keyset. | |
* | |
* @param exclusionList | |
* to be added to the map. | |
* @return a map of the exclusions using the Dependency ManagementKey as the | |
* keyset. | |
*/ | |
public Map addExclusions( List exclusionList ) | |
{ | |
Map exclusions = new HashMap(); | |
if ( exclusionList != null ) | |
{ | |
Iterator exclusionIter = exclusionList.iterator(); | |
while ( exclusionIter.hasNext() ) | |
{ | |
Dependency exclusion = (Dependency) exclusionIter.next(); | |
exclusions.put( exclusion.getManagementKey(), exclusion ); | |
} | |
} | |
return exclusions; | |
} | |
/** | |
* Returns a List of the artifacts that should have been excluded, but where | |
* found in the dependency tree. | |
* | |
* @param exclusions | |
* a map of the DependencyManagement exclusions, with the | |
* ManagementKey as the key and Dependency as the value. | |
* @param allDependencyArtifacts | |
* resolved artifacts to be compared. | |
* @return list of artifacts that should have been excluded. | |
*/ | |
public List getExclusionErrors( Map exclusions, Set allDependencyArtifacts ) | |
{ | |
List list = new ArrayList(); | |
Iterator iter = allDependencyArtifacts.iterator(); | |
while ( iter.hasNext() ) | |
{ | |
Artifact artifact = (Artifact) iter.next(); | |
if ( exclusions.containsKey( getArtifactManagementKey( artifact ) ) ) | |
{ | |
list.add( artifact ); | |
} | |
} | |
return list; | |
} | |
/** | |
* Calculate the mismatches between the DependencyManagement and resolved | |
* artifacts | |
* | |
* @param depMgtMap | |
* contains the Dependency.GetManagementKey as the keyset for | |
* quick lookup. | |
* @param allDependencyArtifacts | |
* contains the set of all artifacts to compare. | |
* @return a map containing the resolved artifact as the key and the listed | |
* dependency as the value. | |
*/ | |
public Map getMismatch( Map depMgtMap, Set allDependencyArtifacts ) | |
{ | |
Map mismatchMap = new HashMap(); | |
Iterator iter = allDependencyArtifacts.iterator(); | |
while ( iter.hasNext() ) | |
{ | |
Artifact dependencyArtifact = (Artifact) iter.next(); | |
Dependency depFromDepMgt = (Dependency) depMgtMap.get( getArtifactManagementKey( dependencyArtifact ) ); | |
if ( depFromDepMgt != null ) | |
{ | |
ArtifactVersion artifactVersion = new DefaultArtifactVersion( dependencyArtifact.getVersion() ); | |
if ( !dependencyArtifact.isSnapshot() | |
&& !depFromDepMgt.getVersion().equals( dependencyArtifact.getVersion() ) ) | |
{ | |
mismatchMap.put( dependencyArtifact, depFromDepMgt ); | |
} | |
} | |
} | |
return mismatchMap; | |
} | |
/** | |
* This function displays the log to the screen showing the versions and | |
* information about the artifacts that don't match. | |
* | |
* @param dependencyArtifact | |
* the artifact that was resolved. | |
* @param dependencyFromDepMgt | |
* the dependency listed in the DependencyManagement section. | |
* @throws MojoExecutionException | |
*/ | |
public void logMismatch( Artifact dependencyArtifact, Dependency dependencyFromDepMgt ) | |
throws MojoExecutionException | |
{ | |
if ( dependencyArtifact == null || dependencyFromDepMgt == null ) | |
{ | |
throw new MojoExecutionException( "Invalid params: Artifact:" + dependencyArtifact + " Dependency:" | |
+ dependencyFromDepMgt ); | |
} | |
getLog().info( "\tDependency: " + dependencyFromDepMgt.getManagementKey() ); | |
getLog().info( "\t\tDepMgt : " + dependencyFromDepMgt.getVersion() ); | |
getLog().info( "\t\tResolved: " + dependencyArtifact.getVersion() ); | |
} | |
/** | |
* This function returns a string comparable with | |
* Dependency.GetManagementKey. | |
* | |
* @param artifact | |
* to gen the key for | |
* @return a string in the form: groupId:ArtifactId:Type[:Classifier] | |
*/ | |
public String getArtifactManagementKey( Artifact artifact ) | |
{ | |
return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getType() | |
+ (( artifact.getClassifier() !=null ) ? ":" + artifact.getClassifier() : "" ); | |
} | |
/** | |
* @return the failBuild | |
*/ | |
public boolean isFailBuild() | |
{ | |
return this.failBuild; | |
} | |
/** | |
* @param theFailBuild | |
* the failBuild to set | |
*/ | |
public void setFailBuild( boolean theFailBuild ) | |
{ | |
this.failBuild = theFailBuild; | |
} | |
/** | |
* @return the project | |
*/ | |
public MavenProject getProject() | |
{ | |
return this.project; | |
} | |
/** | |
* @param theProject | |
* the project to set | |
*/ | |
public void setProject( MavenProject theProject ) | |
{ | |
this.project = theProject; | |
} | |
/** | |
* @return the ignoreDirect | |
*/ | |
public boolean isIgnoreDirect() | |
{ | |
return this.ignoreDirect; | |
} | |
/** | |
* @param theIgnoreDirect | |
* the ignoreDirect to set | |
*/ | |
public void setIgnoreDirect( boolean theIgnoreDirect ) | |
{ | |
this.ignoreDirect = theIgnoreDirect; | |
} | |
} |