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.LevelOrderDependencyNodeConsumerVisitor;
096import org.eclipse.aether.util.graph.visitor.PostorderDependencyNodeConsumerVisitor;
097import org.eclipse.aether.util.graph.visitor.PreorderDependencyNodeConsumerVisitor;
098import org.eclipse.aether.util.repository.ChainedLocalRepositoryManager;
099
100import static java.util.Objects.requireNonNull;
101import static java.util.stream.Collectors.toList;
102
103/**
104 *
105 */
106@Singleton
107@Named
108public class DefaultRepositorySystem implements RepositorySystem {
109    private final AtomicBoolean shutdown;
110
111    private final AtomicInteger sessionIdCounter;
112
113    private final VersionResolver versionResolver;
114
115    private final VersionRangeResolver versionRangeResolver;
116
117    private final ArtifactResolver artifactResolver;
118
119    private final MetadataResolver metadataResolver;
120
121    private final ArtifactDescriptorReader artifactDescriptorReader;
122
123    private final DependencyCollector dependencyCollector;
124
125    private final Installer installer;
126
127    private final Deployer deployer;
128
129    private final LocalRepositoryProvider localRepositoryProvider;
130
131    private final SyncContextFactory syncContextFactory;
132
133    private final RemoteRepositoryManager remoteRepositoryManager;
134
135    private final RepositorySystemLifecycle repositorySystemLifecycle;
136
137    private final Map<String, ArtifactDecoratorFactory> artifactDecoratorFactories;
138
139    private final RepositorySystemValidator repositorySystemValidator;
140
141    @SuppressWarnings("checkstyle:parameternumber")
142    @Inject
143    public DefaultRepositorySystem(
144            VersionResolver versionResolver,
145            VersionRangeResolver versionRangeResolver,
146            ArtifactResolver artifactResolver,
147            MetadataResolver metadataResolver,
148            ArtifactDescriptorReader artifactDescriptorReader,
149            DependencyCollector dependencyCollector,
150            Installer installer,
151            Deployer deployer,
152            LocalRepositoryProvider localRepositoryProvider,
153            SyncContextFactory syncContextFactory,
154            RemoteRepositoryManager remoteRepositoryManager,
155            RepositorySystemLifecycle repositorySystemLifecycle,
156            Map<String, ArtifactDecoratorFactory> artifactDecoratorFactories,
157            RepositorySystemValidator repositorySystemValidator) {
158        this.shutdown = new AtomicBoolean(false);
159        this.sessionIdCounter = new AtomicInteger(0);
160        this.versionResolver = requireNonNull(versionResolver, "version resolver cannot be null");
161        this.versionRangeResolver = requireNonNull(versionRangeResolver, "version range resolver cannot be null");
162        this.artifactResolver = requireNonNull(artifactResolver, "artifact resolver cannot be null");
163        this.metadataResolver = requireNonNull(metadataResolver, "metadata resolver cannot be null");
164        this.artifactDescriptorReader =
165                requireNonNull(artifactDescriptorReader, "artifact descriptor reader cannot be null");
166        this.dependencyCollector = requireNonNull(dependencyCollector, "dependency collector cannot be null");
167        this.installer = requireNonNull(installer, "installer cannot be null");
168        this.deployer = requireNonNull(deployer, "deployer cannot be null");
169        this.localRepositoryProvider =
170                requireNonNull(localRepositoryProvider, "local repository provider cannot be null");
171        this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
172        this.remoteRepositoryManager =
173                requireNonNull(remoteRepositoryManager, "remote repository provider cannot be null");
174        this.repositorySystemLifecycle =
175                requireNonNull(repositorySystemLifecycle, "repository system lifecycle cannot be null");
176        this.artifactDecoratorFactories =
177                requireNonNull(artifactDecoratorFactories, "artifact decorator factories cannot be null");
178        this.repositorySystemValidator =
179                requireNonNull(repositorySystemValidator, "repository system validator cannot be null");
180    }
181
182    @Override
183    public VersionResult resolveVersion(RepositorySystemSession session, VersionRequest request)
184            throws VersionResolutionException {
185        validateSession(session);
186        requireNonNull(request, "request cannot be null");
187        repositorySystemValidator.validateVersionRequest(session, request);
188        return versionResolver.resolveVersion(session, request);
189    }
190
191    @Override
192    public VersionRangeResult resolveVersionRange(RepositorySystemSession session, VersionRangeRequest request)
193            throws VersionRangeResolutionException {
194        validateSession(session);
195        requireNonNull(request, "request cannot be null");
196        repositorySystemValidator.validateVersionRangeRequest(session, request);
197        return versionRangeResolver.resolveVersionRange(session, request);
198    }
199
200    @Override
201    public ArtifactDescriptorResult readArtifactDescriptor(
202            RepositorySystemSession session, ArtifactDescriptorRequest request) throws ArtifactDescriptorException {
203        validateSession(session);
204        requireNonNull(request, "request cannot be null");
205        repositorySystemValidator.validateArtifactDescriptorRequest(session, request);
206        ArtifactDescriptorResult descriptorResult = artifactDescriptorReader.readArtifactDescriptor(session, request);
207        for (ArtifactDecorator decorator : Utils.getArtifactDecorators(session, artifactDecoratorFactories)) {
208            descriptorResult.setArtifact(decorator.decorateArtifact(descriptorResult));
209        }
210        return descriptorResult;
211    }
212
213    @Override
214    public ArtifactResult resolveArtifact(RepositorySystemSession session, ArtifactRequest request)
215            throws ArtifactResolutionException {
216        validateSession(session);
217        requireNonNull(request, "request cannot be null");
218        repositorySystemValidator.validateArtifactRequests(session, Collections.singleton(request));
219        return artifactResolver.resolveArtifact(session, request);
220    }
221
222    @Override
223    public List<ArtifactResult> resolveArtifacts(
224            RepositorySystemSession session, Collection<? extends ArtifactRequest> requests)
225            throws ArtifactResolutionException {
226        validateSession(session);
227        requireNonNull(requests, "requests cannot be null");
228        repositorySystemValidator.validateArtifactRequests(session, requests);
229        return artifactResolver.resolveArtifacts(session, requests);
230    }
231
232    @Override
233    public List<MetadataResult> resolveMetadata(
234            RepositorySystemSession session, Collection<? extends MetadataRequest> requests) {
235        validateSession(session);
236        requireNonNull(requests, "requests cannot be null");
237        repositorySystemValidator.validateMetadataRequests(session, requests);
238        return metadataResolver.resolveMetadata(session, requests);
239    }
240
241    @Override
242    public CollectResult collectDependencies(RepositorySystemSession session, CollectRequest request)
243            throws DependencyCollectionException {
244        validateSession(session);
245        requireNonNull(request, "request cannot be null");
246        repositorySystemValidator.validateCollectRequest(session, request);
247        return dependencyCollector.collectDependencies(session, request);
248    }
249
250    @Override
251    public DependencyResult resolveDependencies(RepositorySystemSession session, DependencyRequest request)
252            throws DependencyResolutionException {
253        validateSession(session);
254        requireNonNull(request, "request cannot be null");
255        repositorySystemValidator.validateDependencyRequest(session, request);
256        RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
257
258        DependencyResult result = new DependencyResult(request);
259
260        DependencyCollectionException dce = null;
261        ArtifactResolutionException are = null;
262
263        if (request.getRoot() != null) {
264            result.setRoot(request.getRoot());
265        } else if (request.getCollectRequest() != null) {
266            CollectResult collectResult;
267            try {
268                request.getCollectRequest().setTrace(trace);
269                collectResult = dependencyCollector.collectDependencies(session, request.getCollectRequest());
270            } catch (DependencyCollectionException e) {
271                dce = e;
272                collectResult = e.getResult();
273            }
274            result.setRoot(collectResult.getRoot());
275            result.setCycles(collectResult.getCycles());
276            result.setCollectExceptions(collectResult.getExceptions());
277        } else {
278            throw new NullPointerException("dependency node and collect request cannot be null");
279        }
280
281        final List<DependencyNode> dependencyNodes =
282                doFlattenDependencyNodes(session, result.getRoot(), request.getFilter());
283
284        final List<ArtifactRequest> requests = dependencyNodes.stream()
285                .map(n -> {
286                    if (n.getDependency() != null) {
287                        ArtifactRequest artifactRequest = new ArtifactRequest(n);
288                        artifactRequest.setTrace(trace);
289                        return artifactRequest;
290                    } else {
291                        return null;
292                    }
293                })
294                .filter(Objects::nonNull)
295                .collect(Collectors.toList());
296        List<ArtifactResult> results;
297        try {
298            results = artifactResolver.resolveArtifacts(session, requests);
299        } catch (ArtifactResolutionException e) {
300            are = e;
301            results = e.getResults();
302        }
303        result.setDependencyNodeResults(dependencyNodes);
304        result.setArtifactResults(results);
305
306        updateNodesWithResolvedArtifacts(results);
307
308        if (dce != null) {
309            throw new DependencyResolutionException(result, dce);
310        } else if (are != null) {
311            throw new DependencyResolutionException(result, are);
312        }
313
314        return result;
315    }
316
317    @Override
318    public List<DependencyNode> flattenDependencyNodes(
319            RepositorySystemSession session, DependencyNode root, DependencyFilter dependencyFilter) {
320        validateSession(session);
321        requireNonNull(root, "root cannot be null");
322
323        return doFlattenDependencyNodes(session, root, dependencyFilter);
324    }
325
326    private List<DependencyNode> doFlattenDependencyNodes(
327            RepositorySystemSession session, DependencyNode root, DependencyFilter dependencyFilter) {
328        final ArrayList<DependencyNode> dependencyNodes = new ArrayList<>();
329        if (root != null) {
330            root.accept(getDependencyVisitor(session, dependencyNodes::add, dependencyFilter));
331        }
332        return dependencyNodes;
333    }
334
335    private DependencyVisitor getDependencyVisitor(
336            RepositorySystemSession session, Consumer<DependencyNode> nodeConsumer, DependencyFilter dependencyFilter) {
337        String strategy = ConfigUtils.getString(
338                session,
339                ConfigurationProperties.DEFAULT_REPOSITORY_SYSTEM_DEPENDENCY_VISITOR,
340                ConfigurationProperties.REPOSITORY_SYSTEM_DEPENDENCY_VISITOR);
341        switch (strategy) {
342            case PreorderDependencyNodeConsumerVisitor.NAME:
343                return new PreorderDependencyNodeConsumerVisitor(nodeConsumer, dependencyFilter);
344            case PostorderDependencyNodeConsumerVisitor.NAME:
345                return new PostorderDependencyNodeConsumerVisitor(nodeConsumer, dependencyFilter);
346            case LevelOrderDependencyNodeConsumerVisitor.NAME:
347                return new LevelOrderDependencyNodeConsumerVisitor(nodeConsumer, dependencyFilter);
348            default:
349                throw new IllegalArgumentException("Invalid dependency visitor strategy: " + strategy);
350        }
351    }
352
353    private void updateNodesWithResolvedArtifacts(List<ArtifactResult> results) {
354        for (ArtifactResult result : results) {
355            Artifact artifact = result.getArtifact();
356            if (artifact != null) {
357                result.getRequest().getDependencyNode().setArtifact(artifact);
358            }
359        }
360    }
361
362    @Override
363    public InstallResult install(RepositorySystemSession session, InstallRequest request) throws InstallationException {
364        validateSession(session);
365        requireNonNull(request, "request cannot be null");
366        repositorySystemValidator.validateInstallRequest(session, request);
367        return installer.install(session, request);
368    }
369
370    @Override
371    public DeployResult deploy(RepositorySystemSession session, DeployRequest request) throws DeploymentException {
372        validateSession(session);
373        requireNonNull(request, "request cannot be null");
374        repositorySystemValidator.validateDeployRequest(session, request);
375        return deployer.deploy(session, request);
376    }
377
378    @Override
379    public LocalRepositoryManager newLocalRepositoryManager(
380            RepositorySystemSession session, LocalRepository localRepository) {
381        requireNonNull(session, "session cannot be null");
382        requireNonNull(localRepository, "localRepository cannot be null");
383        validateSystem();
384        repositorySystemValidator.validateLocalRepositories(session, Collections.singleton(localRepository));
385        return createLocalRepositoryManager(session, localRepository);
386    }
387
388    @Override
389    public LocalRepositoryManager newLocalRepositoryManager(
390            RepositorySystemSession session, LocalRepository... localRepositories) {
391        requireNonNull(session, "session cannot be null");
392        requireNonNull(localRepositories, "localRepositories cannot be null");
393        validateSystem();
394        repositorySystemValidator.validateLocalRepositories(session, Arrays.asList(localRepositories));
395        return createLocalRepositoryManager(session, Arrays.asList(localRepositories));
396    }
397
398    @Override
399    public LocalRepositoryManager newLocalRepositoryManager(
400            RepositorySystemSession session, List<LocalRepository> localRepositories) {
401        requireNonNull(session, "session cannot be null");
402        requireNonNull(localRepositories, "localRepositories cannot be null");
403        validateSystem();
404        repositorySystemValidator.validateLocalRepositories(session, localRepositories);
405        return createLocalRepositoryManager(session, localRepositories);
406    }
407
408    private LocalRepositoryManager createLocalRepositoryManager(
409            RepositorySystemSession session, List<LocalRepository> localRepositories) {
410        if (localRepositories.isEmpty()) {
411            throw new IllegalArgumentException("empty localRepositories");
412        } else if (localRepositories.size() == 1) {
413            return createLocalRepositoryManager(session, localRepositories.get(0));
414        } else {
415            LocalRepositoryManager head = createLocalRepositoryManager(session, localRepositories.get(0));
416            List<LocalRepositoryManager> tail = localRepositories.subList(1, localRepositories.size()).stream()
417                    .map(l -> createLocalRepositoryManager(session, l))
418                    .collect(toList());
419            return new ChainedLocalRepositoryManager(head, tail, session);
420        }
421    }
422
423    private LocalRepositoryManager createLocalRepositoryManager(
424            RepositorySystemSession session, LocalRepository localRepository) {
425        try {
426            return localRepositoryProvider.newLocalRepositoryManager(session, localRepository);
427        } catch (NoLocalRepositoryManagerException e) {
428            throw new IllegalArgumentException(e.getMessage(), e);
429        }
430    }
431
432    @Override
433    public SyncContext newSyncContext(RepositorySystemSession session, boolean shared) {
434        validateSession(session);
435        return syncContextFactory.newInstance(session, shared);
436    }
437
438    @Override
439    public List<RemoteRepository> newResolutionRepositories(
440            RepositorySystemSession session, List<RemoteRepository> repositories) {
441        validateSession(session);
442        validateRepositories(repositories);
443        repositorySystemValidator.validateRemoteRepositories(session, repositories);
444        repositories = remoteRepositoryManager.aggregateRepositories(session, new ArrayList<>(), repositories, true);
445        return repositories;
446    }
447
448    @Override
449    public RemoteRepository newDeploymentRepository(RepositorySystemSession session, RemoteRepository repository) {
450        validateSession(session);
451        requireNonNull(repository, "repository cannot be null");
452        repositorySystemValidator.validateRemoteRepositories(session, Collections.singletonList(repository));
453        RemoteRepository.Builder builder = new RemoteRepository.Builder(repository);
454        Authentication auth = session.getAuthenticationSelector().getAuthentication(repository);
455        builder.setAuthentication(auth);
456        Proxy proxy = session.getProxySelector().getProxy(repository);
457        builder.setProxy(proxy);
458        return builder.build();
459    }
460
461    @Override
462    public void addOnSystemEndedHandler(Runnable handler) {
463        validateSystem();
464        repositorySystemLifecycle.addOnSystemEndedHandler(handler);
465    }
466
467    @Override
468    public RepositorySystemSession.SessionBuilder createSessionBuilder() {
469        validateSystem();
470        return new DefaultSessionBuilder(
471                this, repositorySystemLifecycle, () -> "id-" + sessionIdCounter.incrementAndGet());
472    }
473
474    @Override
475    public void shutdown() {
476        if (shutdown.compareAndSet(false, true)) {
477            repositorySystemLifecycle.systemEnded();
478        }
479    }
480
481    private void validateSession(RepositorySystemSession session) {
482        requireNonNull(session, "repository system session cannot be null");
483        invalidSession(session.getLocalRepositoryManager(), "local repository manager");
484        invalidSession(session.getSystemProperties(), "system properties");
485        invalidSession(session.getUserProperties(), "user properties");
486        invalidSession(session.getConfigProperties(), "config properties");
487        invalidSession(session.getMirrorSelector(), "mirror selector");
488        invalidSession(session.getProxySelector(), "proxy selector");
489        invalidSession(session.getAuthenticationSelector(), "authentication selector");
490        invalidSession(session.getArtifactTypeRegistry(), "artifact type registry");
491        invalidSession(session.getData(), "data");
492        validateSystem();
493    }
494
495    private void validateSystem() {
496        if (shutdown.get()) {
497            throw new IllegalStateException("repository system is already shut down");
498        }
499    }
500
501    private void validateRepositories(List<RemoteRepository> repositories) {
502        requireNonNull(repositories, "repositories cannot be null");
503        for (RemoteRepository repository : repositories) {
504            requireNonNull(repository, "repository cannot be null");
505        }
506    }
507
508    private void invalidSession(Object obj, String name) {
509        requireNonNull(obj, "repository system session's " + name + " cannot be null");
510    }
511}