| package org.apache.maven.plugins.dependency.analyze; |
| |
| /* |
| * 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.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.model.Dependency; |
| import org.apache.maven.model.DependencyManagement; |
| import org.apache.maven.model.Exclusion; |
| import org.apache.maven.plugin.AbstractMojo; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugin.MojoFailureException; |
| import org.apache.maven.plugins.annotations.Mojo; |
| import org.apache.maven.plugins.annotations.Parameter; |
| import org.apache.maven.plugins.annotations.ResolutionScope; |
| 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="https://issues.apache.org/jira/browse/MNG-1577">MNG-1577</a> for more info. This |
| * mojo is also useful 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> |
| * @since 2.0-alpha-3 |
| */ |
| @Mojo( name = "analyze-dep-mgt", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true ) |
| public class AnalyzeDepMgt |
| extends AbstractMojo |
| { |
| // fields ----------------------------------------------------------------- |
| |
| /** |
| * |
| */ |
| @Parameter( defaultValue = "${project}", readonly = true, required = true ) |
| private MavenProject project; |
| |
| /** |
| * Fail the build if a problem is detected. |
| */ |
| @Parameter( property = "mdep.analyze.failBuild", defaultValue = "false" ) |
| private boolean failBuild = false; |
| |
| /** |
| * Ignore Direct Dependency Overrides of dependencyManagement section. |
| */ |
| @Parameter( property = "mdep.analyze.ignore.direct", defaultValue = "true" ) |
| private boolean ignoreDirect = true; |
| |
| /** |
| * Skip plugin execution completely. |
| * |
| * @since 2.7 |
| */ |
| @Parameter( property = "mdep.analyze.skip", defaultValue = "false" ) |
| private boolean skip; |
| |
| // Mojo methods ----------------------------------------------------------- |
| |
| /* |
| * @see org.apache.maven.plugin.Mojo#execute() |
| */ |
| @Override |
| public void execute() |
| throws MojoExecutionException, MojoFailureException |
| { |
| if ( skip ) |
| { |
| getLog().info( "Skipping plugin execution" ); |
| return; |
| } |
| |
| 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<Dependency> 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<String, Dependency> depMgtMap = new HashMap<String, Dependency>(); |
| Map<String, Exclusion> exclusions = new HashMap<String, Exclusion>(); |
| for ( Dependency depMgtDependency : depMgtDependencies ) |
| { |
| 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<Artifact> allDependencyArtifacts = new LinkedHashSet<Artifact>( 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<Artifact> directDependencies = project.getDependencyArtifacts(); |
| allDependencyArtifacts.removeAll( directDependencies ); |
| } |
| |
| // log exclusion errors |
| List<Artifact> exclusionErrors = getExclusionErrors( exclusions, allDependencyArtifacts ); |
| for ( Artifact exclusion : exclusionErrors ) |
| { |
| getLog().info( StringUtils.stripEnd( 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<Artifact, Dependency> mismatch = getMismatch( depMgtMap, allDependencyArtifacts ); |
| for ( Map.Entry<Artifact, Dependency> entry : mismatch.entrySet() ) |
| { |
| logMismatch( entry.getKey(), entry.getValue() ); |
| foundError = true; |
| } |
| if ( !foundError ) |
| { |
| getLog().info( "\tNone" ); |
| } |
| } |
| else |
| { |
| getLog().info( "\tNothing in DepMgt." ); |
| } |
| |
| 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<String, Exclusion> addExclusions( List<Exclusion> exclusionList ) |
| { |
| Map<String, Exclusion> exclusions = new HashMap<String, Exclusion>(); |
| if ( exclusionList != null ) |
| { |
| for ( Exclusion exclusion : exclusionList ) |
| { |
| exclusions.put( getExclusionKey( exclusion ), exclusion ); |
| } |
| } |
| return exclusions; |
| } |
| |
| /** |
| * Returns a List of the artifacts that should have been excluded, but were 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<Artifact> getExclusionErrors( Map<String, Exclusion> exclusions, Set<Artifact> allDependencyArtifacts ) |
| { |
| List<Artifact> list = new ArrayList<Artifact>(); |
| |
| for ( Artifact artifact : allDependencyArtifacts ) |
| { |
| if ( exclusions.containsKey( getExclusionKey( artifact ) ) ) |
| { |
| list.add( artifact ); |
| } |
| } |
| |
| return list; |
| } |
| |
| /** |
| * @param artifact {@link Artifact} |
| * @return The resulting GA. |
| */ |
| public String getExclusionKey( Artifact artifact ) |
| { |
| return artifact.getGroupId() + ":" + artifact.getArtifactId(); |
| } |
| |
| /** |
| * @param ex The exclusion key. |
| * @return The resulting combination of groupId+artifactId. |
| */ |
| public String getExclusionKey( Exclusion ex ) |
| { |
| return ex.getGroupId() + ":" + ex.getArtifactId(); |
| } |
| |
| /** |
| * 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<Artifact, Dependency> getMismatch( Map<String, Dependency> depMgtMap, |
| Set<Artifact> allDependencyArtifacts ) |
| { |
| Map<Artifact, Dependency> mismatchMap = new HashMap<Artifact, Dependency>(); |
| |
| for ( Artifact dependencyArtifact : allDependencyArtifacts ) |
| { |
| Dependency depFromDepMgt = depMgtMap.get( getArtifactManagementKey( dependencyArtifact ) ); |
| if ( depFromDepMgt != null ) |
| { |
| // workaround for MNG-2961 |
| dependencyArtifact.isSnapshot(); |
| |
| if ( depFromDepMgt.getVersion() != null |
| && !depFromDepMgt.getVersion().equals( dependencyArtifact.getBaseVersion() ) ) |
| { |
| 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 in case of errors. |
| */ |
| 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: " + StringUtils.stripEnd( dependencyFromDepMgt.getManagementKey(), ":" ) ); |
| getLog().info( "\t\tDepMgt : " + dependencyFromDepMgt.getVersion() ); |
| getLog().info( "\t\tResolved: " + dependencyArtifact.getBaseVersion() ); |
| } |
| |
| /** |
| * 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 |
| */ |
| protected final boolean isFailBuild() |
| { |
| return this.failBuild; |
| } |
| |
| /** |
| * @param theFailBuild the failBuild to set |
| */ |
| public void setFailBuild( boolean theFailBuild ) |
| { |
| this.failBuild = theFailBuild; |
| } |
| |
| /** |
| * @return the project |
| */ |
| protected final MavenProject getProject() |
| { |
| return this.project; |
| } |
| |
| /** |
| * @param theProject the project to set |
| */ |
| public void setProject( MavenProject theProject ) |
| { |
| this.project = theProject; |
| } |
| |
| /** |
| * @return the ignoreDirect |
| */ |
| protected final boolean isIgnoreDirect() |
| { |
| return this.ignoreDirect; |
| } |
| |
| /** |
| * @param theIgnoreDirect the ignoreDirect to set |
| */ |
| public void setIgnoreDirect( boolean theIgnoreDirect ) |
| { |
| this.ignoreDirect = theIgnoreDirect; |
| } |
| } |