View Javadoc
1   package org.apache.maven.plugin.surefire;
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.util.Collection;
23  import java.util.Iterator;
24  import java.util.LinkedHashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  
29  import org.apache.maven.artifact.Artifact;
30  import org.apache.maven.artifact.repository.ArtifactRepository;
31  import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
32  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
33  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
34  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
35  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
36  import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
37  import org.apache.maven.artifact.versioning.VersionRange;
38  import org.apache.maven.model.Dependency;
39  import org.apache.maven.plugin.MojoExecutionException;
40  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
41  import org.apache.maven.project.ProjectBuildingRequest;
42  import org.apache.maven.repository.RepositorySystem;
43  import org.apache.maven.shared.artifact.filter.resolve.ScopeFilter;
44  import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
45  import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
46  import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolverException;
47  
48  import javax.annotation.Nonnull;
49  import javax.annotation.Nullable;
50  
51  import static java.util.Arrays.asList;
52  import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE;
53  import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE_PLUS_RUNTIME;
54  import static org.apache.maven.artifact.Artifact.SCOPE_RUNTIME;
55  import static org.apache.maven.artifact.ArtifactUtils.artifactMapByVersionlessId;
56  import static org.apache.maven.artifact.versioning.VersionRange.createFromVersionSpec;
57  
58  /**
59   * Does dependency resolution and artifact handling for the surefire plugin.
60   *
61   * @author Stephen Connolly
62   * @author Kristian Rosenvold
63   */
64  final class SurefireDependencyResolver
65  {
66      static final String PROVIDER_GROUP_ID = "org.apache.maven.surefire";
67  
68      private static final String[] PROVIDER_CLASSPATH_ORDER = {
69              "surefire-junit3",
70              "surefire-junit4",
71              "surefire-junit47",
72              "surefire-testng",
73              "surefire-junit-platform",
74              "surefire-api",
75              "surefire-logger-api",
76              "common-java5",
77              "common-junit3",
78              "common-junit4",
79              "common-junit48",
80              "common-testng-utils"
81      };
82  
83      private final RepositorySystem repositorySystem;
84  
85      private final ConsoleLogger log;
86  
87      private final ArtifactRepository localRepository;
88  
89      private final List<ArtifactRepository> pluginRemoteRepositories;
90  
91      private final List<ArtifactRepository> projectRemoteRepositories;
92  
93      private final String pluginName;
94  
95      private final DependencyResolver depencencyResolver;
96  
97      SurefireDependencyResolver( RepositorySystem repositorySystem, ConsoleLogger log,
98                                  ArtifactRepository localRepository,
99                                  List<ArtifactRepository> pluginRemoteRepositories,
100                                 List<ArtifactRepository> projectRemoteRepositories, String pluginName,
101                                 DependencyResolver depencencyResolver )
102     {
103         this.repositorySystem = repositorySystem;
104         this.log = log;
105         this.localRepository = localRepository;
106         this.pluginRemoteRepositories = pluginRemoteRepositories;
107         this.projectRemoteRepositories = projectRemoteRepositories;
108         this.pluginName = pluginName;
109         this.depencencyResolver = depencencyResolver;
110     }
111 
112     static boolean isWithinVersionSpec( @Nullable Artifact artifact, @Nonnull String versionSpec )
113     {
114         if ( artifact == null )
115         {
116             return false;
117         }
118         try
119         {
120             VersionRange range = createFromVersionSpec( versionSpec );
121             try
122             {
123                 return range.containsVersion( artifact.getSelectedVersion() );
124             }
125             catch ( NullPointerException e )
126             {
127                 return range.containsVersion( new DefaultArtifactVersion( artifact.getBaseVersion() ) );
128             }
129         }
130         catch ( InvalidVersionSpecificationException | OverConstrainedVersionException e )
131         {
132             throw new RuntimeException( "Bug in plugin. Please report with stacktrace" );
133         }
134     }
135 
136     Set<Artifact> resolvePluginDependencies( ProjectBuildingRequest request, Collection<Dependency> pluginDependencies )
137             throws MojoExecutionException
138     {
139         try
140         {
141             Iterable<ArtifactResult> resolvedPluginDependencies = depencencyResolver.resolveDependencies( request,
142                     pluginDependencies, null, ScopeFilter.including( SCOPE_COMPILE, SCOPE_RUNTIME ) );
143 
144             Set<Artifact> resolved = new LinkedHashSet<>();
145             for ( ArtifactResult resolvedPluginDependency : resolvedPluginDependencies )
146             {
147                 resolved.add( resolvedPluginDependency.getArtifact() );
148             }
149             return resolved;
150         }
151         catch ( DependencyResolverException e )
152         {
153             throw new MojoExecutionException( e.getLocalizedMessage(), e );
154         }
155     }
156 
157     ArtifactResolutionResult resolvePluginArtifact( Artifact artifact )
158     {
159         return resolveArtifact( artifact, pluginRemoteRepositories );
160     }
161 
162     ArtifactResolutionResult resolveProjectArtifact( Artifact artifact )
163     {
164         return resolveArtifact( artifact, projectRemoteRepositories );
165     }
166 
167     private ArtifactResolutionResult resolveArtifact( Artifact artifact, List<ArtifactRepository> repositories )
168     {
169         ArtifactResolutionRequest request = new ArtifactResolutionRequest()
170                 .setArtifact( artifact )
171                 .setLocalRepository( localRepository )
172                 .setResolveTransitively( true )
173                 .setCollectionFilter( new RuntimeArtifactFilter() )
174                 .setRemoteRepositories( repositories );
175 
176         return repositorySystem.resolve( request );
177     }
178 
179     @Nonnull
180     Set<Artifact> getProviderClasspath( String providerArtifactId, String providerVersion )
181     {
182         Dependency provider = toProviderDependency( providerArtifactId, providerVersion );
183 
184         Artifact providerArtifact = repositorySystem.createDependencyArtifact( provider );
185 
186         ArtifactResolutionResult result = resolvePluginArtifact( providerArtifact );
187 
188         if ( log.isDebugEnabled() )
189         {
190             for ( Artifact artifact : result.getArtifacts() )
191             {
192                 String artifactPath = artifact.getFile().getAbsolutePath();
193                 String scope = artifact.getScope();
194                 log.debug( "Adding to " + pluginName + " test classpath: " + artifactPath + " Scope: " + scope );
195             }
196         }
197 
198         return orderProviderArtifacts( result.getArtifacts() );
199     }
200 
201     @Nonnull
202     Map<String, Artifact> getProviderClasspathAsMap( String providerArtifactId, String providerVersion )
203     {
204         return artifactMapByVersionlessId( getProviderClasspath( providerArtifactId, providerVersion ) );
205     }
206 
207     Set<Artifact> addProviderToClasspath( Map<String, Artifact> pluginArtifactMap, Artifact mojoPluginArtifact,
208                                           Artifact surefireApi, Artifact surefireLoggerApi )
209     {
210         Set<Artifact> providerArtifacts = new LinkedHashSet<>();
211         ArtifactResolutionResult artifactResolutionResult = resolvePluginArtifact( mojoPluginArtifact );
212         for ( Artifact artifact : pluginArtifactMap.values() )
213         {
214             if ( !artifactResolutionResult.getArtifacts().contains( artifact ) )
215             {
216                 providerArtifacts.add( artifact );
217                 for ( Artifact dependency : resolvePluginArtifact( artifact ).getArtifacts() )
218                 {
219                     String groupId = dependency.getGroupId();
220                     String artifactId = dependency.getArtifactId();
221                     if ( groupId.equals( surefireApi.getGroupId() )
222                             && artifactId.equals( surefireApi.getArtifactId() ) )
223                     {
224                         providerArtifacts.add( surefireApi );
225                     }
226                     else if ( groupId.equals( surefireLoggerApi.getGroupId() )
227                             && artifactId.equals( surefireLoggerApi.getArtifactId() ) )
228                     {
229                         providerArtifacts.add( surefireLoggerApi );
230                     }
231                 }
232             }
233         }
234         return orderProviderArtifacts( providerArtifacts );
235     }
236 
237     private static Set<Artifact> orderProviderArtifacts( Set<Artifact> providerArtifacts )
238     {
239         Set<Artifact> orderedProviderArtifacts = new LinkedHashSet<>();
240         for ( String order : PROVIDER_CLASSPATH_ORDER )
241         {
242             Iterator<Artifact> providerArtifactsIt = providerArtifacts.iterator();
243             while ( providerArtifactsIt.hasNext() )
244             {
245                 Artifact providerArtifact = providerArtifactsIt.next();
246                 if ( providerArtifact.getArtifactId().equals( order ) )
247                 {
248                     orderedProviderArtifacts.add( providerArtifact );
249                     providerArtifactsIt.remove();
250                 }
251             }
252         }
253         orderedProviderArtifacts.addAll( providerArtifacts );
254         return orderedProviderArtifacts;
255     }
256 
257     private static Dependency toProviderDependency( String providerArtifactId, String providerVersion )
258     {
259         Dependency dependency = new Dependency();
260         dependency.setGroupId( PROVIDER_GROUP_ID );
261         dependency.setArtifactId( providerArtifactId );
262         dependency.setVersion( providerVersion );
263         dependency.setType( "jar" );
264         return dependency;
265     }
266 
267     static class RuntimeArtifactFilter implements ArtifactFilter
268     {
269         private static final Collection<String> SCOPES =
270                 asList( SCOPE_COMPILE, SCOPE_COMPILE_PLUS_RUNTIME, SCOPE_RUNTIME );
271 
272         @Override
273         public boolean include( Artifact artifact )
274         {
275             String scope = artifact.getScope();
276             return !artifact.isOptional() && ( scope == null || SCOPES.contains( scope ) );
277         }
278     }
279 }