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