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