001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.internal.impl;
020
021import javax.inject.Inject;
022import javax.inject.Named;
023import javax.inject.Singleton;
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.List;
030import java.util.Map;
031import java.util.Objects;
032import java.util.concurrent.atomic.AtomicBoolean;
033import java.util.concurrent.atomic.AtomicInteger;
034import java.util.function.Consumer;
035import java.util.stream.Collectors;
036
037import org.eclipse.aether.ConfigurationProperties;
038import org.eclipse.aether.RepositorySystem;
039import org.eclipse.aether.RepositorySystemSession;
040import org.eclipse.aether.RequestTrace;
041import org.eclipse.aether.SyncContext;
042import org.eclipse.aether.artifact.Artifact;
043import org.eclipse.aether.collection.CollectRequest;
044import org.eclipse.aether.collection.CollectResult;
045import org.eclipse.aether.collection.DependencyCollectionException;
046import org.eclipse.aether.deployment.DeployRequest;
047import org.eclipse.aether.deployment.DeployResult;
048import org.eclipse.aether.deployment.DeploymentException;
049import org.eclipse.aether.graph.DependencyFilter;
050import org.eclipse.aether.graph.DependencyNode;
051import org.eclipse.aether.graph.DependencyVisitor;
052import org.eclipse.aether.impl.ArtifactDescriptorReader;
053import org.eclipse.aether.impl.ArtifactResolver;
054import org.eclipse.aether.impl.DependencyCollector;
055import org.eclipse.aether.impl.Deployer;
056import org.eclipse.aether.impl.Installer;
057import org.eclipse.aether.impl.LocalRepositoryProvider;
058import org.eclipse.aether.impl.MetadataResolver;
059import org.eclipse.aether.impl.RemoteRepositoryManager;
060import org.eclipse.aether.impl.RepositorySystemLifecycle;
061import org.eclipse.aether.impl.RepositorySystemValidator;
062import org.eclipse.aether.impl.VersionRangeResolver;
063import org.eclipse.aether.impl.VersionResolver;
064import org.eclipse.aether.installation.InstallRequest;
065import org.eclipse.aether.installation.InstallResult;
066import org.eclipse.aether.installation.InstallationException;
067import org.eclipse.aether.internal.impl.session.DefaultSessionBuilder;
068import org.eclipse.aether.repository.Authentication;
069import org.eclipse.aether.repository.LocalRepository;
070import org.eclipse.aether.repository.LocalRepositoryManager;
071import org.eclipse.aether.repository.NoLocalRepositoryManagerException;
072import org.eclipse.aether.repository.Proxy;
073import org.eclipse.aether.repository.RemoteRepository;
074import org.eclipse.aether.resolution.ArtifactDescriptorException;
075import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
076import org.eclipse.aether.resolution.ArtifactDescriptorResult;
077import org.eclipse.aether.resolution.ArtifactRequest;
078import org.eclipse.aether.resolution.ArtifactResolutionException;
079import org.eclipse.aether.resolution.ArtifactResult;
080import org.eclipse.aether.resolution.DependencyRequest;
081import org.eclipse.aether.resolution.DependencyResolutionException;
082import org.eclipse.aether.resolution.DependencyResult;
083import org.eclipse.aether.resolution.MetadataRequest;
084import org.eclipse.aether.resolution.MetadataResult;
085import org.eclipse.aether.resolution.VersionRangeRequest;
086import org.eclipse.aether.resolution.VersionRangeResolutionException;
087import org.eclipse.aether.resolution.VersionRangeResult;
088import org.eclipse.aether.resolution.VersionRequest;
089import org.eclipse.aether.resolution.VersionResolutionException;
090import org.eclipse.aether.resolution.VersionResult;
091import org.eclipse.aether.spi.artifact.decorator.ArtifactDecorator;
092import org.eclipse.aether.spi.artifact.decorator.ArtifactDecoratorFactory;
093import org.eclipse.aether.spi.synccontext.SyncContextFactory;
094import org.eclipse.aether.util.ConfigUtils;
095import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor;
096import org.eclipse.aether.util.graph.visitor.LevelOrderDependencyNodeConsumerVisitor;
097import org.eclipse.aether.util.graph.visitor.PostorderDependencyNodeConsumerVisitor;
098import org.eclipse.aether.util.graph.visitor.PreorderDependencyNodeConsumerVisitor;
099import org.eclipse.aether.util.repository.ChainedLocalRepositoryManager;
100
101import static java.util.Objects.requireNonNull;
102import static java.util.stream.Collectors.toList;
103
104/**
105 *
106 */
107@Singleton
108@Named
109public class DefaultRepositorySystem implements RepositorySystem {
110    private final AtomicBoolean shutdown;
111
112    private final AtomicInteger sessionIdCounter;
113
114    private final VersionResolver versionResolver;
115
116    private final VersionRangeResolver versionRangeResolver;
117
118    private final ArtifactResolver artifactResolver;
119
120    private final MetadataResolver metadataResolver;
121
122    private final ArtifactDescriptorReader artifactDescriptorReader;
123
124    private final DependencyCollector dependencyCollector;
125
126    private final Installer installer;
127
128    private final Deployer deployer;
129
130    private final LocalRepositoryProvider localRepositoryProvider;
131
132    private final SyncContextFactory syncContextFactory;
133
134    private final RemoteRepositoryManager remoteRepositoryManager;
135
136    private final RepositorySystemLifecycle repositorySystemLifecycle;
137
138    private final Map<String, ArtifactDecoratorFactory> artifactDecoratorFactories;
139
140    private final RepositorySystemValidator repositorySystemValidator;
141
142    @SuppressWarnings("checkstyle:parameternumber")
143    @Inject
144    public DefaultRepositorySystem(
145            VersionResolver versionResolver,
146            VersionRangeResolver versionRangeResolver,
147            ArtifactResolver artifactResolver,
148            MetadataResolver metadataResolver,
149            ArtifactDescriptorReader artifactDescriptorReader,
150            DependencyCollector dependencyCollector,
151            Installer installer,
152            Deployer deployer,
153            LocalRepositoryProvider localRepositoryProvider,
154            SyncContextFactory syncContextFactory,
155            RemoteRepositoryManager remoteRepositoryManager,
156            RepositorySystemLifecycle repositorySystemLifecycle,
157            Map<String, ArtifactDecoratorFactory> artifactDecoratorFactories,
158            RepositorySystemValidator repositorySystemValidator) {
159        this.shutdown = new AtomicBoolean(false);
160        this.sessionIdCounter = new AtomicInteger(0);
161        this.versionResolver = requireNonNull(versionResolver, "version resolver cannot be null");
162        this.versionRangeResolver = requireNonNull(versionRangeResolver, "version range resolver cannot be null");
163        this.artifactResolver = requireNonNull(artifactResolver, "artifact resolver cannot be null");
164        this.metadataResolver = requireNonNull(metadataResolver, "metadata resolver cannot be null");
165        this.artifactDescriptorReader =
166                requireNonNull(artifactDescriptorReader, "artifact descriptor reader cannot be null");
167        this.dependencyCollector = requireNonNull(dependencyCollector, "dependency collector cannot be null");
168        this.installer = requireNonNull(installer, "installer cannot be null");
169        this.deployer = requireNonNull(deployer, "deployer cannot be null");
170        this.localRepositoryProvider =
171                requireNonNull(localRepositoryProvider, "local repository provider cannot be null");
172        this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
173        this.remoteRepositoryManager =
174                requireNonNull(remoteRepositoryManager, "remote repository provider cannot be null");
175        this.repositorySystemLifecycle =
176                requireNonNull(repositorySystemLifecycle, "repository system lifecycle cannot be null");
177        this.artifactDecoratorFactories =
178                requireNonNull(artifactDecoratorFactories, "artifact decorator factories cannot be null");
179        this.repositorySystemValidator =
180                requireNonNull(repositorySystemValidator, "repository system validator cannot be null");
181    }
182
183    @Override
184    public VersionResult resolveVersion(RepositorySystemSession session, VersionRequest request)
185            throws VersionResolutionException {
186        validateSession(session);
187        requireNonNull(request, "request cannot be null");
188        repositorySystemValidator.validateVersionRequest(session, request);
189        return versionResolver.resolveVersion(session, request);
190    }
191
192    @Override
193    public VersionRangeResult resolveVersionRange(RepositorySystemSession session, VersionRangeRequest request)
194            throws VersionRangeResolutionException {
195        validateSession(session);
196        requireNonNull(request, "request cannot be null");
197        repositorySystemValidator.validateVersionRangeRequest(session, request);
198        return versionRangeResolver.resolveVersionRange(session, request);
199    }
200
201    @Override
202    public ArtifactDescriptorResult readArtifactDescriptor(
203            RepositorySystemSession session, ArtifactDescriptorRequest request) throws ArtifactDescriptorException {
204        validateSession(session);
205        requireNonNull(request, "request cannot be null");
206        repositorySystemValidator.validateArtifactDescriptorRequest(session, request);
207        ArtifactDescriptorResult descriptorResult = artifactDescriptorReader.readArtifactDescriptor(session, request);
208        for (ArtifactDecorator decorator : Utils.getArtifactDecorators(session, artifactDecoratorFactories)) {
209            descriptorResult.setArtifact(decorator.decorateArtifact(descriptorResult));
210        }
211        return descriptorResult;
212    }
213
214    @Override
215    public ArtifactResult resolveArtifact(RepositorySystemSession session, ArtifactRequest request)
216            throws ArtifactResolutionException {
217        validateSession(session);
218        requireNonNull(request, "request cannot be null");
219        repositorySystemValidator.validateArtifactRequests(session, Collections.singleton(request));
220        return artifactResolver.resolveArtifact(session, request);
221    }
222
223    @Override
224    public List<ArtifactResult> resolveArtifacts(
225            RepositorySystemSession session, Collection<? extends ArtifactRequest> requests)
226            throws ArtifactResolutionException {
227        validateSession(session);
228        requireNonNull(requests, "requests cannot be null");
229        repositorySystemValidator.validateArtifactRequests(session, requests);
230        return artifactResolver.resolveArtifacts(session, requests);
231    }
232
233    @Override
234    public List<MetadataResult> resolveMetadata(
235            RepositorySystemSession session, Collection<? extends MetadataRequest> requests) {
236        validateSession(session);
237        requireNonNull(requests, "requests cannot be null");
238        repositorySystemValidator.validateMetadataRequests(session, requests);
239        return metadataResolver.resolveMetadata(session, requests);
240    }
241
242    @Override
243    public CollectResult collectDependencies(RepositorySystemSession session, CollectRequest request)
244            throws DependencyCollectionException {
245        validateSession(session);
246        requireNonNull(request, "request cannot be null");
247        repositorySystemValidator.validateCollectRequest(session, request);
248        return dependencyCollector.collectDependencies(session, request);
249    }
250
251    @Override
252    public DependencyResult resolveDependencies(RepositorySystemSession session, DependencyRequest request)
253            throws DependencyResolutionException {
254        validateSession(session);
255        requireNonNull(request, "request cannot be null");
256        repositorySystemValidator.validateDependencyRequest(session, request);
257        RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
258
259        DependencyResult result = new DependencyResult(request);
260
261        DependencyCollectionException dce = null;
262        ArtifactResolutionException are = null;
263
264        if (request.getRoot() != null) {
265            result.setRoot(request.getRoot());
266        } else if (request.getCollectRequest() != null) {
267            CollectResult collectResult;
268            try {
269                request.getCollectRequest().setTrace(trace);
270                collectResult = dependencyCollector.collectDependencies(session, request.getCollectRequest());
271            } catch (DependencyCollectionException e) {
272                dce = e;
273                collectResult = e.getResult();
274            }
275            result.setRoot(collectResult.getRoot());
276            result.setCycles(collectResult.getCycles());
277            result.setCollectExceptions(collectResult.getExceptions());
278        } else {
279            throw new NullPointerException("dependency node and collect request cannot be null");
280        }
281
282        final List<DependencyNode> dependencyNodes =
283                doFlattenDependencyNodes(session, result.getRoot(), request.getFilter());
284
285        final List<ArtifactRequest> requests = dependencyNodes.stream()
286                .map(n -> {
287                    if (n.getDependency() != null) {
288                        ArtifactRequest artifactRequest = new ArtifactRequest(n);
289                        artifactRequest.setTrace(trace);
290                        return artifactRequest;
291                    } else {
292                        return null;
293                    }
294                })
295                .filter(Objects::nonNull)
296                .collect(Collectors.toList());
297        List<ArtifactResult> results;
298        try {
299            results = artifactResolver.resolveArtifacts(session, requests);
300        } catch (ArtifactResolutionException e) {
301            are = e;
302            results = e.getResults();
303        }
304        result.setDependencyNodeResults(dependencyNodes);
305        result.setArtifactResults(results);
306
307        updateNodesWithResolvedArtifacts(results);
308
309        if (dce != null) {
310            throw new DependencyResolutionException(result, dce);
311        } else if (are != null) {
312            throw new DependencyResolutionException(result, are);
313        }
314
315        return result;
316    }
317
318    @Override
319    public List<DependencyNode> flattenDependencyNodes(
320            RepositorySystemSession session, DependencyNode root, DependencyFilter dependencyFilter) {
321        validateSession(session);
322        requireNonNull(root, "root cannot be null");
323
324        return doFlattenDependencyNodes(session, root, dependencyFilter);
325    }
326
327    private List<DependencyNode> doFlattenDependencyNodes(
328            RepositorySystemSession session, DependencyNode root, DependencyFilter dependencyFilter) {
329        final ArrayList<DependencyNode> dependencyNodes = new ArrayList<>();
330        if (root != null) {
331            DependencyVisitor builder = getDependencyVisitor(session, dependencyNodes::add);
332            DependencyVisitor visitor =
333                    (dependencyFilter != null) ? new FilteringDependencyVisitor(builder, dependencyFilter) : builder;
334            root.accept(visitor);
335        }
336        return dependencyNodes;
337    }
338
339    private DependencyVisitor getDependencyVisitor(
340            RepositorySystemSession session, Consumer<DependencyNode> nodeConsumer) {
341        String strategy = ConfigUtils.getString(
342                session,
343                ConfigurationProperties.REPOSITORY_SYSTEM_DEPENDENCY_VISITOR_PREORDER,
344                ConfigurationProperties.REPOSITORY_SYSTEM_DEPENDENCY_VISITOR);
345        switch (strategy) {
346            case PreorderDependencyNodeConsumerVisitor.NAME:
347                return new PreorderDependencyNodeConsumerVisitor(nodeConsumer);
348            case PostorderDependencyNodeConsumerVisitor.NAME:
349                return new PostorderDependencyNodeConsumerVisitor(nodeConsumer);
350            case LevelOrderDependencyNodeConsumerVisitor.NAME:
351                return new LevelOrderDependencyNodeConsumerVisitor(nodeConsumer);
352            default:
353                throw new IllegalArgumentException("Invalid dependency visitor strategy: " + strategy);
354        }
355    }
356
357    private void updateNodesWithResolvedArtifacts(List<ArtifactResult> results) {
358        for (ArtifactResult result : results) {
359            Artifact artifact = result.getArtifact();
360            if (artifact != null) {
361                result.getRequest().getDependencyNode().setArtifact(artifact);
362            }
363        }
364    }
365
366    @Override
367    public InstallResult install(RepositorySystemSession session, InstallRequest request) throws InstallationException {
368        validateSession(session);
369        requireNonNull(request, "request cannot be null");
370        repositorySystemValidator.validateInstallRequest(session, request);
371        return installer.install(session, request);
372    }
373
374    @Override
375    public DeployResult deploy(RepositorySystemSession session, DeployRequest request) throws DeploymentException {
376        validateSession(session);
377        requireNonNull(request, "request cannot be null");
378        repositorySystemValidator.validateDeployRequest(session, request);
379        return deployer.deploy(session, request);
380    }
381
382    @Override
383    public LocalRepositoryManager newLocalRepositoryManager(
384            RepositorySystemSession session, LocalRepository localRepository) {
385        requireNonNull(session, "session cannot be null");
386        requireNonNull(localRepository, "localRepository cannot be null");
387        validateSystem();
388        repositorySystemValidator.validateLocalRepositories(session, Collections.singleton(localRepository));
389        return createLocalRepositoryManager(session, localRepository);
390    }
391
392    @Override
393    public LocalRepositoryManager newLocalRepositoryManager(
394            RepositorySystemSession session, LocalRepository... localRepositories) {
395        requireNonNull(session, "session cannot be null");
396        requireNonNull(localRepositories, "localRepositories cannot be null");
397        validateSystem();
398        repositorySystemValidator.validateLocalRepositories(session, Arrays.asList(localRepositories));
399        return createLocalRepositoryManager(session, Arrays.asList(localRepositories));
400    }
401
402    @Override
403    public LocalRepositoryManager newLocalRepositoryManager(
404            RepositorySystemSession session, List<LocalRepository> localRepositories) {
405        requireNonNull(session, "session cannot be null");
406        requireNonNull(localRepositories, "localRepositories cannot be null");
407        validateSystem();
408        repositorySystemValidator.validateLocalRepositories(session, localRepositories);
409        return createLocalRepositoryManager(session, localRepositories);
410    }
411
412    private LocalRepositoryManager createLocalRepositoryManager(
413            RepositorySystemSession session, List<LocalRepository> localRepositories) {
414        if (localRepositories.isEmpty()) {
415            throw new IllegalArgumentException("empty localRepositories");
416        } else if (localRepositories.size() == 1) {
417            return createLocalRepositoryManager(session, localRepositories.get(0));
418        } else {
419            LocalRepositoryManager head = createLocalRepositoryManager(session, localRepositories.get(0));
420            List<LocalRepositoryManager> tail = localRepositories.subList(1, localRepositories.size()).stream()
421                    .map(l -> createLocalRepositoryManager(session, l))
422                    .collect(toList());
423            return new ChainedLocalRepositoryManager(head, tail, session);
424        }
425    }
426
427    private LocalRepositoryManager createLocalRepositoryManager(
428            RepositorySystemSession session, LocalRepository localRepository) {
429        try {
430            return localRepositoryProvider.newLocalRepositoryManager(session, localRepository);
431        } catch (NoLocalRepositoryManagerException e) {
432            throw new IllegalArgumentException(e.getMessage(), e);
433        }
434    }
435
436    @Override
437    public SyncContext newSyncContext(RepositorySystemSession session, boolean shared) {
438        validateSession(session);
439        return syncContextFactory.newInstance(session, shared);
440    }
441
442    @Override
443    public List<RemoteRepository> newResolutionRepositories(
444            RepositorySystemSession session, List<RemoteRepository> repositories) {
445        validateSession(session);
446        validateRepositories(repositories);
447        repositorySystemValidator.validateRemoteRepositories(session, repositories);
448        repositories = remoteRepositoryManager.aggregateRepositories(session, new ArrayList<>(), repositories, true);
449        return repositories;
450    }
451
452    @Override
453    public RemoteRepository newDeploymentRepository(RepositorySystemSession session, RemoteRepository repository) {
454        validateSession(session);
455        requireNonNull(repository, "repository cannot be null");
456        repositorySystemValidator.validateRemoteRepositories(session, Collections.singletonList(repository));
457        RemoteRepository.Builder builder = new RemoteRepository.Builder(repository);
458        Authentication auth = session.getAuthenticationSelector().getAuthentication(repository);
459        builder.setAuthentication(auth);
460        Proxy proxy = session.getProxySelector().getProxy(repository);
461        builder.setProxy(proxy);
462        return builder.build();
463    }
464
465    @Override
466    public void addOnSystemEndedHandler(Runnable handler) {
467        validateSystem();
468        repositorySystemLifecycle.addOnSystemEndedHandler(handler);
469    }
470
471    @Override
472    public RepositorySystemSession.SessionBuilder createSessionBuilder() {
473        validateSystem();
474        return new DefaultSessionBuilder(
475                this, repositorySystemLifecycle, () -> "id-" + sessionIdCounter.incrementAndGet());
476    }
477
478    @Override
479    public void shutdown() {
480        if (shutdown.compareAndSet(false, true)) {
481            repositorySystemLifecycle.systemEnded();
482        }
483    }
484
485    private void validateSession(RepositorySystemSession session) {
486        requireNonNull(session, "repository system session cannot be null");
487        invalidSession(session.getLocalRepositoryManager(), "local repository manager");
488        invalidSession(session.getSystemProperties(), "system properties");
489        invalidSession(session.getUserProperties(), "user properties");
490        invalidSession(session.getConfigProperties(), "config properties");
491        invalidSession(session.getMirrorSelector(), "mirror selector");
492        invalidSession(session.getProxySelector(), "proxy selector");
493        invalidSession(session.getAuthenticationSelector(), "authentication selector");
494        invalidSession(session.getArtifactTypeRegistry(), "artifact type registry");
495        invalidSession(session.getData(), "data");
496        validateSystem();
497    }
498
499    private void validateSystem() {
500        if (shutdown.get()) {
501            throw new IllegalStateException("repository system is already shut down");
502        }
503    }
504
505    private void validateRepositories(List<RemoteRepository> repositories) {
506        requireNonNull(repositories, "repositories cannot be null");
507        for (RemoteRepository repository : repositories) {
508            requireNonNull(repository, "repository cannot be null");
509        }
510    }
511
512    private void invalidSession(Object obj, String name) {
513        requireNonNull(obj, "repository system session's " + name + " cannot be null");
514    }
515}