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.plugin.surefire;
20  
21  import javax.annotation.Nonnull;
22  import javax.annotation.Nullable;
23  import javax.inject.Inject;
24  import javax.inject.Named;
25  import javax.inject.Singleton;
26  
27  import java.util.Collection;
28  import java.util.Iterator;
29  import java.util.LinkedHashMap;
30  import java.util.LinkedHashSet;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  import java.util.stream.Collectors;
35  
36  import org.apache.maven.RepositoryUtils;
37  import org.apache.maven.artifact.Artifact;
38  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
39  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
40  import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
41  import org.apache.maven.artifact.versioning.VersionRange;
42  import org.apache.maven.model.Dependency;
43  import org.apache.maven.model.Plugin;
44  import org.apache.maven.plugin.MojoExecutionException;
45  import org.eclipse.aether.RepositorySystem;
46  import org.eclipse.aether.RepositorySystemSession;
47  import org.eclipse.aether.collection.CollectRequest;
48  import org.eclipse.aether.repository.RemoteRepository;
49  import org.eclipse.aether.resolution.ArtifactResult;
50  import org.eclipse.aether.resolution.DependencyRequest;
51  import org.eclipse.aether.resolution.DependencyResolutionException;
52  import org.eclipse.aether.resolution.DependencyResult;
53  import org.eclipse.aether.util.artifact.JavaScopes;
54  import org.eclipse.aether.util.filter.DependencyFilterUtils;
55  
56  import static org.apache.maven.artifact.ArtifactUtils.artifactMapByVersionlessId;
57  import static org.apache.maven.artifact.versioning.VersionRange.createFromVersionSpec;
58  
59  /**
60   * Does dependency resolution and artifact handling for the surefire plugin.
61   *
62   * @author Stephen Connolly
63   * @author Kristian Rosenvold
64   */
65  @Named
66  @Singleton
67  class SurefireDependencyResolver {
68  
69      static final String PROVIDER_GROUP_ID = "org.apache.maven.surefire";
70  
71      private static final String[] PROVIDER_CLASSPATH_ORDER = {
72          "surefire-junit3",
73          "surefire-junit4",
74          "surefire-junit47",
75          "surefire-testng",
76          "surefire-junit-platform",
77          "surefire-api",
78          "surefire-logger-api",
79          "surefire-shared-utils",
80          "common-java5",
81          "common-junit3",
82          "common-junit4",
83          "common-junit48",
84          "common-testng-utils"
85      };
86  
87      private final RepositorySystem repositorySystem;
88  
89      @Inject
90      SurefireDependencyResolver(RepositorySystem repositorySystem) {
91          this.repositorySystem = repositorySystem;
92      }
93  
94      static boolean isWithinVersionSpec(@Nullable Artifact artifact, @Nonnull String versionSpec) {
95          if (artifact == null) {
96              return false;
97          }
98          try {
99              VersionRange range = createFromVersionSpec(versionSpec);
100             try {
101                 return range.containsVersion(artifact.getSelectedVersion());
102             } catch (NullPointerException e) {
103                 return range.containsVersion(new DefaultArtifactVersion(artifact.getBaseVersion()));
104             }
105         } catch (InvalidVersionSpecificationException | OverConstrainedVersionException e) {
106             throw new RuntimeException("Bug in plugin. Please report with stacktrace");
107         }
108     }
109 
110     Map<String, Artifact> resolvePluginDependencies(
111             RepositorySystemSession session,
112             List<RemoteRepository> repositories,
113             Plugin plugin,
114             Map<String, Artifact> pluginResolvedDependencies)
115             throws MojoExecutionException {
116         Map<String, Artifact> resolved = new LinkedHashMap<>();
117         Collection<Dependency> pluginDependencies = plugin.getDependencies();
118 
119         for (Dependency dependency : pluginDependencies) {
120             Set<Artifact> artifacts = resolveDependencies(
121                     session, repositories, RepositoryUtils.toDependency(dependency, session.getArtifactTypeRegistry()));
122             for (Artifact artifact : artifacts) {
123                 String key = artifact.getGroupId() + ":" + artifact.getArtifactId();
124                 Artifact resolvedPluginDependency = pluginResolvedDependencies.get(key);
125                 if (resolvedPluginDependency != null) {
126                     resolved.put(key, artifact);
127                 }
128             }
129         }
130         return resolved;
131     }
132 
133     public Set<Artifact> resolveArtifacts(
134             RepositorySystemSession session, List<RemoteRepository> repositories, Artifact artifact)
135             throws MojoExecutionException {
136         return resolveDependencies(session, repositories, RepositoryUtils.toDependency(artifact, null));
137     }
138 
139     private Set<Artifact> resolveDependencies(
140             RepositorySystemSession session,
141             List<RemoteRepository> repositories,
142             org.eclipse.aether.graph.Dependency dependency)
143             throws MojoExecutionException {
144 
145         try {
146 
147             CollectRequest collectRequest = new CollectRequest();
148             collectRequest.setRoot(dependency);
149             collectRequest.setRepositories(repositories);
150 
151             DependencyRequest request = new DependencyRequest();
152             request.setCollectRequest(collectRequest);
153             request.setFilter(DependencyFilterUtils.classpathFilter(JavaScopes.RUNTIME));
154 
155             DependencyResult dependencyResult = repositorySystem.resolveDependencies(session, request);
156             return dependencyResult.getArtifactResults().stream()
157                     .map(ArtifactResult::getArtifact)
158                     .map(RepositoryUtils::toArtifact)
159                     .collect(Collectors.toSet());
160 
161         } catch (DependencyResolutionException e) {
162             throw new MojoExecutionException(e.getMessage(), e);
163         }
164     }
165 
166     @Nonnull
167     Set<Artifact> getProviderClasspath(
168             RepositorySystemSession session,
169             List<RemoteRepository> repositories,
170             String providerArtifactId,
171             String providerVersion)
172             throws MojoExecutionException {
173         Dependency provider = toProviderDependency(providerArtifactId, providerVersion);
174 
175         org.eclipse.aether.graph.Dependency dependency =
176                 RepositoryUtils.toDependency(provider, session.getArtifactTypeRegistry());
177 
178         Set<Artifact> result = resolveDependencies(session, repositories, dependency);
179 
180         return orderProviderArtifacts(result);
181     }
182 
183     @Nonnull
184     Map<String, Artifact> getProviderClasspathAsMap(
185             RepositorySystemSession session,
186             List<RemoteRepository> repositories,
187             String providerArtifactId,
188             String providerVersion)
189             throws MojoExecutionException {
190         return artifactMapByVersionlessId(
191                 getProviderClasspath(session, repositories, providerArtifactId, providerVersion));
192     }
193 
194     // FIXME
195     // method argument should be unchanged
196     // what if providerArtifacts will be unmodifiable
197     private static Set<Artifact> orderProviderArtifacts(Set<Artifact> providerArtifacts) {
198         Set<Artifact> orderedProviderArtifacts = new LinkedHashSet<>();
199         for (String order : PROVIDER_CLASSPATH_ORDER) {
200             Iterator<Artifact> providerArtifactsIt = providerArtifacts.iterator();
201             while (providerArtifactsIt.hasNext()) {
202                 Artifact providerArtifact = providerArtifactsIt.next();
203                 if (providerArtifact.getArtifactId().equals(order)) {
204                     orderedProviderArtifacts.add(providerArtifact);
205                     providerArtifactsIt.remove();
206                 }
207             }
208         }
209         orderedProviderArtifacts.addAll(providerArtifacts);
210         return orderedProviderArtifacts;
211     }
212 
213     private static Dependency toProviderDependency(String providerArtifactId, String providerVersion) {
214         Dependency dependency = new Dependency();
215         dependency.setGroupId(PROVIDER_GROUP_ID);
216         dependency.setArtifactId(providerArtifactId);
217         dependency.setVersion(providerVersion);
218         dependency.setType("jar");
219         return dependency;
220     }
221 }