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