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