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