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