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