1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.project;
20
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ForkJoinPool;
30 import java.util.concurrent.ForkJoinTask;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.atomic.AtomicReference;
33
34 import org.apache.maven.api.model.Dependency;
35 import org.apache.maven.api.model.Model;
36 import org.apache.maven.api.model.Parent;
37 import org.apache.maven.api.model.Repository;
38 import org.apache.maven.internal.impl.resolver.ArtifactDescriptorUtils;
39 import org.apache.maven.model.building.ArtifactModelSource;
40 import org.apache.maven.model.building.FileModelSource;
41 import org.apache.maven.model.building.ModelSource;
42 import org.apache.maven.model.resolution.InvalidRepositoryException;
43 import org.apache.maven.model.resolution.ModelResolver;
44 import org.apache.maven.model.resolution.UnresolvableModelException;
45 import org.eclipse.aether.RepositorySystem;
46 import org.eclipse.aether.RepositorySystemSession;
47 import org.eclipse.aether.RequestTrace;
48 import org.eclipse.aether.artifact.Artifact;
49 import org.eclipse.aether.artifact.DefaultArtifact;
50 import org.eclipse.aether.impl.RemoteRepositoryManager;
51 import org.eclipse.aether.repository.RemoteRepository;
52 import org.eclipse.aether.resolution.ArtifactRequest;
53 import org.eclipse.aether.resolution.ArtifactResolutionException;
54 import org.eclipse.aether.resolution.VersionRangeRequest;
55 import org.eclipse.aether.resolution.VersionRangeResolutionException;
56 import org.eclipse.aether.resolution.VersionRangeResult;
57
58
59
60
61
62
63 public class ProjectModelResolver implements ModelResolver {
64
65 private static final int MAX_CAP = 0x7fff;
66
67 private final RepositorySystemSession session;
68
69 private final RequestTrace trace;
70
71 private final String context = "project";
72
73 private List<RemoteRepository> repositories;
74
75 private List<RemoteRepository> pomRepositories;
76
77 private final List<RemoteRepository> externalRepositories;
78
79 private final RepositorySystem resolver;
80
81 private final RemoteRepositoryManager remoteRepositoryManager;
82
83 private final Set<String> repositoryIds;
84
85 private final ReactorModelPool modelPool;
86
87 private final ProjectBuildingRequest.RepositoryMerging repositoryMerging;
88
89 private final Map<String, Future<Result>> parentCache;
90
91 public ProjectModelResolver(
92 RepositorySystemSession session,
93 RequestTrace trace,
94 RepositorySystem resolver,
95 RemoteRepositoryManager remoteRepositoryManager,
96 List<RemoteRepository> repositories,
97 ProjectBuildingRequest.RepositoryMerging repositoryMerging,
98 ReactorModelPool modelPool,
99 Map<String, Object> parentCache) {
100 this.session = session;
101 this.trace = trace;
102 this.resolver = resolver;
103 this.remoteRepositoryManager = remoteRepositoryManager;
104 this.pomRepositories = new ArrayList<>();
105 this.externalRepositories = Collections.unmodifiableList(new ArrayList<>(repositories));
106 this.repositories = new ArrayList<>();
107 this.repositories.addAll(externalRepositories);
108 this.repositoryMerging = repositoryMerging;
109 this.repositoryIds = new HashSet<>();
110 this.modelPool = modelPool;
111 this.parentCache = parentCache != null ? (Map) parentCache : new ConcurrentHashMap<>();
112 }
113
114 private ProjectModelResolver(ProjectModelResolver original) {
115 this.session = original.session;
116 this.trace = original.trace;
117 this.resolver = original.resolver;
118 this.remoteRepositoryManager = original.remoteRepositoryManager;
119 this.pomRepositories = new ArrayList<>(original.pomRepositories);
120 this.externalRepositories = original.externalRepositories;
121 this.repositories = new ArrayList<>(original.repositories);
122 this.repositoryMerging = original.repositoryMerging;
123 this.repositoryIds = new HashSet<>(original.repositoryIds);
124 this.modelPool = original.modelPool;
125 this.parentCache = original.parentCache;
126 }
127
128 public void addRepository(Repository repository) throws InvalidRepositoryException {
129 addRepository(repository, false);
130 }
131
132 @Override
133 public void addRepository(final Repository repository, boolean replace) throws InvalidRepositoryException {
134 if (!repositoryIds.add(repository.getId())) {
135 if (!replace) {
136 return;
137 }
138
139
140 removeMatchingRepository(repositories, repository.getId());
141 removeMatchingRepository(pomRepositories, repository.getId());
142 }
143
144 List<RemoteRepository> newRepositories =
145 Collections.singletonList(ArtifactDescriptorUtils.toRemoteRepository(repository));
146
147 if (ProjectBuildingRequest.RepositoryMerging.REQUEST_DOMINANT.equals(repositoryMerging)) {
148 repositories = remoteRepositoryManager.aggregateRepositories(session, repositories, newRepositories, true);
149 } else {
150 pomRepositories =
151 remoteRepositoryManager.aggregateRepositories(session, pomRepositories, newRepositories, true);
152 repositories = remoteRepositoryManager.aggregateRepositories(
153 session, pomRepositories, externalRepositories, false);
154 }
155 }
156
157 private static void removeMatchingRepository(Iterable<RemoteRepository> repositories, final String id) {
158 Iterator<RemoteRepository> iterator = repositories.iterator();
159 while (iterator.hasNext()) {
160 RemoteRepository next = iterator.next();
161 if (next.getId().equals(id)) {
162 iterator.remove();
163 }
164 }
165 }
166
167 public ModelResolver newCopy() {
168 return new ProjectModelResolver(this);
169 }
170
171 public ModelSource resolveModel(String groupId, String artifactId, String version)
172 throws UnresolvableModelException {
173 Artifact pomArtifact = new DefaultArtifact(groupId, artifactId, "", "pom", version);
174
175 try {
176 ArtifactRequest request = new ArtifactRequest(pomArtifact, repositories, context);
177 request.setTrace(trace);
178 pomArtifact = resolver.resolveArtifact(session, request).getArtifact();
179 } catch (ArtifactResolutionException e) {
180 throw new UnresolvableModelException(e.getMessage(), groupId, artifactId, version, e);
181 }
182
183 return new ArtifactModelSource(pomArtifact.getFile(), groupId, artifactId, version);
184 }
185
186 record Result(ModelSource source, Parent parent, Exception e) {}
187
188 @Override
189 public ModelSource resolveModel(final Parent parent, AtomicReference<Parent> modified)
190 throws UnresolvableModelException {
191 Result result;
192 try {
193 Future<Result> future = parentCache.computeIfAbsent(parent.getId(), id -> {
194 ForkJoinPool pool = new ForkJoinPool(MAX_CAP);
195 ForkJoinTask<Result> task = new ForkJoinTask<>() {
196 Result result;
197
198 @Override
199 public Result getRawResult() {
200 return result;
201 }
202
203 @Override
204 protected void setRawResult(Result result) {
205 this.result = result;
206 }
207
208 @Override
209 protected boolean exec() {
210 try {
211 AtomicReference<Parent> modified = new AtomicReference<>();
212 ModelSource source = doResolveModel(parent, modified);
213 result = new Result(source, modified.get(), null);
214 } catch (Exception e) {
215 result = new Result(null, null, e);
216 } finally {
217 pool.shutdown();
218 }
219 return true;
220 }
221 };
222 pool.submit(task);
223 return task;
224 });
225 result = future.get();
226 } catch (Exception e) {
227 throw new UnresolvableModelException(e, parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
228 }
229 if (result.e != null) {
230 uncheckedThrow(result.e);
231 return null;
232 } else {
233 if (result.parent != null && modified != null) {
234 modified.set(result.parent);
235 }
236 return result.source;
237 }
238 }
239
240 static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
241 throw (T) t;
242 }
243
244 private ModelSource doResolveModel(Parent parent, AtomicReference<Parent> modified)
245 throws UnresolvableModelException {
246 try {
247 final Artifact artifact =
248 new DefaultArtifact(parent.getGroupId(), parent.getArtifactId(), "", "pom", parent.getVersion());
249
250 final VersionRangeRequest versionRangeRequest = new VersionRangeRequest(artifact, repositories, context);
251 versionRangeRequest.setTrace(trace);
252
253 final VersionRangeResult versionRangeResult = resolver.resolveVersionRange(session, versionRangeRequest);
254
255 if (versionRangeResult.getHighestVersion() == null) {
256 throw new UnresolvableModelException(
257 String.format(
258 "No versions matched the requested parent version range '%s'", parent.getVersion()),
259 parent.getGroupId(),
260 parent.getArtifactId(),
261 parent.getVersion());
262 }
263
264 if (versionRangeResult.getVersionConstraint() != null
265 && versionRangeResult.getVersionConstraint().getRange() != null
266 && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null) {
267
268 throw new UnresolvableModelException(
269 String.format(
270 "The requested parent version range '%s' does not specify an upper bound",
271 parent.getVersion()),
272 parent.getGroupId(),
273 parent.getArtifactId(),
274 parent.getVersion());
275 }
276
277 String newVersion = versionRangeResult.getHighestVersion().toString();
278 if (!parent.getVersion().equals(newVersion)) {
279 modified.set(parent.withVersion(newVersion));
280 }
281
282 return resolveModel(parent.getGroupId(), parent.getArtifactId(), newVersion);
283 } catch (final VersionRangeResolutionException e) {
284 throw new UnresolvableModelException(
285 e.getMessage(), parent.getGroupId(), parent.getArtifactId(), parent.getVersion(), e);
286 }
287 }
288
289 @Override
290 public ModelSource resolveModel(final Dependency dependency, AtomicReference<Dependency> modified)
291 throws UnresolvableModelException {
292 try {
293 final Artifact artifact = new DefaultArtifact(
294 dependency.getGroupId(), dependency.getArtifactId(), "", "pom", dependency.getVersion());
295
296 final VersionRangeRequest versionRangeRequest = new VersionRangeRequest(artifact, repositories, context);
297 versionRangeRequest.setTrace(trace);
298
299 final VersionRangeResult versionRangeResult = resolver.resolveVersionRange(session, versionRangeRequest);
300
301 if (versionRangeResult.getHighestVersion() == null) {
302 throw new UnresolvableModelException(
303 String.format(
304 "No versions matched the requested dependency version range '%s'",
305 dependency.getVersion()),
306 dependency.getGroupId(),
307 dependency.getArtifactId(),
308 dependency.getVersion());
309 }
310
311 if (versionRangeResult.getVersionConstraint() != null
312 && versionRangeResult.getVersionConstraint().getRange() != null
313 && versionRangeResult.getVersionConstraint().getRange().getUpperBound() == null) {
314
315 throw new UnresolvableModelException(
316 String.format(
317 "The requested dependency version range '%s' does not specify an upper bound",
318 dependency.getVersion()),
319 dependency.getGroupId(),
320 dependency.getArtifactId(),
321 dependency.getVersion());
322 }
323
324 String newVersion = versionRangeResult.getHighestVersion().toString();
325 if (!dependency.getVersion().equals(newVersion)) {
326 modified.set(dependency.withVersion(newVersion));
327 }
328
329 if (modelPool != null) {
330 Model model = modelPool.get(dependency.getGroupId(), dependency.getArtifactId(), newVersion);
331
332 if (model != null) {
333 return new FileModelSource(model.getPomFile());
334 }
335 }
336
337 return resolveModel(dependency.getGroupId(), dependency.getArtifactId(), newVersion);
338 } catch (VersionRangeResolutionException e) {
339 throw new UnresolvableModelException(
340 e.getMessage(), dependency.getGroupId(), dependency.getArtifactId(), dependency.getVersion(), e);
341 }
342 }
343
344 @Override
345 public ModelSource resolveModel(org.apache.maven.model.Parent parent) throws UnresolvableModelException {
346 AtomicReference<org.apache.maven.api.model.Parent> resolvedParent = new AtomicReference<>();
347 ModelSource result = resolveModel(parent.getDelegate(), resolvedParent);
348 if (resolvedParent.get() != null) {
349 parent.setVersion(resolvedParent.get().getVersion());
350 }
351 return result;
352 }
353
354 @Override
355 public ModelSource resolveModel(org.apache.maven.model.Dependency dependency) throws UnresolvableModelException {
356 AtomicReference<org.apache.maven.api.model.Dependency> resolvedDependency = new AtomicReference<>();
357 ModelSource result = resolveModel(dependency.getDelegate(), resolvedDependency);
358 if (resolvedDependency.get() != null) {
359 dependency.setVersion(resolvedDependency.get().getVersion());
360 }
361 return result;
362 }
363
364 @Override
365 public void addRepository(org.apache.maven.model.Repository repository) throws InvalidRepositoryException {
366 addRepository(repository.getDelegate());
367 }
368
369 @Override
370 public void addRepository(org.apache.maven.model.Repository repository, boolean replace)
371 throws InvalidRepositoryException {
372 addRepository(repository.getDelegate(), replace);
373 }
374 }