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