org.eclipse.aether.internal.impl.DefaultMetadataResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of maven-resolver-impl Show documentation
Show all versions of maven-resolver-impl Show documentation
An implementation of the repository system.
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.eclipse.aether.internal.impl;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import org.eclipse.aether.ConfigurationProperties;
import org.eclipse.aether.RepositoryEvent;
import org.eclipse.aether.RepositoryEvent.EventType;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.RequestTrace;
import org.eclipse.aether.SyncContext;
import org.eclipse.aether.impl.MetadataResolver;
import org.eclipse.aether.impl.OfflineController;
import org.eclipse.aether.impl.RemoteRepositoryFilterManager;
import org.eclipse.aether.impl.RemoteRepositoryManager;
import org.eclipse.aether.impl.RepositoryConnectorProvider;
import org.eclipse.aether.impl.RepositoryEventDispatcher;
import org.eclipse.aether.impl.UpdateCheck;
import org.eclipse.aether.impl.UpdateCheckManager;
import org.eclipse.aether.metadata.Metadata;
import org.eclipse.aether.repository.ArtifactRepository;
import org.eclipse.aether.repository.LocalMetadataRegistration;
import org.eclipse.aether.repository.LocalMetadataRequest;
import org.eclipse.aether.repository.LocalMetadataResult;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.LocalRepositoryManager;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.resolution.MetadataRequest;
import org.eclipse.aether.resolution.MetadataResult;
import org.eclipse.aether.spi.connector.MetadataDownload;
import org.eclipse.aether.spi.connector.RepositoryConnector;
import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
import org.eclipse.aether.spi.io.PathProcessor;
import org.eclipse.aether.spi.synccontext.SyncContextFactory;
import org.eclipse.aether.transfer.MetadataNotFoundException;
import org.eclipse.aether.transfer.MetadataTransferException;
import org.eclipse.aether.transfer.NoRepositoryConnectorException;
import org.eclipse.aether.transfer.RepositoryOfflineException;
import org.eclipse.aether.util.concurrency.ExecutorUtils;
import org.eclipse.aether.util.concurrency.RunnableErrorForwarder;
import static java.util.Objects.requireNonNull;
/**
*/
@Singleton
@Named
public class DefaultMetadataResolver implements MetadataResolver {
private static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_AETHER + "metadataResolver.";
/**
* Number of threads to use in parallel for resolving metadata.
*
* @since 0.9.0.M4
* @configurationSource {@link RepositorySystemSession#getConfigProperties()}
* @configurationType {@link java.lang.Integer}
* @configurationDefaultValue {@link #DEFAULT_THREADS}
*/
public static final String CONFIG_PROP_THREADS = CONFIG_PROPS_PREFIX + "threads";
public static final int DEFAULT_THREADS = 4;
private final RepositoryEventDispatcher repositoryEventDispatcher;
private final UpdateCheckManager updateCheckManager;
private final RepositoryConnectorProvider repositoryConnectorProvider;
private final RemoteRepositoryManager remoteRepositoryManager;
private final SyncContextFactory syncContextFactory;
private final OfflineController offlineController;
private final RemoteRepositoryFilterManager remoteRepositoryFilterManager;
private final PathProcessor pathProcessor;
@SuppressWarnings("checkstyle:parameternumber")
@Inject
public DefaultMetadataResolver(
RepositoryEventDispatcher repositoryEventDispatcher,
UpdateCheckManager updateCheckManager,
RepositoryConnectorProvider repositoryConnectorProvider,
RemoteRepositoryManager remoteRepositoryManager,
SyncContextFactory syncContextFactory,
OfflineController offlineController,
RemoteRepositoryFilterManager remoteRepositoryFilterManager,
PathProcessor pathProcessor) {
this.repositoryEventDispatcher =
requireNonNull(repositoryEventDispatcher, "repository event dispatcher cannot be null");
this.updateCheckManager = requireNonNull(updateCheckManager, "update check manager cannot be null");
this.repositoryConnectorProvider =
requireNonNull(repositoryConnectorProvider, "repository connector provider cannot be null");
this.remoteRepositoryManager =
requireNonNull(remoteRepositoryManager, "remote repository provider cannot be null");
this.syncContextFactory = requireNonNull(syncContextFactory, "sync context factory cannot be null");
this.offlineController = requireNonNull(offlineController, "offline controller cannot be null");
this.remoteRepositoryFilterManager =
requireNonNull(remoteRepositoryFilterManager, "remote repository filter manager cannot be null");
this.pathProcessor = requireNonNull(pathProcessor, "path processor cannot be null");
}
@Override
public List resolveMetadata(
RepositorySystemSession session, Collection extends MetadataRequest> requests) {
requireNonNull(session, "session cannot be null");
requireNonNull(requests, "requests cannot be null");
try (SyncContext shared = syncContextFactory.newInstance(session, true);
SyncContext exclusive = syncContextFactory.newInstance(session, false)) {
Collection metadata = new ArrayList<>(requests.size());
for (MetadataRequest request : requests) {
metadata.add(request.getMetadata());
}
return resolve(shared, exclusive, metadata, session, requests);
}
}
@SuppressWarnings("checkstyle:methodlength")
private List resolve(
SyncContext shared,
SyncContext exclusive,
Collection subjects,
RepositorySystemSession session,
Collection extends MetadataRequest> requests) {
SyncContext current = shared;
try {
while (true) {
current.acquire(null, subjects);
final List results = new ArrayList<>(requests.size());
final List tasks = new ArrayList<>(requests.size());
final Map localLastUpdates = new HashMap<>();
final RemoteRepositoryFilter remoteRepositoryFilter =
remoteRepositoryFilterManager.getRemoteRepositoryFilter(session);
for (MetadataRequest request : requests) {
RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
MetadataResult result = new MetadataResult(request);
results.add(result);
Metadata metadata = request.getMetadata();
RemoteRepository repository = request.getRepository();
if (repository == null) {
LocalRepository localRepo =
session.getLocalRepositoryManager().getRepository();
metadataResolving(session, trace, metadata, localRepo);
Path localFile = getLocalFile(session, metadata);
if (localFile != null) {
metadata = metadata.setPath(localFile);
result.setMetadata(metadata);
} else {
result.setException(new MetadataNotFoundException(metadata, localRepo));
}
metadataResolved(session, trace, metadata, localRepo, result.getException());
continue;
}
if (remoteRepositoryFilter != null) {
RemoteRepositoryFilter.Result filterResult =
remoteRepositoryFilter.acceptMetadata(repository, metadata);
if (!filterResult.isAccepted()) {
result.setException(
new MetadataNotFoundException(metadata, repository, filterResult.reasoning()));
continue;
}
}
List repositories =
getEnabledSourceRepositories(repository, metadata.getNature());
if (repositories.isEmpty()) {
continue;
}
metadataResolving(session, trace, metadata, repository);
LocalRepositoryManager lrm = session.getLocalRepositoryManager();
LocalMetadataRequest localRequest =
new LocalMetadataRequest(metadata, repository, request.getRequestContext());
LocalMetadataResult lrmResult = lrm.find(session, localRequest);
Path metadataPath = lrmResult.getPath();
try {
Utils.checkOffline(session, offlineController, repository);
} catch (RepositoryOfflineException e) {
if (metadataPath != null) {
metadata = metadata.setPath(metadataPath);
result.setMetadata(metadata);
} else {
String msg = "Cannot access " + repository.getId() + " (" + repository.getUrl()
+ ") in offline mode and the metadata " + metadata
+ " has not been downloaded from it before";
result.setException(new MetadataNotFoundException(metadata, repository, msg, e));
}
metadataResolved(session, trace, metadata, repository, result.getException());
continue;
}
Long localLastUpdate = null;
if (request.isFavorLocalRepository()) {
Path localPath = getLocalFile(session, metadata);
localLastUpdate = localLastUpdates.get(localPath);
if (localLastUpdate == null) {
localLastUpdate = localPath != null ? pathProcessor.lastModified(localPath, 0L) : 0L;
localLastUpdates.put(localPath, localLastUpdate);
}
}
List> checks = new ArrayList<>();
Exception exception = null;
for (RemoteRepository repo : repositories) {
RepositoryPolicy policy = getPolicy(session, repo, metadata.getNature());
UpdateCheck check = new UpdateCheck<>();
check.setLocalLastUpdated((localLastUpdate != null) ? localLastUpdate : 0);
check.setItem(metadata);
// use 'main' installation file for the check (-> use requested repository)
Path checkPath = session.getLocalRepositoryManager()
.getAbsolutePathForRemoteMetadata(metadata, repository, request.getRequestContext());
check.setPath(checkPath);
check.setRepository(repository);
check.setAuthoritativeRepository(repo);
check.setArtifactPolicy(policy.getArtifactUpdatePolicy());
check.setMetadataPolicy(policy.getMetadataUpdatePolicy());
if (lrmResult.isStale()) {
checks.add(check);
} else {
updateCheckManager.checkMetadata(session, check);
if (check.isRequired()) {
checks.add(check);
} else if (exception == null) {
exception = check.getException();
}
}
}
if (!checks.isEmpty()) {
RepositoryPolicy policy = getPolicy(session, repository, metadata.getNature());
// install path may be different from lookup path
Path installPath = session.getLocalRepositoryManager()
.getAbsolutePathForRemoteMetadata(
metadata, request.getRepository(), request.getRequestContext());
ResolveTask task = new ResolveTask(
session, trace, result, installPath, checks, policy.getChecksumPolicy());
tasks.add(task);
} else {
result.setException(exception);
if (metadataPath != null) {
metadata = metadata.setPath(metadataPath);
result.setMetadata(metadata);
}
metadataResolved(session, trace, metadata, repository, result.getException());
}
}
if (!tasks.isEmpty() && current == shared) {
current.close();
current = exclusive;
continue;
}
if (!tasks.isEmpty()) {
int threads = ExecutorUtils.threadCount(session, DEFAULT_THREADS, CONFIG_PROP_THREADS);
Executor executor = ExecutorUtils.executor(
Math.min(tasks.size(), threads), getClass().getSimpleName() + '-');
try {
RunnableErrorForwarder errorForwarder = new RunnableErrorForwarder();
for (ResolveTask task : tasks) {
metadataDownloading(
task.session, task.trace, task.request.getMetadata(), task.request.getRepository());
executor.execute(errorForwarder.wrap(task));
}
errorForwarder.await();
for (ResolveTask task : tasks) {
/*
* NOTE: Touch after registration with local repo to ensure concurrent resolution is not
* rejected with "already updated" via session data when actual update to local repo is
* still pending.
*/
for (UpdateCheck check : task.checks) {
updateCheckManager.touchMetadata(task.session, check.setException(task.exception));
}
metadataDownloaded(
session,
task.trace,
task.request.getMetadata(),
task.request.getRepository(),
task.metadataPath,
task.exception);
task.result.setException(task.exception);
}
} finally {
ExecutorUtils.shutdown(executor);
}
for (ResolveTask task : tasks) {
Metadata metadata = task.request.getMetadata();
// re-lookup metadata for resolve
LocalMetadataRequest localRequest = new LocalMetadataRequest(
metadata, task.request.getRepository(), task.request.getRequestContext());
Path metadataPath = session.getLocalRepositoryManager()
.find(session, localRequest)
.getPath();
if (metadataPath != null) {
metadata = metadata.setPath(metadataPath);
task.result.setMetadata(metadata);
}
if (task.result.getException() == null) {
task.result.setUpdated(true);
}
metadataResolved(
session,
task.trace,
metadata,
task.request.getRepository(),
task.result.getException());
}
}
return results;
}
} finally {
current.close();
}
}
private Path getLocalFile(RepositorySystemSession session, Metadata metadata) {
LocalRepositoryManager lrm = session.getLocalRepositoryManager();
LocalMetadataResult localResult = lrm.find(session, new LocalMetadataRequest(metadata, null, null));
return localResult.getPath();
}
private List getEnabledSourceRepositories(RemoteRepository repository, Metadata.Nature nature) {
List repositories = new ArrayList<>();
if (repository.isRepositoryManager()) {
for (RemoteRepository repo : repository.getMirroredRepositories()) {
if (isEnabled(repo, nature)) {
repositories.add(repo);
}
}
} else if (isEnabled(repository, nature)) {
repositories.add(repository);
}
return repositories;
}
private boolean isEnabled(RemoteRepository repository, Metadata.Nature nature) {
if (!Metadata.Nature.SNAPSHOT.equals(nature)
&& repository.getPolicy(false).isEnabled()) {
return true;
}
return !Metadata.Nature.RELEASE.equals(nature)
&& repository.getPolicy(true).isEnabled();
}
private RepositoryPolicy getPolicy(
RepositorySystemSession session, RemoteRepository repository, Metadata.Nature nature) {
boolean releases = !Metadata.Nature.SNAPSHOT.equals(nature);
boolean snapshots = !Metadata.Nature.RELEASE.equals(nature);
return remoteRepositoryManager.getPolicy(session, repository, releases, snapshots);
}
private void metadataResolving(
RepositorySystemSession session, RequestTrace trace, Metadata metadata, ArtifactRepository repository) {
RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVING);
event.setTrace(trace);
event.setMetadata(metadata);
event.setRepository(repository);
repositoryEventDispatcher.dispatch(event.build());
}
private void metadataResolved(
RepositorySystemSession session,
RequestTrace trace,
Metadata metadata,
ArtifactRepository repository,
Exception exception) {
RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_RESOLVED);
event.setTrace(trace);
event.setMetadata(metadata);
event.setRepository(repository);
event.setException(exception);
event.setPath(metadata.getPath());
repositoryEventDispatcher.dispatch(event.build());
}
private void metadataDownloading(
RepositorySystemSession session, RequestTrace trace, Metadata metadata, ArtifactRepository repository) {
RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADING);
event.setTrace(trace);
event.setMetadata(metadata);
event.setRepository(repository);
repositoryEventDispatcher.dispatch(event.build());
}
private void metadataDownloaded(
RepositorySystemSession session,
RequestTrace trace,
Metadata metadata,
ArtifactRepository repository,
Path path,
Exception exception) {
RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_DOWNLOADED);
event.setTrace(trace);
event.setMetadata(metadata);
event.setRepository(repository);
event.setException(exception);
event.setPath(path);
repositoryEventDispatcher.dispatch(event.build());
}
class ResolveTask implements Runnable {
final RepositorySystemSession session;
final RequestTrace trace;
final MetadataResult result;
final MetadataRequest request;
final Path metadataPath;
final String policy;
final List> checks;
volatile MetadataTransferException exception;
ResolveTask(
RepositorySystemSession session,
RequestTrace trace,
MetadataResult result,
Path metadataPath,
List> checks,
String policy) {
this.session = session;
this.trace = trace;
this.result = result;
this.request = result.getRequest();
this.metadataPath = metadataPath;
this.policy = policy;
this.checks = checks;
}
public void run() {
Metadata metadata = request.getMetadata();
RemoteRepository requestRepository = request.getRepository();
try {
List repositories = new ArrayList<>();
for (UpdateCheck check : checks) {
repositories.add(check.getAuthoritativeRepository());
}
MetadataDownload download = new MetadataDownload();
download.setMetadata(metadata);
download.setRequestContext(request.getRequestContext());
download.setPath(metadataPath);
download.setChecksumPolicy(policy);
download.setRepositories(repositories);
download.setListener(SafeTransferListener.wrap(session));
download.setTrace(trace);
try (RepositoryConnector connector =
repositoryConnectorProvider.newRepositoryConnector(session, requestRepository)) {
connector.get(null, Collections.singletonList(download));
}
exception = download.getException();
if (exception == null) {
List contexts = Collections.singletonList(request.getRequestContext());
LocalMetadataRegistration registration =
new LocalMetadataRegistration(metadata, requestRepository, contexts);
session.getLocalRepositoryManager().add(session, registration);
} else if (request.isDeleteLocalCopyIfMissing() && exception instanceof MetadataNotFoundException) {
try {
Files.deleteIfExists(download.getPath());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
} catch (NoRepositoryConnectorException e) {
exception = new MetadataTransferException(metadata, requestRepository, e);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy