View Javadoc

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 java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  
30  import org.apache.maven.artifact.Artifact;
31  import org.apache.maven.model.Dependency;
32  import org.apache.maven.model.DependencyManagement;
33  import org.apache.maven.model.Exclusion;
34  import org.apache.maven.plugin.AbstractMojo;
35  import org.apache.maven.plugin.MojoExecutionException;
36  import org.apache.maven.plugin.MojoFailureException;
37  import org.apache.maven.project.MavenProject;
38  import org.codehaus.plexus.util.StringUtils;
39  
40  /**
41   * This mojo looks at the dependencies after final resolution and looks for
42   * mismatches in your dependencyManagement section. In versions of maven prior
43   * to 2.0.6, it was possible to inherit versions that didn't match your
44   * dependencyManagement. See <a
45   * href="http://jira.codehaus.org/browse/MNG-1577">MNG-1577</a> for more info.
46   * This mojo is also usefull for just detecting projects that override the
47   * dependencyManagement directly. Set ignoreDirect to false to detect these
48   * otherwise normal conditions.
49   * 
50   * @author <a href="mailto:brianefox@gmail.com">Brian Fox</a>
51   * @version $Id: AnalyzeDepMgt.java 728546 2008-12-21 22:56:51Z bentmann $
52   * @goal analyze-dep-mgt
53   * @requiresDependencyResolution test
54   * @since 2.0-alpha-3
55   */
56  public class AnalyzeDepMgt
57      extends AbstractMojo
58  {
59      // fields -----------------------------------------------------------------
60  
61      /**
62       * 
63       * 
64       * @parameter expression="${project}"
65       * @required
66       * @readonly
67       */
68      private MavenProject project;
69  
70      /**
71       * Fail the build if a problem is detected.
72       * 
73       * @parameter expression="${mdep.analyze.failBuild}" default-value="false"
74       */
75      private boolean failBuild = false;
76  
77      /**
78       * Ignore Direct Dependency Overrides of dependencyManagement section.
79       * 
80       * @parameter expression="${mdep.analyze.ignore.direct}" default-value="true"
81       */
82      private boolean ignoreDirect = true;
83  
84      // Mojo methods -----------------------------------------------------------
85  
86      /*
87       * @see org.apache.maven.plugin.Mojo#execute()
88       */
89      public void execute()
90          throws MojoExecutionException, MojoFailureException
91      {
92          boolean result = checkDependencyManagement();
93          if ( result )
94          {
95              if ( this.failBuild )
96  
97              {
98                  throw new MojoExecutionException( "Found Dependency errors." );
99              }
100             else
101             {
102                 getLog().warn( "Potential problems found in Dependency Management " );
103             }
104         }
105     }
106 
107     /**
108      * Does the work of checking the DependencyManagement Section.
109      * @return true if errors are found.
110      * @throws MojoExecutionException
111      */
112     private boolean checkDependencyManagement()
113         throws MojoExecutionException
114     {
115         boolean foundError = false;
116 
117         getLog().info( "Found Resolved Dependency / DependencyManagement mismatches:" );
118 
119         List depMgtDependencies = null;
120 
121         DependencyManagement depMgt = project.getDependencyManagement();
122         if ( depMgt != null )
123         {
124             depMgtDependencies = depMgt.getDependencies();
125         }
126 
127         if ( depMgtDependencies != null && !depMgtDependencies.isEmpty() )
128         {
129             // put all the dependencies from depMgt into a map for quick lookup
130             Map depMgtMap = new HashMap();
131             Map exclusions = new HashMap();
132             Iterator iter = depMgtDependencies.iterator();
133             while ( iter.hasNext() )
134             {
135                 Dependency depMgtDependency = (Dependency) iter.next();
136                 depMgtMap.put( depMgtDependency.getManagementKey(), depMgtDependency );
137 
138                 // now put all the exclusions into a map for quick lookup
139                 exclusions.putAll( addExclusions( depMgtDependency.getExclusions() ) );
140             }
141 
142             // get dependencies for the project (including transitive)
143             Set allDependencyArtifacts = new HashSet( project.getArtifacts() );
144 
145             // don't warn if a dependency that is directly listed overrides
146             // depMgt. That's ok.
147             if ( this.ignoreDirect )
148             {
149                 getLog().info( "\tIgnoring Direct Dependencies." );
150                 Set directDependencies = project.getDependencyArtifacts();
151                 allDependencyArtifacts.removeAll( directDependencies );
152             }
153 
154             // log exclusion errors
155             List exclusionErrors = getExclusionErrors( exclusions, allDependencyArtifacts );
156             Iterator exclusionIter = exclusionErrors.iterator();
157             while ( exclusionIter.hasNext() )
158             {
159                 Artifact exclusion = (Artifact) exclusionIter.next();
160                 getLog().info(
161                                StringUtils.stripEnd( getArtifactManagementKey( exclusion ),":") + " was excluded in DepMgt, but version "
162                                    + exclusion.getVersion() + " has been found in the dependency tree." );
163                 foundError = true;
164             }
165 
166             // find and log version mismatches
167             Map mismatch = getMismatch( depMgtMap, allDependencyArtifacts );
168             Iterator mismatchIter = mismatch.keySet().iterator();
169             while ( mismatchIter.hasNext() )
170             {
171                 Artifact resolvedArtifact = (Artifact) mismatchIter.next();
172                 Dependency depMgtDependency = (Dependency) mismatch.get( resolvedArtifact );
173                 logMismatch( resolvedArtifact, depMgtDependency );
174             }
175             if ( !foundError )
176             {
177                 getLog().info( "   None" );
178             }
179         }
180         else
181         {
182             getLog().info( "   Nothing in DepMgt." );
183         }
184 
185 
186 
187         return foundError;
188     }
189 
190     /**
191      * Returns a map of the exclusions using the Dependency ManagementKey as the
192      * keyset.
193      * 
194      * @param exclusionList
195      *            to be added to the map.
196      * @return a map of the exclusions using the Dependency ManagementKey as the
197      *         keyset.
198      */
199     public Map addExclusions( List exclusionList )
200     {
201         Map exclusions = new HashMap();
202         if ( exclusionList != null )
203         {
204             Iterator exclusionIter = exclusionList.iterator();
205             while ( exclusionIter.hasNext() )
206             {
207                 Exclusion exclusion = (Exclusion) exclusionIter.next();
208                 exclusions.put( getExclusionKey( exclusion ), exclusion );
209             }
210         }
211         return exclusions;
212     }
213 
214     /**
215      * Returns a List of the artifacts that should have been excluded, but were
216      * found in the dependency tree.
217      * 
218      * @param exclusions
219      *            a map of the DependencyManagement exclusions, with the
220      *            ManagementKey as the key and Dependency as the value.
221      * @param allDependencyArtifacts
222      *            resolved artifacts to be compared.
223      * @return list of artifacts that should have been excluded.
224      */
225     public List getExclusionErrors( Map exclusions, Set allDependencyArtifacts )
226     {
227         List list = new ArrayList();
228 
229         Iterator iter = allDependencyArtifacts.iterator();
230         while ( iter.hasNext() )
231         {
232             Artifact artifact = (Artifact) iter.next();
233             if ( exclusions.containsKey( getExclusionKey( artifact ) ) )
234             {
235                 list.add( artifact );
236             }
237         }
238 
239         return list;
240     }
241 
242     public String getExclusionKey(Artifact artifact)
243     {
244         return artifact.getGroupId()+":"+artifact.getArtifactId();
245     }
246     
247     public String getExclusionKey(Exclusion ex)
248     {
249         return ex.getGroupId()+":"+ex.getArtifactId();
250     }
251     
252     /**
253      * Calculate the mismatches between the DependencyManagement and resolved
254      * artifacts
255      * 
256      * @param depMgtMap
257      *            contains the Dependency.GetManagementKey as the keyset for
258      *            quick lookup.
259      * @param allDependencyArtifacts
260      *            contains the set of all artifacts to compare.
261      * @return a map containing the resolved artifact as the key and the listed
262      *         dependency as the value.
263      */
264     public Map getMismatch( Map depMgtMap, Set allDependencyArtifacts )
265     {
266         Map mismatchMap = new HashMap();
267 
268         Iterator iter = allDependencyArtifacts.iterator();
269         while ( iter.hasNext() )
270         {
271             Artifact dependencyArtifact = (Artifact) iter.next();
272             Dependency depFromDepMgt = (Dependency) depMgtMap.get( getArtifactManagementKey( dependencyArtifact ) );
273             if ( depFromDepMgt != null )
274             {
275                
276                 //workaround for MNG-2961
277                 dependencyArtifact.isSnapshot();
278                 
279                 if (!depFromDepMgt.getVersion().equals( dependencyArtifact.getBaseVersion()) )
280                 {
281                     mismatchMap.put( dependencyArtifact, depFromDepMgt );
282                 }
283             }
284         }
285         return mismatchMap;
286     }
287 
288     /**
289      * This function displays the log to the screen showing the versions and
290      * information about the artifacts that don't match.
291      * 
292      * @param dependencyArtifact
293      *            the artifact that was resolved.
294      * @param dependencyFromDepMgt
295      *            the dependency listed in the DependencyManagement section.
296      * @throws MojoExecutionException
297      */
298     public void logMismatch( Artifact dependencyArtifact, Dependency dependencyFromDepMgt )
299         throws MojoExecutionException
300     {
301         if ( dependencyArtifact == null || dependencyFromDepMgt == null )
302         {
303             throw new MojoExecutionException( "Invalid params: Artifact:" + dependencyArtifact + " Dependency:"
304                 + dependencyFromDepMgt );
305         }
306 
307         getLog().info( "\tDependency: " + StringUtils.stripEnd(dependencyFromDepMgt.getManagementKey(),":") );
308         getLog().info( "\t\tDepMgt  : " + dependencyFromDepMgt.getVersion() );
309         getLog().info( "\t\tResolved: " + dependencyArtifact.getBaseVersion() );
310     }
311 
312     /**
313      * This function returns a string comparable with
314      * Dependency.GetManagementKey.
315      * 
316      * @param artifact
317      *            to gen the key for
318      * @return a string in the form: groupId:ArtifactId:Type[:Classifier]
319      */
320     public String getArtifactManagementKey( Artifact artifact )
321     {
322         return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getType()
323             + (( artifact.getClassifier() !=null ) ? ":" + artifact.getClassifier() : "" );
324     }
325 
326     /**
327      * @return the failBuild
328      */
329     public boolean isFailBuild()
330     {
331         return this.failBuild;
332     }
333 
334     /**
335      * @param theFailBuild
336      *            the failBuild to set
337      */
338     public void setFailBuild( boolean theFailBuild )
339     {
340         this.failBuild = theFailBuild;
341     }
342 
343     /**
344      * @return the project
345      */
346     public MavenProject getProject()
347     {
348         return this.project;
349     }
350 
351     /**
352      * @param theProject
353      *            the project to set
354      */
355     public void setProject( MavenProject theProject )
356     {
357         this.project = theProject;
358     }
359 
360     /**
361      * @return the ignoreDirect
362      */
363     public boolean isIgnoreDirect()
364     {
365         return this.ignoreDirect;
366     }
367 
368     /**
369      * @param theIgnoreDirect
370      *            the ignoreDirect to set
371      */
372     public void setIgnoreDirect( boolean theIgnoreDirect )
373     {
374         this.ignoreDirect = theIgnoreDirect;
375     }
376 }