1 package org.apache.maven.plugin.surefire;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import javax.annotation.Nonnull;
23 import javax.annotation.Nullable;
24
25 import java.util.Collection;
26 import java.util.Iterator;
27 import java.util.LinkedHashMap;
28 import java.util.LinkedHashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32
33 import org.apache.maven.artifact.Artifact;
34 import org.apache.maven.artifact.repository.ArtifactRepository;
35 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
36 import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
37 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
38 import org.apache.maven.artifact.resolver.ResolutionErrorHandler;
39 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
40 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
41 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
42 import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
43 import org.apache.maven.artifact.versioning.VersionRange;
44 import org.apache.maven.model.Dependency;
45 import org.apache.maven.model.Plugin;
46 import org.apache.maven.plugin.MojoExecutionException;
47 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
48 import org.apache.maven.repository.RepositorySystem;
49
50 import static java.util.Arrays.asList;
51 import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE;
52 import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE_PLUS_RUNTIME;
53 import static org.apache.maven.artifact.Artifact.SCOPE_RUNTIME;
54 import static org.apache.maven.artifact.ArtifactUtils.artifactMapByVersionlessId;
55 import static org.apache.maven.artifact.versioning.VersionRange.createFromVersionSpec;
56
57
58
59
60
61
62
63 final class SurefireDependencyResolver
64 {
65 static final String PROVIDER_GROUP_ID = "org.apache.maven.surefire";
66
67 private static final String[] PROVIDER_CLASSPATH_ORDER = {
68 "surefire-junit3",
69 "surefire-junit4",
70 "surefire-junit47",
71 "surefire-testng",
72 "surefire-junit-platform",
73 "surefire-api",
74 "surefire-logger-api",
75 "surefire-shared-utils",
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 ResolutionErrorHandler resolutionErrorHandler;
94
95 private final String pluginName;
96
97 private final boolean offline;
98
99 SurefireDependencyResolver( RepositorySystem repositorySystem, ConsoleLogger log,
100 ArtifactRepository localRepository,
101 List<ArtifactRepository> pluginRemoteRepositories,
102 List<ArtifactRepository> projectRemoteRepositories,
103 ResolutionErrorHandler resolutionErrorHandler,
104 String pluginName, boolean offline )
105 {
106 this.repositorySystem = repositorySystem;
107 this.log = log;
108 this.localRepository = localRepository;
109 this.pluginRemoteRepositories = pluginRemoteRepositories;
110 this.projectRemoteRepositories = projectRemoteRepositories;
111 this.resolutionErrorHandler = resolutionErrorHandler;
112 this.pluginName = pluginName;
113 this.offline = offline;
114 }
115
116 static boolean isWithinVersionSpec( @Nullable Artifact artifact, @Nonnull String versionSpec )
117 {
118 if ( artifact == null )
119 {
120 return false;
121 }
122 try
123 {
124 VersionRange range = createFromVersionSpec( versionSpec );
125 try
126 {
127 return range.containsVersion( artifact.getSelectedVersion() );
128 }
129 catch ( NullPointerException e )
130 {
131 return range.containsVersion( new DefaultArtifactVersion( artifact.getBaseVersion() ) );
132 }
133 }
134 catch ( InvalidVersionSpecificationException | OverConstrainedVersionException e )
135 {
136 throw new RuntimeException( "Bug in plugin. Please report with stacktrace" );
137 }
138 }
139
140 Map<String, Artifact> resolvePluginDependencies( Plugin plugin, Map<String, Artifact> pluginResolvedDependencies )
141 throws MojoExecutionException
142 {
143 Map<String, Artifact> resolved = new LinkedHashMap<>();
144 Collection<Dependency> pluginDependencies = plugin.getDependencies();
145
146 for ( Dependency dependency : pluginDependencies )
147 {
148 Artifact dependencyArtifact = repositorySystem.createDependencyArtifact( dependency );
149 ArtifactResolutionResult artifactResolutionResult = resolvePluginArtifact( dependencyArtifact );
150 for ( Artifact artifact : artifactResolutionResult.getArtifacts() )
151 {
152 String key = artifact.getGroupId() + ":" + artifact.getArtifactId();
153 Artifact resolvedPluginDependency = pluginResolvedDependencies.get( key );
154 if ( resolvedPluginDependency != null )
155 {
156 resolved.put( key, artifact );
157 }
158 }
159 }
160 return resolved;
161 }
162
163 ArtifactResolutionResult resolvePluginArtifact( Artifact artifact ) throws MojoExecutionException
164 {
165 return resolvePluginArtifact( artifact, new RuntimeArtifactFilter() );
166 }
167
168 ArtifactResolutionResult resolveProjectArtifact( Artifact artifact ) throws MojoExecutionException
169 {
170 return resolveProjectArtifact( artifact, new RuntimeArtifactFilter() );
171 }
172
173 private ArtifactResolutionResult resolvePluginArtifact( Artifact artifact, ArtifactFilter filter )
174 throws MojoExecutionException
175 {
176 return resolveArtifact( artifact, pluginRemoteRepositories, filter );
177 }
178
179 private ArtifactResolutionResult resolveProjectArtifact( Artifact artifact, ArtifactFilter filter )
180 throws MojoExecutionException
181 {
182 return resolveArtifact( artifact, projectRemoteRepositories, filter );
183 }
184
185 private ArtifactResolutionResult resolveArtifact( Artifact artifact, List<ArtifactRepository> repositories,
186 ArtifactFilter filter ) throws MojoExecutionException
187 {
188 ArtifactResolutionRequest request = new ArtifactResolutionRequest()
189 .setOffline( offline )
190 .setArtifact( artifact )
191 .setLocalRepository( localRepository )
192 .setResolveTransitively( true )
193 .setCollectionFilter( filter )
194 .setRemoteRepositories( repositories );
195
196 ArtifactResolutionResult result = repositorySystem.resolve( request );
197 try
198 {
199 resolutionErrorHandler.throwErrors( request, result );
200 }
201 catch ( ArtifactResolutionException e )
202 {
203 throw new MojoExecutionException( e.getMessage(), e );
204 }
205
206 return result;
207 }
208
209 @Nonnull
210 Set<Artifact> getProviderClasspath( String providerArtifactId, String providerVersion )
211 throws MojoExecutionException
212 {
213 Dependency provider = toProviderDependency( providerArtifactId, providerVersion );
214
215 Artifact providerArtifact = repositorySystem.createDependencyArtifact( provider );
216
217 ArtifactResolutionResult result = resolvePluginArtifact( providerArtifact );
218
219 if ( log.isDebugEnabled() )
220 {
221 for ( Artifact artifact : result.getArtifacts() )
222 {
223 String artifactPath = artifact.getFile().getAbsolutePath();
224 String scope = artifact.getScope();
225 log.debug( "Adding to " + pluginName + " test classpath: " + artifactPath + " Scope: " + scope );
226 }
227 }
228
229 return orderProviderArtifacts( result.getArtifacts() );
230 }
231
232 @Nonnull
233 Map<String, Artifact> getProviderClasspathAsMap( String providerArtifactId, String providerVersion )
234 throws MojoExecutionException
235 {
236 return artifactMapByVersionlessId( getProviderClasspath( providerArtifactId, providerVersion ) );
237 }
238
239
240
241
242 private static Set<Artifact> orderProviderArtifacts( Set<Artifact> providerArtifacts )
243 {
244 Set<Artifact> orderedProviderArtifacts = new LinkedHashSet<>();
245 for ( String order : PROVIDER_CLASSPATH_ORDER )
246 {
247 Iterator<Artifact> providerArtifactsIt = providerArtifacts.iterator();
248 while ( providerArtifactsIt.hasNext() )
249 {
250 Artifact providerArtifact = providerArtifactsIt.next();
251 if ( providerArtifact.getArtifactId().equals( order ) )
252 {
253 orderedProviderArtifacts.add( providerArtifact );
254 providerArtifactsIt.remove();
255 }
256 }
257 }
258 orderedProviderArtifacts.addAll( providerArtifacts );
259 return orderedProviderArtifacts;
260 }
261
262 private static Dependency toProviderDependency( String providerArtifactId, String providerVersion )
263 {
264 Dependency dependency = new Dependency();
265 dependency.setGroupId( PROVIDER_GROUP_ID );
266 dependency.setArtifactId( providerArtifactId );
267 dependency.setVersion( providerVersion );
268 dependency.setType( "jar" );
269 return dependency;
270 }
271
272 static class RuntimeArtifactFilter implements ArtifactFilter
273 {
274 private static final Collection<String> SCOPES =
275 asList( SCOPE_COMPILE, SCOPE_COMPILE_PLUS_RUNTIME, SCOPE_RUNTIME );
276
277 private final Artifact filter;
278
279 RuntimeArtifactFilter()
280 {
281 this( null );
282 }
283
284 RuntimeArtifactFilter( Artifact filter )
285 {
286 this.filter = filter;
287 }
288
289 @Override
290 public boolean include( Artifact artifact )
291 {
292 String scope = artifact.getScope();
293 return ( filter == null || artifact.equals( filter ) )
294 && !artifact.isOptional() && ( scope == null || SCOPES.contains( scope ) );
295 }
296 }
297 }