blob: c8ca6bd374d61f424e723f7acc510484536efc99 [file] [log] [blame]
/*
* 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.apache.maven.repository.internal;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.repository.metadata.Versioning;
import org.apache.maven.artifact.repository.metadata.io.MetadataStaxReader;
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.RepositoryEventDispatcher;
import org.eclipse.aether.impl.VersionRangeResolver;
import org.eclipse.aether.metadata.DefaultMetadata;
import org.eclipse.aether.metadata.Metadata;
import org.eclipse.aether.repository.ArtifactRepository;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.resolution.MetadataRequest;
import org.eclipse.aether.resolution.MetadataResult;
import org.eclipse.aether.resolution.VersionRangeRequest;
import org.eclipse.aether.resolution.VersionRangeResolutionException;
import org.eclipse.aether.resolution.VersionRangeResult;
import org.eclipse.aether.spi.synccontext.SyncContextFactory;
import org.eclipse.aether.version.InvalidVersionSpecificationException;
import org.eclipse.aether.version.Version;
import org.eclipse.aether.version.VersionConstraint;
import org.eclipse.aether.version.VersionRange;
import org.eclipse.aether.version.VersionScheme;
/**
*/
@Named
@Singleton
public class DefaultVersionRangeResolver implements VersionRangeResolver {
private static final String MAVEN_METADATA_XML = "maven-metadata.xml";
private final MetadataResolver metadataResolver;
private final SyncContextFactory syncContextFactory;
private final RepositoryEventDispatcher repositoryEventDispatcher;
private final VersionScheme versionScheme;
@Inject
public DefaultVersionRangeResolver(
MetadataResolver metadataResolver,
SyncContextFactory syncContextFactory,
RepositoryEventDispatcher repositoryEventDispatcher,
VersionScheme versionScheme) {
this.metadataResolver = Objects.requireNonNull(metadataResolver, "metadataResolver cannot be null");
this.syncContextFactory = Objects.requireNonNull(syncContextFactory, "syncContextFactory cannot be null");
this.repositoryEventDispatcher =
Objects.requireNonNull(repositoryEventDispatcher, "repositoryEventDispatcher cannot be null");
this.versionScheme = Objects.requireNonNull(versionScheme, "versionScheme cannot be null");
}
@Override
public VersionRangeResult resolveVersionRange(RepositorySystemSession session, VersionRangeRequest request)
throws VersionRangeResolutionException {
VersionRangeResult result = new VersionRangeResult(request);
VersionConstraint versionConstraint;
try {
versionConstraint =
versionScheme.parseVersionConstraint(request.getArtifact().getVersion());
} catch (InvalidVersionSpecificationException e) {
result.addException(e);
throw new VersionRangeResolutionException(result);
}
result.setVersionConstraint(versionConstraint);
if (versionConstraint.getRange() == null) {
result.addVersion(versionConstraint.getVersion());
} else {
VersionRange.Bound lowerBound = versionConstraint.getRange().getLowerBound();
if (lowerBound != null
&& lowerBound.equals(versionConstraint.getRange().getUpperBound())) {
result.addVersion(lowerBound.getVersion());
} else {
Map<String, ArtifactRepository> versionIndex = getVersions(session, result, request);
List<Version> versions = new ArrayList<>();
for (Map.Entry<String, ArtifactRepository> v : versionIndex.entrySet()) {
try {
Version ver = versionScheme.parseVersion(v.getKey());
if (versionConstraint.containsVersion(ver)) {
versions.add(ver);
result.setRepository(ver, v.getValue());
}
} catch (InvalidVersionSpecificationException e) {
result.addException(e);
}
}
Collections.sort(versions);
result.setVersions(versions);
}
}
return result;
}
private Map<String, ArtifactRepository> getVersions(
RepositorySystemSession session, VersionRangeResult result, VersionRangeRequest request) {
RequestTrace trace = RequestTrace.newChild(request.getTrace(), request);
Map<String, ArtifactRepository> versionIndex = new HashMap<>();
Metadata metadata = new DefaultMetadata(
request.getArtifact().getGroupId(),
request.getArtifact().getArtifactId(),
MAVEN_METADATA_XML,
Metadata.Nature.RELEASE_OR_SNAPSHOT);
List<MetadataRequest> metadataRequests =
new ArrayList<>(request.getRepositories().size());
metadataRequests.add(new MetadataRequest(metadata, null, request.getRequestContext()));
for (RemoteRepository repository : request.getRepositories()) {
MetadataRequest metadataRequest = new MetadataRequest(metadata, repository, request.getRequestContext());
metadataRequest.setDeleteLocalCopyIfMissing(true);
metadataRequest.setTrace(trace);
metadataRequests.add(metadataRequest);
}
List<MetadataResult> metadataResults = metadataResolver.resolveMetadata(session, metadataRequests);
WorkspaceReader workspace = session.getWorkspaceReader();
if (workspace != null) {
List<String> versions = workspace.findVersions(request.getArtifact());
for (String version : versions) {
versionIndex.put(version, workspace.getRepository());
}
}
for (MetadataResult metadataResult : metadataResults) {
result.addException(metadataResult.getException());
ArtifactRepository repository = metadataResult.getRequest().getRepository();
if (repository == null) {
repository = session.getLocalRepository();
}
Versioning versioning = readVersions(session, trace, metadataResult.getMetadata(), repository, result);
versioning = filterVersionsByRepositoryType(
versioning, metadataResult.getRequest().getRepository());
for (String version : versioning.getVersions()) {
if (!versionIndex.containsKey(version)) {
versionIndex.put(version, repository);
}
}
}
return versionIndex;
}
private Versioning readVersions(
RepositorySystemSession session,
RequestTrace trace,
Metadata metadata,
ArtifactRepository repository,
VersionRangeResult result) {
Versioning versioning = null;
try {
if (metadata != null) {
try (SyncContext syncContext = syncContextFactory.newInstance(session, true)) {
syncContext.acquire(null, Collections.singleton(metadata));
if (metadata.getFile() != null && metadata.getFile().exists()) {
try (InputStream in =
Files.newInputStream(metadata.getFile().toPath())) {
versioning = new Versioning(
new MetadataStaxReader().read(in, false).getVersioning());
}
}
}
}
} catch (Exception e) {
invalidMetadata(session, trace, metadata, repository, e);
result.addException(e);
}
return (versioning != null) ? versioning : new Versioning();
}
private Versioning filterVersionsByRepositoryType(Versioning versioning, RemoteRepository remoteRepository) {
if (remoteRepository == null) {
return versioning;
}
Versioning filteredVersions = versioning.clone();
for (String version : versioning.getVersions()) {
if (!remoteRepository.getPolicy(ArtifactUtils.isSnapshot(version)).isEnabled()) {
filteredVersions.removeVersion(version);
}
}
return filteredVersions;
}
private void invalidMetadata(
RepositorySystemSession session,
RequestTrace trace,
Metadata metadata,
ArtifactRepository repository,
Exception exception) {
RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INVALID);
event.setTrace(trace);
event.setMetadata(metadata);
event.setException(exception);
event.setRepository(repository);
repositoryEventDispatcher.dispatch(event.build());
}
}