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.impl;
20  
21  import java.io.File;
22  import java.nio.file.Path;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.NoSuchElementException;
29  import java.util.Objects;
30  import java.util.Optional;
31  import java.util.WeakHashMap;
32  import java.util.concurrent.ConcurrentHashMap;
33  import java.util.concurrent.CopyOnWriteArrayList;
34  import java.util.function.Function;
35  import java.util.function.Supplier;
36  import java.util.stream.Collectors;
37  
38  import org.apache.maven.api.Artifact;
39  import org.apache.maven.api.ArtifactCoordinates;
40  import org.apache.maven.api.Dependency;
41  import org.apache.maven.api.DependencyCoordinates;
42  import org.apache.maven.api.DependencyScope;
43  import org.apache.maven.api.DownloadedArtifact;
44  import org.apache.maven.api.Exclusion;
45  import org.apache.maven.api.Language;
46  import org.apache.maven.api.Listener;
47  import org.apache.maven.api.LocalRepository;
48  import org.apache.maven.api.MonotonicClock;
49  import org.apache.maven.api.Node;
50  import org.apache.maven.api.Packaging;
51  import org.apache.maven.api.PathScope;
52  import org.apache.maven.api.PathType;
53  import org.apache.maven.api.ProducedArtifact;
54  import org.apache.maven.api.Project;
55  import org.apache.maven.api.ProjectScope;
56  import org.apache.maven.api.RemoteRepository;
57  import org.apache.maven.api.Service;
58  import org.apache.maven.api.Session;
59  import org.apache.maven.api.SessionData;
60  import org.apache.maven.api.Type;
61  import org.apache.maven.api.Version;
62  import org.apache.maven.api.VersionConstraint;
63  import org.apache.maven.api.VersionRange;
64  import org.apache.maven.api.WorkspaceRepository;
65  import org.apache.maven.api.annotations.Nonnull;
66  import org.apache.maven.api.annotations.Nullable;
67  import org.apache.maven.api.cache.RequestCache;
68  import org.apache.maven.api.cache.RequestCacheFactory;
69  import org.apache.maven.api.model.Repository;
70  import org.apache.maven.api.services.ArtifactCoordinatesFactory;
71  import org.apache.maven.api.services.ArtifactDeployer;
72  import org.apache.maven.api.services.ArtifactDeployerException;
73  import org.apache.maven.api.services.ArtifactFactory;
74  import org.apache.maven.api.services.ArtifactInstaller;
75  import org.apache.maven.api.services.ArtifactInstallerException;
76  import org.apache.maven.api.services.ArtifactManager;
77  import org.apache.maven.api.services.ArtifactResolver;
78  import org.apache.maven.api.services.ArtifactResolverException;
79  import org.apache.maven.api.services.DependencyCoordinatesFactory;
80  import org.apache.maven.api.services.DependencyResolver;
81  import org.apache.maven.api.services.DependencyResolverException;
82  import org.apache.maven.api.services.DependencyResolverRequest;
83  import org.apache.maven.api.services.LanguageRegistry;
84  import org.apache.maven.api.services.LocalRepositoryManager;
85  import org.apache.maven.api.services.Lookup;
86  import org.apache.maven.api.services.LookupException;
87  import org.apache.maven.api.services.PackagingRegistry;
88  import org.apache.maven.api.services.PathScopeRegistry;
89  import org.apache.maven.api.services.ProjectScopeRegistry;
90  import org.apache.maven.api.services.RepositoryFactory;
91  import org.apache.maven.api.services.Request;
92  import org.apache.maven.api.services.RequestTrace;
93  import org.apache.maven.api.services.Result;
94  import org.apache.maven.api.services.TypeRegistry;
95  import org.apache.maven.api.services.VersionParser;
96  import org.apache.maven.api.services.VersionRangeResolver;
97  import org.apache.maven.api.services.VersionResolver;
98  import org.apache.maven.api.services.VersionResolverException;
99  import org.eclipse.aether.DefaultRepositorySystemSession;
100 import org.eclipse.aether.RepositorySystem;
101 import org.eclipse.aether.RepositorySystemSession;
102 import org.eclipse.aether.artifact.ArtifactType;
103 import org.eclipse.aether.repository.ArtifactRepository;
104 import org.eclipse.aether.transfer.TransferResource;
105 
106 import static org.apache.maven.impl.Utils.map;
107 import static org.apache.maven.impl.Utils.nonNull;
108 
109 public abstract class AbstractSession implements InternalSession {
110 
111     protected final RepositorySystemSession session;
112     protected final RepositorySystem repositorySystem;
113     protected final List<RemoteRepository> repositories;
114     protected final Lookup lookup;
115     private final Map<Class<? extends Service>, Service> services = new ConcurrentHashMap<>();
116     private final List<Listener> listeners = new CopyOnWriteArrayList<>();
117     private final Map<org.eclipse.aether.graph.DependencyNode, Node> allNodes =
118             Collections.synchronizedMap(new WeakHashMap<>());
119     private final Map<Class<? extends Artifact>, Map<org.eclipse.aether.artifact.Artifact, Artifact>> allArtifacts =
120             new ConcurrentHashMap<>();
121     private final Map<org.eclipse.aether.repository.RemoteRepository, RemoteRepository> allRepositories =
122             Collections.synchronizedMap(new WeakHashMap<>());
123     private final Map<org.eclipse.aether.graph.Dependency, Dependency> allDependencies =
124             Collections.synchronizedMap(new WeakHashMap<>());
125     private volatile RequestCache requestCache;
126 
127     static {
128         TransferResource.setClock(MonotonicClock.get());
129     }
130 
131     public AbstractSession(
132             RepositorySystemSession session,
133             RepositorySystem repositorySystem,
134             List<RemoteRepository> repositories,
135             List<org.eclipse.aether.repository.RemoteRepository> resolverRepositories,
136             Lookup lookup) {
137         this.session = nonNull(session, "session");
138         this.repositorySystem = repositorySystem;
139         this.repositories = getRepositories(repositories, resolverRepositories);
140         this.lookup = lookup;
141     }
142 
143     @Override
144     public <REQ extends Request<?>, REP extends Result<REQ>> REP request(REQ req, Function<REQ, REP> supplier) {
145         return getRequestCache().request(req, supplier);
146     }
147 
148     @Override
149     public <REQ extends Request<?>, REP extends Result<REQ>> List<REP> requests(
150             List<REQ> reqs, Function<List<REQ>, List<REP>> supplier) {
151         return getRequestCache().requests(reqs, supplier);
152     }
153 
154     private RequestCache getRequestCache() {
155         RequestCache cache = requestCache;
156         if (cache == null) {
157             synchronized (this) {
158                 cache = requestCache;
159                 if (cache == null) {
160                     RequestCacheFactory factory = lookup.lookup(RequestCacheFactory.class);
161                     requestCache = factory.createCache();
162                     cache = requestCache;
163                 }
164             }
165         }
166         return cache;
167     }
168 
169     @Override
170     public RemoteRepository getRemoteRepository(org.eclipse.aether.repository.RemoteRepository repository) {
171         return allRepositories.computeIfAbsent(repository, DefaultRemoteRepository::new);
172     }
173 
174     @Override
175     public LocalRepository getLocalRepository(org.eclipse.aether.repository.LocalRepository repository) {
176         return new DefaultLocalRepository(repository);
177     }
178 
179     @Override
180     public WorkspaceRepository getWorkspaceRepository(org.eclipse.aether.repository.WorkspaceRepository repository) {
181         return new WorkspaceRepository() {
182             @Nonnull
183             @Override
184             public String getId() {
185                 return repository.getId();
186             }
187 
188             @Nonnull
189             @Override
190             public String getType() {
191                 return repository.getContentType();
192             }
193         };
194     }
195 
196     @Override
197     public org.apache.maven.api.Repository getRepository(ArtifactRepository repository) {
198         if (repository instanceof org.eclipse.aether.repository.RemoteRepository remote) {
199             return getRemoteRepository(remote);
200         } else if (repository instanceof org.eclipse.aether.repository.LocalRepository local) {
201             return getLocalRepository(local);
202         } else if (repository instanceof org.eclipse.aether.repository.WorkspaceRepository workspace) {
203             return getWorkspaceRepository(workspace);
204         } else {
205             throw new IllegalArgumentException("Unsupported repository type: " + repository.getClass());
206         }
207     }
208 
209     @Override
210     public Node getNode(org.eclipse.aether.graph.DependencyNode node) {
211         return getNode(node, false);
212     }
213 
214     @Override
215     public Node getNode(org.eclipse.aether.graph.DependencyNode node, boolean verbose) {
216         return allNodes.computeIfAbsent(node, n -> new DefaultNode(this, n, verbose));
217     }
218 
219     @Nonnull
220     @Override
221     public Artifact getArtifact(@Nonnull org.eclipse.aether.artifact.Artifact artifact) {
222         return getArtifact(Artifact.class, artifact);
223     }
224 
225     @SuppressWarnings("unchecked")
226     @Override
227     public <T extends Artifact> T getArtifact(Class<T> clazz, org.eclipse.aether.artifact.Artifact artifact) {
228         Map<org.eclipse.aether.artifact.Artifact, Artifact> map =
229                 allArtifacts.computeIfAbsent(clazz, c -> Collections.synchronizedMap(new WeakHashMap<>()));
230         if (clazz == Artifact.class) {
231             return (T) map.computeIfAbsent(artifact, a -> new DefaultArtifact(this, a));
232         } else if (clazz == DownloadedArtifact.class) {
233             if (artifact.getPath() == null) {
234                 throw new IllegalArgumentException("The given artifact is not resolved");
235             } else {
236                 return (T) map.computeIfAbsent(artifact, a -> new DefaultDownloadedArtifact(this, a));
237             }
238         } else if (clazz == ProducedArtifact.class) {
239             return (T) map.computeIfAbsent(artifact, a -> new DefaultProducedArtifact(this, a));
240         } else {
241             throw new IllegalArgumentException("Unsupported Artifact class: " + clazz);
242         }
243     }
244 
245     @Nonnull
246     @Override
247     public Dependency getDependency(@Nonnull org.eclipse.aether.graph.Dependency dependency) {
248         return allDependencies.computeIfAbsent(dependency, d -> new DefaultDependency(this, d));
249     }
250 
251     @Override
252     public List<org.eclipse.aether.repository.RemoteRepository> toRepositories(List<RemoteRepository> repositories) {
253         return repositories == null ? null : map(repositories, this::toRepository);
254     }
255 
256     @Override
257     public org.eclipse.aether.repository.RemoteRepository toRepository(RemoteRepository repository) {
258         if (repository instanceof DefaultRemoteRepository defaultRemoteRepository) {
259             return defaultRemoteRepository.getRepository();
260         } else {
261             // TODO
262             throw new UnsupportedOperationException("Not implemented yet");
263         }
264     }
265 
266     @Override
267     public org.eclipse.aether.repository.LocalRepository toRepository(LocalRepository repository) {
268         if (repository instanceof DefaultLocalRepository defaultLocalRepository) {
269             return defaultLocalRepository.getRepository();
270         } else {
271             // TODO
272             throw new UnsupportedOperationException("Not implemented yet");
273         }
274     }
275 
276     @Override
277     public List<org.eclipse.aether.graph.Dependency> toDependencies(
278             Collection<DependencyCoordinates> dependencies, boolean managed) {
279         return dependencies == null ? null : map(dependencies, d -> toDependency(d, managed));
280     }
281 
282     static List<RemoteRepository> getRepositories(
283             List<RemoteRepository> repositories,
284             List<org.eclipse.aether.repository.RemoteRepository> resolverRepositories) {
285         if (repositories != null) {
286             return repositories;
287         } else if (resolverRepositories != null) {
288             return map(resolverRepositories, DefaultRemoteRepository::new);
289         } else {
290             throw new IllegalArgumentException("no remote repositories provided");
291         }
292     }
293 
294     @Nonnull
295     @Override
296     public List<RemoteRepository> getRemoteRepositories() {
297         return Collections.unmodifiableList(repositories);
298     }
299 
300     @Nonnull
301     @Override
302     public SessionData getData() {
303         org.eclipse.aether.SessionData data = session.getData();
304         return new SessionData() {
305             @Override
306             public <T> void set(@Nonnull Key<T> key, @Nullable T value) {
307                 data.set(key, value);
308             }
309 
310             @Override
311             public <T> boolean replace(@Nonnull Key<T> key, @Nullable T oldValue, @Nullable T newValue) {
312                 return data.set(key, oldValue, newValue);
313             }
314 
315             @Nullable
316             @Override
317             @SuppressWarnings("unchecked")
318             public <T> T get(@Nonnull Key<T> key) {
319                 return (T) data.get(key);
320             }
321 
322             @Nullable
323             @Override
324             @SuppressWarnings("unchecked")
325             public <T> T computeIfAbsent(@Nonnull Key<T> key, @Nonnull Supplier<T> supplier) {
326                 return (T) data.computeIfAbsent(key, (Supplier<Object>) supplier);
327             }
328         };
329     }
330 
331     @Nonnull
332     @Override
333     public LocalRepository getLocalRepository() {
334         return new DefaultLocalRepository(session.getLocalRepository());
335     }
336 
337     @Nonnull
338     @Override
339     public Session withLocalRepository(@Nonnull LocalRepository localRepository) {
340         nonNull(localRepository, "localRepository");
341         if (session.getLocalRepository() != null
342                 && Objects.equals(session.getLocalRepository().getBasePath(), localRepository.getPath())) {
343             return this;
344         }
345         org.eclipse.aether.repository.LocalRepository repository = toRepository(localRepository);
346         org.eclipse.aether.repository.LocalRepositoryManager localRepositoryManager =
347                 repositorySystem.newLocalRepositoryManager(session, repository);
348 
349         RepositorySystemSession repoSession =
350                 new DefaultRepositorySystemSession(session).setLocalRepositoryManager(localRepositoryManager);
351         return newSession(repoSession, repositories);
352     }
353 
354     @Nonnull
355     @Override
356     public Session withRemoteRepositories(@Nonnull List<RemoteRepository> repositories) {
357         return newSession(session, repositories);
358     }
359 
360     protected abstract Session newSession(RepositorySystemSession session, List<RemoteRepository> repositories);
361 
362     @Nonnull
363     @Override
364     @SuppressWarnings("unchecked")
365     public <T extends Service> T getService(Class<T> clazz) throws NoSuchElementException {
366         T t = (T) services.computeIfAbsent(clazz, this::lookup);
367         if (t == null) {
368             throw new NoSuchElementException(clazz.getName());
369         }
370         return t;
371     }
372 
373     private Service lookup(Class<? extends Service> c) {
374         try {
375             return lookup.lookup(c);
376         } catch (LookupException e) {
377             throw new NoSuchElementException(c.getName(), e);
378         }
379     }
380 
381     @Nonnull
382     @Override
383     public RepositorySystemSession getSession() {
384         return session;
385     }
386 
387     @Nonnull
388     @Override
389     public RepositorySystem getRepositorySystem() {
390         return repositorySystem;
391     }
392 
393     @Override
394     public org.eclipse.aether.graph.Dependency toDependency(DependencyCoordinates dependency, boolean managed) {
395         org.eclipse.aether.graph.Dependency dep;
396         if (dependency instanceof AetherDependencyWrapper wrapper) {
397             dep = wrapper.dependency;
398         } else {
399             Type type = dependency.getType();
400             dep = new org.eclipse.aether.graph.Dependency(
401                     new org.eclipse.aether.artifact.DefaultArtifact(
402                             dependency.getGroupId(),
403                             dependency.getArtifactId(),
404                             dependency.getClassifier(),
405                             type.getExtension(),
406                             dependency.getVersionConstraint().toString(),
407                             Map.of("type", type.id()),
408                             (ArtifactType) null),
409                     dependency.getScope().id(),
410                     dependency.getOptional(),
411                     map(dependency.getExclusions(), this::toExclusion));
412         }
413         if (!managed && "".equals(dep.getScope())) {
414             dep = dep.setScope(DependencyScope.COMPILE.id());
415         }
416         return dep;
417     }
418 
419     private org.eclipse.aether.graph.Exclusion toExclusion(Exclusion exclusion) {
420         return new org.eclipse.aether.graph.Exclusion(exclusion.getGroupId(), exclusion.getArtifactId(), "*", "*");
421     }
422 
423     @Override
424     public List<org.eclipse.aether.artifact.Artifact> toArtifacts(Collection<? extends Artifact> artifacts) {
425         return artifacts == null ? null : map(artifacts, this::toArtifact);
426     }
427 
428     @Override
429     public org.eclipse.aether.artifact.Artifact toArtifact(Artifact artifact) {
430         Path path = getService(ArtifactManager.class).getPath(artifact).orElse(null);
431         if (artifact instanceof DefaultArtifact defaultArtifact) {
432             org.eclipse.aether.artifact.Artifact a = defaultArtifact.getArtifact();
433             if (Objects.equals(path, a.getPath())) {
434                 return a;
435             }
436         }
437         return new org.eclipse.aether.artifact.DefaultArtifact(
438                 artifact.getGroupId(),
439                 artifact.getArtifactId(),
440                 artifact.getClassifier(),
441                 artifact.getExtension(),
442                 artifact.getVersion().toString(),
443                 null,
444                 path);
445     }
446 
447     @Override
448     public org.eclipse.aether.artifact.Artifact toArtifact(ArtifactCoordinates coords) {
449         if (coords instanceof DefaultArtifactCoordinates defaultArtifactCoordinates) {
450             return defaultArtifactCoordinates.getCoordinates();
451         }
452         return new org.eclipse.aether.artifact.DefaultArtifact(
453                 coords.getGroupId(),
454                 coords.getArtifactId(),
455                 coords.getClassifier(),
456                 coords.getExtension(),
457                 coords.getVersionConstraint().toString(),
458                 null,
459                 (File) null);
460     }
461 
462     @Override
463     public void registerListener(@Nonnull Listener listener) {
464         listeners.add(nonNull(listener));
465     }
466 
467     @Override
468     public void unregisterListener(@Nonnull Listener listener) {
469         listeners.remove(nonNull(listener));
470     }
471 
472     @Nonnull
473     @Override
474     public Collection<Listener> getListeners() {
475         return Collections.unmodifiableCollection(listeners);
476     }
477 
478     //
479     // Shortcut implementations
480     //
481 
482     /**
483      * Shortcut for <code>getService(RepositoryFactory.class).createLocal(...)</code>
484      *
485      * @see RepositoryFactory#createLocal(Path)
486      */
487     @Override
488     public LocalRepository createLocalRepository(Path path) {
489         return getService(RepositoryFactory.class).createLocal(path);
490     }
491 
492     /**
493      * Shortcut for <code>getService(RepositoryFactory.class).createRemote(...)</code>
494      *
495      * @see RepositoryFactory#createRemote(String, String)
496      */
497     @Nonnull
498     @Override
499     public RemoteRepository createRemoteRepository(@Nonnull String id, @Nonnull String url) {
500         return getService(RepositoryFactory.class).createRemote(id, url);
501     }
502 
503     /**
504      * Shortcut for <code>getService(RepositoryFactory.class).createRemote(...)</code>
505      *
506      * @see RepositoryFactory#createRemote(Repository)
507      */
508     @Nonnull
509     @Override
510     public RemoteRepository createRemoteRepository(@Nonnull Repository repository) {
511         return getService(RepositoryFactory.class).createRemote(repository);
512     }
513 
514     /**
515      * Shortcut for <code>getService(ArtifactCoordinatesFactory.class).create(...)</code>
516      *
517      * @see ArtifactFactory#create(Session, String, String, String, String)
518      */
519     @Override
520     public ArtifactCoordinates createArtifactCoordinates(
521             String groupId, String artifactId, String version, String extension) {
522         return getService(ArtifactCoordinatesFactory.class).create(this, groupId, artifactId, version, extension);
523     }
524 
525     /**
526      * Shortcut for <code>getService(ArtifactCoordinatesFactory.class).create(...)</code>
527      *
528      * @see ArtifactCoordinatesFactory#create(Session, String)
529      */
530     @Override
531     public ArtifactCoordinates createArtifactCoordinates(String coordString) {
532         return getService(ArtifactCoordinatesFactory.class).create(this, coordString);
533     }
534 
535     /**
536      * Shortcut for <code>getService(ArtifactCoordinatesFactory.class).create(...)</code>
537      *
538      * @see ArtifactCoordinatesFactory#create(Session, String, String, String, String, String, String)
539      */
540     @Override
541     public ArtifactCoordinates createArtifactCoordinates(
542             String groupId, String artifactId, String version, String classifier, String extension, String type) {
543         return getService(ArtifactCoordinatesFactory.class)
544                 .create(this, groupId, artifactId, version, classifier, extension, type);
545     }
546 
547     /**
548      * Shortcut for <code>getService(ArtifactCoordinatesFactory.class).create(...)</code>
549      *
550      * @see ArtifactCoordinatesFactory#create(Session, String, String, String, String, String, String)
551      */
552     @Override
553     public ArtifactCoordinates createArtifactCoordinates(Artifact artifact) {
554         return getService(ArtifactCoordinatesFactory.class)
555                 .create(
556                         this,
557                         artifact.getGroupId(),
558                         artifact.getArtifactId(),
559                         artifact.getVersion().asString(),
560                         artifact.getClassifier(),
561                         artifact.getExtension(),
562                         null);
563     }
564 
565     /**
566      * Shortcut for <code>getService(ArtifactFactory.class).create(...)</code>
567      *
568      * @see ArtifactFactory#create(Session, String, String, String, String)
569      */
570     @Override
571     public Artifact createArtifact(String groupId, String artifactId, String version, String extension) {
572         return getService(ArtifactFactory.class).create(this, groupId, artifactId, version, extension);
573     }
574 
575     /**
576      * Shortcut for <code>getService(ArtifactFactory.class).create(...)</code>
577      *
578      * @see ArtifactFactory#create(Session, String, String, String, String, String, String)
579      */
580     @Override
581     public Artifact createArtifact(
582             String groupId, String artifactId, String version, String classifier, String extension, String type) {
583         return getService(ArtifactFactory.class)
584                 .create(this, groupId, artifactId, version, classifier, extension, type);
585     }
586 
587     /**
588      * Shortcut for <code>getService(ArtifactFactory.class).createProduced(...)</code>
589      *
590      * @see ArtifactFactory#createProduced(Session, String, String, String, String)
591      */
592     @Override
593     public ProducedArtifact createProducedArtifact(
594             String groupId, String artifactId, String version, String extension) {
595         return getService(ArtifactFactory.class).createProduced(this, groupId, artifactId, version, extension);
596     }
597 
598     /**
599      * Shortcut for <code>getService(ArtifactFactory.class).createProduced(...)</code>
600      *
601      * @see ArtifactFactory#createProduced(Session, String, String, String, String, String, String)
602      */
603     @Override
604     public ProducedArtifact createProducedArtifact(
605             String groupId, String artifactId, String version, String classifier, String extension, String type) {
606         return getService(ArtifactFactory.class)
607                 .createProduced(this, groupId, artifactId, version, classifier, extension, type);
608     }
609 
610     /**
611      * Shortcut for <code>getService(ArtifactResolver.class).resolve(...)</code>
612      *
613      * @throws ArtifactResolverException if the artifact resolution failed
614      * @see ArtifactResolver#resolve(Session, Collection)
615      */
616     @Override
617     public DownloadedArtifact resolveArtifact(ArtifactCoordinates coordinates) {
618         return getService(ArtifactResolver.class)
619                 .resolve(this, Collections.singletonList(coordinates))
620                 .getResults()
621                 .values()
622                 .iterator()
623                 .next()
624                 .getArtifact();
625     }
626 
627     /**
628      * Shortcut for <code>getService(ArtifactResolver.class).resolve(...)</code>
629      *
630      * @throws ArtifactResolverException if the artifact resolution failed
631      * @see ArtifactResolver#resolve(Session, Collection)
632      */
633     @Override
634     public DownloadedArtifact resolveArtifact(ArtifactCoordinates coordinates, List<RemoteRepository> repositories) {
635         return getService(ArtifactResolver.class)
636                 .resolve(this, Collections.singletonList(coordinates), repositories)
637                 .getResults()
638                 .values()
639                 .iterator()
640                 .next()
641                 .getArtifact();
642     }
643 
644     /**
645      * Shortcut for <code>getService(ArtifactResolver.class).resolve(...)</code>
646      *
647      * @throws ArtifactResolverException if the artifact resolution failed
648      * @see ArtifactResolver#resolve(Session, Collection)
649      */
650     @Override
651     public Collection<DownloadedArtifact> resolveArtifacts(ArtifactCoordinates... coordinates) {
652         return resolveArtifacts(Arrays.asList(coordinates));
653     }
654 
655     /**
656      * Shortcut for <code>getService(ArtifactResolver.class).resolve(...)</code>
657      *
658      * @throws ArtifactResolverException if the artifact resolution failed
659      * @see ArtifactResolver#resolve(Session, Collection)
660      */
661     @Override
662     public Collection<DownloadedArtifact> resolveArtifacts(Collection<? extends ArtifactCoordinates> coordinates) {
663         return getService(ArtifactResolver.class).resolve(this, coordinates).getArtifacts();
664     }
665 
666     /**
667      * Shortcut for <code>getService(ArtifactResolver.class).resolve(...)</code>
668      *
669      * @throws ArtifactResolverException if the artifact resolution failed
670      * @see ArtifactResolver#resolve(Session, Collection)
671      */
672     @Override
673     public Collection<DownloadedArtifact> resolveArtifacts(
674             Collection<? extends ArtifactCoordinates> coordinates, List<RemoteRepository> repositories) {
675         return getService(ArtifactResolver.class)
676                 .resolve(this, coordinates, repositories)
677                 .getArtifacts();
678     }
679 
680     /**
681      * Shortcut for <code>getService(ArtifactResolver.class).resolve(...)</code>
682      *
683      * @throws ArtifactResolverException if the artifact resolution failed
684      * @see ArtifactResolver#resolve(Session, Collection)
685      */
686     @Override
687     public DownloadedArtifact resolveArtifact(Artifact artifact) {
688         ArtifactCoordinates coordinates =
689                 getService(ArtifactCoordinatesFactory.class).create(this, artifact);
690         return resolveArtifact(coordinates);
691     }
692 
693     /**
694      * Shortcut for <code>getService(ArtifactResolver.class).resolve(...)</code>
695      *
696      * @throws ArtifactResolverException if the artifact resolution failed
697      * @see ArtifactResolver#resolve(Session, Collection)
698      */
699     @Override
700     public DownloadedArtifact resolveArtifact(Artifact artifact, List<RemoteRepository> repositories) {
701         ArtifactCoordinates coordinates =
702                 getService(ArtifactCoordinatesFactory.class).create(this, artifact);
703         return resolveArtifact(coordinates, repositories);
704     }
705 
706     @Override
707     public Collection<DownloadedArtifact> resolveArtifacts(Artifact... artifacts) {
708         ArtifactCoordinatesFactory acf = getService(ArtifactCoordinatesFactory.class);
709         List<ArtifactCoordinates> coords =
710                 Arrays.stream(artifacts).map(a -> acf.create(this, a)).collect(Collectors.toList());
711         return resolveArtifacts(coords);
712     }
713 
714     /**
715      * Shortcut for {@code getService(ArtifactInstaller.class).install(...)}
716      *
717      * @throws ArtifactInstallerException if the artifacts installation failed
718      * @see ArtifactInstaller#install(Session, Collection)
719      */
720     @Override
721     public void installArtifacts(ProducedArtifact... artifacts) {
722         installArtifacts(Arrays.asList(artifacts));
723     }
724 
725     /**
726      * Shortcut for {@code getService(ArtifactInstaller.class).install(...)}
727      *
728      * @throws ArtifactInstallerException if the artifacts installation failed
729      * @see ArtifactInstaller#install(Session, Collection)
730      */
731     @Override
732     public void installArtifacts(Collection<ProducedArtifact> artifacts) {
733         getService(ArtifactInstaller.class).install(this, artifacts);
734     }
735 
736     /**
737      * Shortcut for <code>getService(ArtifactDeployer.class).deploy(...)</code>
738      *
739      * @throws ArtifactDeployerException if the artifacts deployment failed
740      * @see ArtifactDeployer#deploy(Session, RemoteRepository, Collection)
741      */
742     @Override
743     public void deployArtifact(RemoteRepository repository, ProducedArtifact... artifacts) {
744         getService(ArtifactDeployer.class).deploy(this, repository, Arrays.asList(artifacts));
745     }
746 
747     /**
748      * Shortcut for <code>getService(ArtifactManager.class).setPath(...)</code>
749      *
750      * @see ArtifactManager#setPath(ProducedArtifact, Path)
751      */
752     @Override
753     public void setArtifactPath(@Nonnull ProducedArtifact artifact, @Nonnull Path path) {
754         getService(ArtifactManager.class).setPath(artifact, path);
755     }
756 
757     /**
758      * Shortcut for <code>getService(ArtifactManager.class).getPath(...)</code>
759      *
760      * @see ArtifactManager#getPath(Artifact)
761      */
762     @Nonnull
763     @Override
764     public Optional<Path> getArtifactPath(@Nonnull Artifact artifact) {
765         return getService(ArtifactManager.class).getPath(artifact);
766     }
767 
768     /**
769      * Shortcut for <code>getService(VersionParser.class).isSnapshot(...)</code>
770      *
771      * @see VersionParser#isSnapshot(String)
772      */
773     @Override
774     public boolean isVersionSnapshot(@Nonnull String version) {
775         return getService(VersionParser.class).isSnapshot(version);
776     }
777 
778     /**
779      * Shortcut for <code>getService(DependencyFactory.class).create(...)</code>
780      *
781      * @see DependencyCoordinatesFactory#create(Session, ArtifactCoordinates)
782      */
783     @Nonnull
784     @Override
785     public DependencyCoordinates createDependencyCoordinates(@Nonnull ArtifactCoordinates coordinates) {
786         return getService(DependencyCoordinatesFactory.class).create(this, coordinates);
787     }
788 
789     /**
790      * Shortcut for <code>getService(DependencyFactory.class).create(...)</code>
791      *
792      * @see DependencyCoordinatesFactory#create(Session, ArtifactCoordinates)
793      */
794     @Nonnull
795     @Override
796     public DependencyCoordinates createDependencyCoordinates(@Nonnull Dependency dependency) {
797         return getService(DependencyCoordinatesFactory.class).create(this, dependency);
798     }
799 
800     /**
801      * Shortcut for <code>getService(DependencyResolver.class).collect(...)</code>
802      *
803      * @throws DependencyResolverException if the dependency collection failed
804      * @see DependencyResolver#collect(Session, Artifact, PathScope)
805      */
806     @Nonnull
807     @Override
808     public Node collectDependencies(@Nonnull Artifact artifact, @Nonnull PathScope scope) {
809         return getService(DependencyResolver.class)
810                 .collect(this, artifact, scope)
811                 .getRoot();
812     }
813 
814     /**
815      * Shortcut for <code>getService(DependencyResolver.class).collect(...)</code>
816      *
817      * @throws DependencyResolverException if the dependency collection failed
818      * @see DependencyResolver#collect(Session, Project, PathScope)
819      */
820     @Nonnull
821     @Override
822     public Node collectDependencies(@Nonnull Project project, @Nonnull PathScope scope) {
823         return getService(DependencyResolver.class)
824                 .collect(this, project, scope)
825                 .getRoot();
826     }
827 
828     /**
829      * Shortcut for <code>getService(DependencyResolver.class).collect(...)</code>
830      *
831      * @throws DependencyResolverException if the dependency collection failed
832      * @see DependencyResolver#collect(Session, DependencyCoordinates, PathScope)
833      */
834     @Nonnull
835     @Override
836     public Node collectDependencies(@Nonnull DependencyCoordinates dependency, @Nonnull PathScope scope) {
837         Node root = getService(DependencyResolver.class)
838                 .collect(this, dependency, scope)
839                 .getRoot();
840         return root.getChildren().iterator().next();
841     }
842 
843     @Nonnull
844     @Override
845     public List<Node> flattenDependencies(@Nonnull Node node, @Nonnull PathScope scope) {
846         return getService(DependencyResolver.class).flatten(this, node, scope);
847     }
848 
849     @Override
850     public List<Path> resolveDependencies(DependencyCoordinates dependency) {
851         return getService(DependencyResolver.class).resolve(this, dependency).getPaths();
852     }
853 
854     @Override
855     public List<Path> resolveDependencies(List<DependencyCoordinates> dependencies) {
856         return getService(DependencyResolver.class).resolve(this, dependencies).getPaths();
857     }
858 
859     @Override
860     public List<Path> resolveDependencies(Project project, PathScope scope) {
861         return getService(DependencyResolver.class)
862                 .resolve(this, project, scope)
863                 .getPaths();
864     }
865 
866     @Override
867     public Map<PathType, List<Path>> resolveDependencies(
868             @Nonnull DependencyCoordinates dependency,
869             @Nonnull PathScope scope,
870             @Nonnull Collection<PathType> desiredTypes) {
871         return getService(DependencyResolver.class)
872                 .resolve(DependencyResolverRequest.builder()
873                         .session(this)
874                         .requestType(DependencyResolverRequest.RequestType.RESOLVE)
875                         .dependency(dependency)
876                         .pathScope(scope)
877                         .pathTypeFilter(desiredTypes)
878                         .build())
879                 .getDispatchedPaths();
880     }
881 
882     @Override
883     public Map<PathType, List<Path>> resolveDependencies(
884             @Nonnull Project project, @Nonnull PathScope scope, @Nonnull Collection<PathType> desiredTypes) {
885         return getService(DependencyResolver.class)
886                 .resolve(DependencyResolverRequest.builder()
887                         .session(this)
888                         .requestType(DependencyResolverRequest.RequestType.RESOLVE)
889                         .project(project)
890                         .pathScope(scope)
891                         .pathTypeFilter(desiredTypes)
892                         .build())
893                 .getDispatchedPaths();
894     }
895 
896     @Override
897     public Path getPathForLocalArtifact(@Nonnull Artifact artifact) {
898         return getService(LocalRepositoryManager.class).getPathForLocalArtifact(this, getLocalRepository(), artifact);
899     }
900 
901     @Override
902     public Path getPathForRemoteArtifact(RemoteRepository remote, Artifact artifact) {
903         return getService(LocalRepositoryManager.class)
904                 .getPathForRemoteArtifact(this, getLocalRepository(), remote, artifact);
905     }
906 
907     @Override
908     public Version parseVersion(String version) {
909         return getService(VersionParser.class).parseVersion(version);
910     }
911 
912     @Override
913     public VersionRange parseVersionRange(String versionRange) {
914         return getService(VersionParser.class).parseVersionRange(versionRange);
915     }
916 
917     @Override
918     public VersionConstraint parseVersionConstraint(String versionConstraint) {
919         return getService(VersionParser.class).parseVersionConstraint(versionConstraint);
920     }
921 
922     @Override
923     public Version resolveVersion(ArtifactCoordinates artifact) throws VersionResolverException {
924         return getService(VersionResolver.class).resolve(this, artifact).getVersion();
925     }
926 
927     @Override
928     public List<Version> resolveVersionRange(ArtifactCoordinates artifact) throws VersionResolverException {
929         return getService(VersionRangeResolver.class).resolve(this, artifact).getVersions();
930     }
931 
932     @Override
933     public List<Version> resolveVersionRange(ArtifactCoordinates artifact, List<RemoteRepository> repositories)
934             throws VersionResolverException {
935         return getService(VersionRangeResolver.class)
936                 .resolve(this, artifact, repositories)
937                 .getVersions();
938     }
939 
940     @Override
941     public Optional<Version> resolveHighestVersion(ArtifactCoordinates artifact, List<RemoteRepository> repositories)
942             throws VersionResolverException {
943         return getService(VersionRangeResolver.class)
944                 .resolve(this, artifact, repositories)
945                 .getHighestVersion();
946     }
947 
948     @Override
949     public Type requireType(String id) {
950         return getService(TypeRegistry.class).require(id);
951     }
952 
953     @Override
954     public Language requireLanguage(String id) {
955         return getService(LanguageRegistry.class).require(id);
956     }
957 
958     @Override
959     public Packaging requirePackaging(String id) {
960         return getService(PackagingRegistry.class).require(id);
961     }
962 
963     @Override
964     public ProjectScope requireProjectScope(String id) {
965         return getService(ProjectScopeRegistry.class).require(id);
966     }
967 
968     @Override
969     public DependencyScope requireDependencyScope(String id) {
970         return DependencyScope.forId(nonNull(id, "id"));
971     }
972 
973     @Override
974     public PathScope requirePathScope(String id) {
975         return getService(PathScopeRegistry.class).require(id);
976     }
977 
978     @Override
979     public void setCurrentTrace(RequestTrace trace) {
980         getTraceHolder().set(trace);
981     }
982 
983     @Override
984     public RequestTrace getCurrentTrace() {
985         return getTraceHolder().get();
986     }
987 
988     @SuppressWarnings("unchecked")
989     private ThreadLocal<RequestTrace> getTraceHolder() {
990         org.eclipse.aether.SessionData data = session.getData();
991         return (ThreadLocal<RequestTrace>) data.computeIfAbsent(RequestTrace.class, ThreadLocal::new);
992     }
993 }