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