View Javadoc
1   package org.apache.maven.shared.artifact.filter.collection;
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 org.apache.maven.RepositoryUtils;
23  import org.apache.maven.artifact.Artifact;
24  import org.apache.maven.project.DefaultProjectBuildingRequest;
25  import org.apache.maven.project.DependencyResolutionResult;
26  import org.apache.maven.project.ProjectBuilder;
27  import org.apache.maven.project.ProjectBuildingException;
28  import org.apache.maven.project.ProjectBuildingRequest;
29  import org.apache.maven.project.ProjectBuildingResult;
30  
31  import java.lang.reflect.InvocationTargetException;
32  import java.util.LinkedHashSet;
33  import java.util.List;
34  import java.util.Set;
35  
36  /**
37   * This filter will exclude everything that is not a dependency of the selected artifact.
38   *
39   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
40   */
41  public class ArtifactTransitivityFilter
42      extends AbstractArtifactsFilter
43  {
44      /**
45       * List of dependencyConflictIds of transitiveArtifacts
46       */
47      private Set<String> transitiveArtifacts;
48  
49      /**
50       * <p>
51       * Use {@link org.apache.maven.execution.MavenSession#getProjectBuildingRequest()} to get the buildingRequest.
52       * The projectBuilder should be resolved with CDI.
53       * </p>
54       * <pre>
55       *   // For Mojo
56       *   &#64;Component
57       *   private ProjectBuilder projectBuilder;
58       *
59       *   // For Components
60       *   &#64;Requirement // or &#64;Inject
61       *   private ProjectBuilder projectBuilder;
62       * </pre>
63       *
64       * @param artifact        the artifact to resolve the dependencies from
65       * @param buildingRequest the buildingRequest
66       * @param projectBuilder  the projectBuilder
67       * @throws ProjectBuildingException if the project descriptor could not be successfully built
68       */
69      public ArtifactTransitivityFilter( Artifact artifact, ProjectBuildingRequest buildingRequest,
70                                         ProjectBuilder projectBuilder )
71          throws ProjectBuildingException
72      {
73          ProjectBuildingRequest request = new DefaultProjectBuildingRequest( buildingRequest );
74  
75          request.setResolveDependencies( true );
76  
77          ProjectBuildingResult buildingResult = projectBuilder.build( artifact, request );
78  
79          DependencyResolutionResult resolutionResult = buildingResult.getDependencyResolutionResult();
80          if ( resolutionResult != null )
81          {
82              if ( isMaven31() )
83              {
84                  try
85                  {
86                      @SuppressWarnings( "unchecked" ) List<org.eclipse.aether.graph.Dependency> dependencies =
87                          (List<org.eclipse.aether.graph.Dependency>) Invoker.invoke( resolutionResult,
88                                                                                      "getDependencies" );
89  
90                      for ( org.eclipse.aether.graph.Dependency dependency : dependencies )
91                      {
92                          Artifact mavenArtifact = 
93                                          (Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
94                                                                      org.eclipse.aether.artifact.Artifact.class,
95                                                                      dependency.getArtifact() );
96  
97                          transitiveArtifacts.add( mavenArtifact.getDependencyConflictId() );
98                      }
99                  }
100                 catch ( IllegalAccessException | InvocationTargetException | NoSuchMethodException e )
101                 {
102                     // don't want to pollute method signature with ReflectionExceptions
103                     throw new RuntimeException( e.getMessage(), e );
104                 }
105             }
106             else
107             {
108                 try
109                 {
110                     @SuppressWarnings( "unchecked" ) List<org.sonatype.aether.graph.Dependency> dependencies =
111                         (List<org.sonatype.aether.graph.Dependency>) Invoker.invoke( resolutionResult,
112                                                                                      "getDependencies" );
113 
114                     for ( org.sonatype.aether.graph.Dependency dependency : dependencies )
115                     {
116                         Artifact mavenArtifact = 
117                                         (Artifact) Invoker.invoke( RepositoryUtils.class, "toArtifact",
118                                                                     org.sonatype.aether.artifact.Artifact.class,
119                                                                     dependency.getArtifact() );
120 
121                         transitiveArtifacts.add( mavenArtifact.getDependencyConflictId() );
122                     }
123                 }
124                 catch ( IllegalAccessException | InvocationTargetException | NoSuchMethodException e )
125                 {
126                     // don't want to pollute method signature with ReflectionExceptions
127                     throw new RuntimeException( e.getMessage(), e );
128                 }
129             }
130         }
131     }
132 
133     /**
134      * @return true if the current Maven version is Maven 3.1.
135      */
136     protected static boolean isMaven31()
137     {
138         return canFindCoreClass( "org.eclipse.aether.artifact.Artifact" ); // Maven 3.1 specific
139     }
140 
141     private static boolean canFindCoreClass( String className )
142     {
143         try
144         {
145             Thread.currentThread().getContextClassLoader().loadClass( className );
146 
147             return true;
148         }
149         catch ( ClassNotFoundException e )
150         {
151             return false;
152         }
153     }
154 
155     /**
156      * {@inheritDoc}
157      */
158     public Set<Artifact> filter( Set<Artifact> artifacts )
159     {
160 
161         Set<Artifact> result = new LinkedHashSet<>();
162         for ( Artifact artifact : artifacts )
163         {
164             if ( artifactIsATransitiveDependency( artifact ) )
165             {
166                 result.add( artifact );
167             }
168         }
169         return result;
170     }
171 
172     /**
173      * Compares the artifact to the list of dependencies to see if it is directly included by this project
174      *
175      * @param artifact representing the item to compare.
176      * @return true if artifact is a transitive dependency
177      */
178     public boolean artifactIsATransitiveDependency( Artifact artifact )
179     {
180         return transitiveArtifacts.contains( artifact.getDependencyConflictId() );
181     }
182 }