View Javadoc
1   package org.apache.maven.plugins.dependency.fromDependencies;
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.io.File;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.LinkedHashSet;
26  import java.util.Set;
27  
28  import org.apache.maven.artifact.Artifact;
29  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
30  import org.apache.maven.plugin.MojoExecutionException;
31  import org.apache.maven.plugins.annotations.Component;
32  import org.apache.maven.plugins.annotations.Parameter;
33  import org.apache.maven.plugins.dependency.AbstractDependencyMojo;
34  import org.apache.maven.plugins.dependency.utils.DependencyStatusSets;
35  import org.apache.maven.plugins.dependency.utils.DependencyUtil;
36  import org.apache.maven.plugins.dependency.utils.translators.ArtifactTranslator;
37  import org.apache.maven.plugins.dependency.utils.translators.ClassifierTypeTranslator;
38  import org.apache.maven.project.MavenProject;
39  import org.apache.maven.project.ProjectBuilder;
40  import org.apache.maven.project.ProjectBuildingException;
41  import org.apache.maven.project.ProjectBuildingRequest;
42  import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
43  import org.apache.maven.shared.artifact.filter.collection.ArtifactIdFilter;
44  import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
45  import org.apache.maven.shared.artifact.filter.collection.ClassifierFilter;
46  import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
47  import org.apache.maven.shared.artifact.filter.collection.GroupIdFilter;
48  import org.apache.maven.shared.artifact.filter.collection.ProjectTransitivityFilter;
49  import org.apache.maven.shared.artifact.filter.collection.ScopeFilter;
50  import org.apache.maven.shared.artifact.filter.collection.TypeFilter;
51  import org.apache.maven.shared.transfer.artifact.ArtifactCoordinate;
52  import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
53  import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException;
54  import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
55  import org.apache.maven.shared.transfer.repository.RepositoryManager;
56  import org.codehaus.plexus.util.StringUtils;
57  
58  /**
59   * Class that encapsulates the plugin parameters, and contains methods that handle dependency filtering
60   *
61   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
62   * @see org.apache.maven.plugins.dependency.AbstractDependencyMojo
63   */
64  public abstract class AbstractDependencyFilterMojo
65      extends AbstractDependencyMojo
66  {
67      @Component
68      private ArtifactResolver artifactResolver;
69  
70      @Component
71      private DependencyResolver dependencyResolver;
72  
73      @Component
74      private RepositoryManager repositoryManager;
75  
76      /**
77       * Overwrite release artifacts
78       *
79       * @since 1.0
80       */
81      @Parameter( property = "overWriteReleases", defaultValue = "false" )
82      protected boolean overWriteReleases;
83  
84      /**
85       * Overwrite snapshot artifacts
86       *
87       * @since 1.0
88       */
89      @Parameter( property = "overWriteSnapshots", defaultValue = "false" )
90      protected boolean overWriteSnapshots;
91  
92      /**
93       * Overwrite artifacts that don't exist or are older than the source.
94       *
95       * @since 2.0
96       */
97      @Parameter( property = "overWriteIfNewer", defaultValue = "true" )
98      protected boolean overWriteIfNewer;
99  
100     /**
101      * If we should exclude transitive dependencies
102      *
103      * @since 2.0
104      */
105     @Parameter( property = "excludeTransitive", defaultValue = "false" )
106     protected boolean excludeTransitive;
107 
108     /**
109      * Comma Separated list of Types to include. Empty String indicates include everything (default).
110      *
111      * @since 2.0
112      */
113     @Parameter( property = "includeTypes", defaultValue = "" )
114     protected String includeTypes;
115 
116     /**
117      * Comma Separated list of Types to exclude. Empty String indicates don't exclude anything (default).
118      *
119      * @since 2.0
120      */
121     @Parameter( property = "excludeTypes", defaultValue = "" )
122     protected String excludeTypes;
123 
124     /**
125      * Scope threshold to include. An empty string indicates include all dependencies (default).<br>
126      * The scope threshold value being interpreted is the scope as
127      * Maven filters for creating a classpath, not as specified in the pom. In summary:
128      * <ul>
129      * <li><code>runtime</code> include scope gives runtime and compile dependencies,</li>
130      * <li><code>compile</code> include scope gives compile, provided, and system dependencies,</li>
131      * <li><code>test</code> include scope gives all dependencies (equivalent to default),</li>
132      * <li><code>provided</code> include scope just gives provided dependencies,</li>
133      * <li><code>system</code> include scope just gives system dependencies.</li>
134      * </ul>
135      *
136      * @since 2.0
137      */
138     @Parameter( property = "includeScope", defaultValue = "" )
139     protected String includeScope;
140 
141     /**
142      * Scope threshold to exclude, if no value is defined for include.
143      * An empty string indicates no dependencies (default).<br>
144      * The scope threshold value being interpreted is the scope as
145      * Maven filters for creating a classpath, not as specified in the pom. In summary:
146      * <ul>
147      * <li><code>runtime</code> exclude scope excludes runtime and compile dependencies,</li>
148      * <li><code>compile</code> exclude scope excludes compile, provided, and system dependencies,</li>
149      * <li><code>test</code> exclude scope excludes all dependencies, then not really a legitimate option: it will
150      * fail, you probably meant to configure includeScope = compile</li>
151      * <li><code>provided</code> exclude scope just excludes provided dependencies,</li>
152      * <li><code>system</code> exclude scope just excludes system dependencies.</li>
153      * </ul>
154      *
155      * @since 2.0
156      */
157     @Parameter( property = "excludeScope", defaultValue = "" )
158     protected String excludeScope;
159 
160     /**
161      * Comma Separated list of Classifiers to include. Empty String indicates include everything (default).
162      *
163      * @since 2.0
164      */
165     @Parameter( property = "includeClassifiers", defaultValue = "" )
166     protected String includeClassifiers;
167 
168     /**
169      * Comma Separated list of Classifiers to exclude. Empty String indicates don't exclude anything (default).
170      *
171      * @since 2.0
172      */
173     @Parameter( property = "excludeClassifiers", defaultValue = "" )
174     protected String excludeClassifiers;
175 
176     /**
177      * Specify classifier to look for. Example: sources
178      *
179      * @since 2.0
180      */
181     @Parameter( property = "classifier", defaultValue = "" )
182     protected String classifier;
183 
184     /**
185      * Specify type to look for when constructing artifact based on classifier. Example: java-source,jar,war
186      *
187      * @since 2.0
188      */
189     @Parameter( property = "type", defaultValue = "" )
190     protected String type;
191 
192     /**
193      * Comma separated list of Artifact names to exclude.
194      *
195      * @since 2.0
196      */
197     @Parameter( property = "excludeArtifactIds", defaultValue = "" )
198     protected String excludeArtifactIds;
199 
200     /**
201      * Comma separated list of Artifact names to include. Empty String indicates include everything (default).
202      *
203      * @since 2.0
204      */
205     @Parameter( property = "includeArtifactIds", defaultValue = "" )
206     protected String includeArtifactIds;
207 
208     /**
209      * Comma separated list of GroupId Names to exclude.
210      *
211      * @since 2.0
212      */
213     @Parameter( property = "excludeGroupIds", defaultValue = "" )
214     protected String excludeGroupIds;
215 
216     /**
217      * Comma separated list of GroupIds to include. Empty String indicates include everything (default).
218      *
219      * @since 2.0
220      */
221     @Parameter( property = "includeGroupIds", defaultValue = "" )
222     protected String includeGroupIds;
223 
224     /**
225      * Directory to store flag files
226      *
227      * @since 2.0
228      */
229     //CHECKSTYLE_OFF: LineLength
230     @Parameter( property = "markersDirectory", defaultValue = "${project.build.directory}/dependency-maven-plugin-markers" )
231     //CHECKSTYLE_ON: LineLength
232     protected File markersDirectory;
233 
234     /**
235      * Prepend the groupId during copy.
236      *
237      * @since 2.2
238      */
239     @Parameter( property = "mdep.prependGroupId", defaultValue = "false" )
240     protected boolean prependGroupId = false;
241 
242     @Component
243     private ProjectBuilder projectBuilder;
244 
245     @Component
246     private ArtifactHandlerManager artifactHandlerManager;
247 
248     /**
249      * Return an {@link ArtifactsFilter} indicating which artifacts must be filtered out.
250      *
251      * @return an {@link ArtifactsFilter} indicating which artifacts must be filtered out.
252      */
253     protected abstract ArtifactsFilter getMarkedArtifactFilter();
254 
255     /**
256      * Retrieves dependencies, either direct only or all including transitive.
257      *
258      * @param stopOnFailure true to fail if resolution does not work or false not to fail.
259      * @return A set of artifacts
260      * @throws MojoExecutionException in case of errors.
261      */
262     protected Set<Artifact> getResolvedDependencies( boolean stopOnFailure )
263         throws MojoExecutionException
264 
265     {
266         DependencyStatusSets status = getDependencySets( stopOnFailure );
267 
268         return status.getResolvedDependencies();
269     }
270 
271     /**
272      * @param stopOnFailure true/false.
273      * @return {@link DependencyStatusSets}
274      * @throws MojoExecutionException in case of an error.
275      */
276     protected DependencyStatusSets getDependencySets( boolean stopOnFailure )
277         throws MojoExecutionException
278     {
279         return getDependencySets( stopOnFailure, false );
280     }
281 
282     /**
283      * Method creates filters and filters the projects dependencies. This method also transforms the dependencies if
284      * classifier is set. The dependencies are filtered in least specific to most specific order
285      *
286      * @param stopOnFailure true to fail if artifacts can't be resolved false otherwise.
287      * @param includeParents <code>true</code> if parents should be included or not <code>false</code>.
288      * @return DependencyStatusSets - Bean of TreeSets that contains information on the projects dependencies
289      * @throws MojoExecutionException in case of errors.
290      */
291     protected DependencyStatusSets getDependencySets( boolean stopOnFailure, boolean includeParents )
292         throws MojoExecutionException
293     {
294         // add filters in well known order, least specific to most specific
295         FilterArtifacts filter = new FilterArtifacts();
296 
297         filter.addFilter( new ProjectTransitivityFilter( getProject().getDependencyArtifacts(),
298                                                          this.excludeTransitive ) );
299 
300         if ( "test".equals( this.excludeScope ) )
301         {
302             throw new MojoExecutionException( "Excluding every artifact inside 'test' resolution scope means "
303                 + "excluding everything: you probably want includeScope='compile', "
304                 + "read parameters documentation for detailed explanations" );
305         }
306         filter.addFilter( new ScopeFilter( DependencyUtil.cleanToBeTokenizedString( this.includeScope ),
307                                            DependencyUtil.cleanToBeTokenizedString( this.excludeScope ) ) );
308 
309         filter.addFilter( new TypeFilter( DependencyUtil.cleanToBeTokenizedString( this.includeTypes ),
310                                           DependencyUtil.cleanToBeTokenizedString( this.excludeTypes ) ) );
311 
312         filter.addFilter( new ClassifierFilter( DependencyUtil.cleanToBeTokenizedString( this.includeClassifiers ),
313                                                 DependencyUtil.cleanToBeTokenizedString( this.excludeClassifiers ) ) );
314 
315         filter.addFilter( new GroupIdFilter( DependencyUtil.cleanToBeTokenizedString( this.includeGroupIds ),
316                                              DependencyUtil.cleanToBeTokenizedString( this.excludeGroupIds ) ) );
317 
318         filter.addFilter( new ArtifactIdFilter( DependencyUtil.cleanToBeTokenizedString( this.includeArtifactIds ),
319                                                 DependencyUtil.cleanToBeTokenizedString( this.excludeArtifactIds ) ) );
320 
321         // start with all artifacts.
322         Set<Artifact> artifacts = getProject().getArtifacts();
323 
324         if ( includeParents )
325         {
326             // add dependencies parents
327             for ( Artifact dep : new ArrayList<>( artifacts ) )
328             {
329                 addParentArtifacts( buildProjectFromArtifact( dep ), artifacts );
330             }
331 
332             // add current project parent
333             addParentArtifacts( getProject(), artifacts );
334         }
335 
336         // perform filtering
337         try
338         {
339             artifacts = filter.filter( artifacts );
340         }
341         catch ( ArtifactFilterException e )
342         {
343             throw new MojoExecutionException( e.getMessage(), e );
344         }
345 
346         // transform artifacts if classifier is set
347         DependencyStatusSets status;
348         if ( StringUtils.isNotEmpty( classifier ) )
349         {
350             status = getClassifierTranslatedDependencies( artifacts, stopOnFailure );
351         }
352         else
353         {
354             status = filterMarkedDependencies( artifacts );
355         }
356 
357         return status;
358     }
359 
360     private MavenProject buildProjectFromArtifact( Artifact artifact )
361         throws MojoExecutionException
362     {
363         try
364         {
365             return projectBuilder.build( artifact, session.getProjectBuildingRequest() ).getProject();
366         }
367         catch ( ProjectBuildingException e )
368         {
369             throw new MojoExecutionException( e.getMessage(), e );
370         }
371     }
372 
373     private void addParentArtifacts( MavenProject project, Set<Artifact> artifacts )
374         throws MojoExecutionException
375     {
376         while ( project.hasParent() )
377         {
378             project = project.getParent();
379 
380             if ( artifacts.contains( project.getArtifact() ) )
381             {
382                 // artifact already in the set
383                 break;
384             }
385             try
386             {
387                 ProjectBuildingRequest buildingRequest = newResolveArtifactProjectBuildingRequest();
388 
389                 Artifact resolvedArtifact =
390                     artifactResolver.resolveArtifact( buildingRequest, project.getArtifact() ).getArtifact();
391 
392                 artifacts.add( resolvedArtifact );
393             }
394             catch ( ArtifactResolverException e )
395             {
396                 throw new MojoExecutionException( e.getMessage(), e );
397             }
398         }
399     }
400 
401     /**
402      * Transform artifacts
403      *
404      * @param artifacts set of artifacts {@link Artifact}.
405      * @param stopOnFailure true/false.
406      * @return DependencyStatusSets - Bean of TreeSets that contains information on the projects dependencies
407      * @throws MojoExecutionException in case of an error.
408      */
409     protected DependencyStatusSets getClassifierTranslatedDependencies( Set<Artifact> artifacts, boolean stopOnFailure )
410         throws MojoExecutionException
411     {
412         Set<Artifact> unResolvedArtifacts = new LinkedHashSet<>();
413         Set<Artifact> resolvedArtifacts = artifacts;
414         DependencyStatusSets status = new DependencyStatusSets();
415 
416         // possibly translate artifacts into a new set of artifacts based on the
417         // classifier and type
418         // if this did something, we need to resolve the new artifacts
419         if ( StringUtils.isNotEmpty( classifier ) )
420         {
421             ArtifactTranslator translator =
422                 new ClassifierTypeTranslator( artifactHandlerManager, this.classifier, this.type );
423             Collection<ArtifactCoordinate> coordinates = translator.translate( artifacts, getLog() );
424 
425             status = filterMarkedDependencies( artifacts );
426 
427             // the unskipped artifacts are in the resolved set.
428             artifacts = status.getResolvedDependencies();
429 
430             // resolve the rest of the artifacts
431             resolvedArtifacts = resolve( new LinkedHashSet<>( coordinates ), stopOnFailure );
432 
433             // calculate the artifacts not resolved.
434             unResolvedArtifacts.addAll( artifacts );
435             unResolvedArtifacts.removeAll( resolvedArtifacts );
436         }
437 
438         // return a bean of all 3 sets.
439         status.setResolvedDependencies( resolvedArtifacts );
440         status.setUnResolvedDependencies( unResolvedArtifacts );
441 
442         return status;
443     }
444 
445     /**
446      * Filter the marked dependencies
447      *
448      * @param artifacts The artifacts set {@link Artifact}.
449      * @return status set {@link DependencyStatusSets}.
450      * @throws MojoExecutionException in case of an error.
451      */
452     protected DependencyStatusSets filterMarkedDependencies( Set<Artifact> artifacts )
453         throws MojoExecutionException
454     {
455         // remove files that have markers already
456         FilterArtifacts filter = new FilterArtifacts();
457         filter.clearFilters();
458         filter.addFilter( getMarkedArtifactFilter() );
459 
460         Set<Artifact> unMarkedArtifacts;
461         try
462         {
463             unMarkedArtifacts = filter.filter( artifacts );
464         }
465         catch ( ArtifactFilterException e )
466         {
467             throw new MojoExecutionException( e.getMessage(), e );
468         }
469 
470         // calculate the skipped artifacts
471         Set<Artifact> skippedArtifacts = new LinkedHashSet<>();
472         skippedArtifacts.addAll( artifacts );
473         skippedArtifacts.removeAll( unMarkedArtifacts );
474 
475         return new DependencyStatusSets( unMarkedArtifacts, null, skippedArtifacts );
476     }
477 
478     /**
479      * @param coordinates The set of artifact coordinates{@link ArtifactCoordinate}.
480      * @param stopOnFailure <code>true</code> if we should fail with exception if an artifact couldn't be resolved
481      *            <code>false</code> otherwise.
482      * @return the resolved artifacts. {@link Artifact}.
483      * @throws MojoExecutionException in case of error.
484      */
485     protected Set<Artifact> resolve( Set<ArtifactCoordinate> coordinates, boolean stopOnFailure )
486         throws MojoExecutionException
487     {
488         ProjectBuildingRequest buildingRequest = newResolveArtifactProjectBuildingRequest();
489 
490         Set<Artifact> resolvedArtifacts = new LinkedHashSet<>();
491         for ( ArtifactCoordinate coordinate : coordinates )
492         {
493             try
494             {
495                 Artifact artifact = artifactResolver.resolveArtifact( buildingRequest, coordinate ).getArtifact();
496                 resolvedArtifacts.add( artifact );
497             }
498             catch ( ArtifactResolverException ex )
499             {
500                 // an error occurred during resolution, log it an continue
501                 getLog().debug( "error resolving: " + coordinate );
502                 getLog().debug( ex );
503                 if ( stopOnFailure )
504                 {
505                     throw new MojoExecutionException( "error resolving: " + coordinate, ex );
506                 }
507             }
508         }
509         return resolvedArtifacts;
510     }
511 
512     /**
513      * @return Returns the markersDirectory.
514      */
515     public File getMarkersDirectory()
516     {
517         return this.markersDirectory;
518     }
519 
520     /**
521      * @param theMarkersDirectory The markersDirectory to set.
522      */
523     public void setMarkersDirectory( File theMarkersDirectory )
524     {
525         this.markersDirectory = theMarkersDirectory;
526     }
527 
528     // TODO: Set marker files.
529 
530     /**
531      * @return true, if the groupId should be prepended to the filename.
532      */
533     public boolean isPrependGroupId()
534     {
535         return prependGroupId;
536     }
537 
538     /**
539      * @param prependGroupId - true if the groupId must be prepended during the copy.
540      */
541     public void setPrependGroupId( boolean prependGroupId )
542     {
543         this.prependGroupId = prependGroupId;
544     }
545 
546     /**
547      * @return {@link #artifactResolver}
548      */
549     protected final ArtifactResolver getArtifactResolver()
550     {
551         return artifactResolver;
552     }
553 
554     /**
555      * @return {@link #dependencyResolver}
556      */
557     protected final DependencyResolver getDependencyResolver()
558     {
559         return dependencyResolver;
560     }
561 
562     /**
563      * @return {@link #repositoryManager}
564      */
565     protected final RepositoryManager getRepositoryManager()
566     {
567         return repositoryManager;
568     }
569 }