Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MNG-7960] Artifact collection filtering #1353

Merged
merged 11 commits into from
Jan 11, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.apache.maven.RepositoryUtils;
Expand Down Expand Up @@ -59,13 +60,17 @@
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession.SessionBuilder;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.VersionFilter;
import org.eclipse.aether.repository.AuthenticationContext;
import org.eclipse.aether.repository.AuthenticationSelector;
import org.eclipse.aether.repository.ProxySelector;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.resolution.ResolutionErrorPolicy;
import org.eclipse.aether.util.graph.version.*;
import org.eclipse.aether.util.listener.ChainedRepositoryListener;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.util.repository.ChainedLocalRepositoryManager;
Expand All @@ -74,6 +79,10 @@
import org.eclipse.aether.util.repository.DefaultProxySelector;
import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
import org.eclipse.aether.util.repository.SimpleResolutionErrorPolicy;
import org.eclipse.aether.version.InvalidVersionSpecificationException;
import org.eclipse.aether.version.Version;
import org.eclipse.aether.version.VersionRange;
import org.eclipse.aether.version.VersionScheme;
import org.eclipse.sisu.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -83,6 +92,25 @@
*/
@Named
public class DefaultRepositorySystemSessionFactory {
/**
* User property for version filters expression, a comma separated list of filters to apply. By default no version
* filter is applied (like in Maven 3).
* <p>
* Supported filters:
* <ul>
* <li>"h" or "h(num)" - highest version or top list of highest ones filter</li>
* <li>"l" or "l(num)" - lowest version or bottom list of lowest ones filter</li>
* <li>"s" - contextual snapshot filter</li>
* <li>"e(G:A:V)" - predicate filter (leaves out G:A:V from range, if hit, V can be range)</li>
* </ul>
* Example filter expression: {@code "h(5),s,e(org.foo:bar:1)} will cause: ranges are filtered for "top 5" (instead
* full range), snapshots are banned if root project is not a snapshot, and if range for {@code org.foo:bar} is
* being processed, version 1 is omitted.
*
* @since 4.0.0
*/
private static final String MAVEN_VERSION_FILTERS = "maven.versionFilters";

/**
* User property for chained LRM: list of "tail" local repository paths (separated by comma), to be used with
* {@link ChainedLocalRepositoryManager}.
Expand Down Expand Up @@ -148,6 +176,8 @@ public class DefaultRepositorySystemSessionFactory {

private final TypeRegistry typeRegistry;

private final VersionScheme versionScheme;

@SuppressWarnings("checkstyle:ParameterNumber")
@Inject
public DefaultRepositorySystemSessionFactory(
Expand All @@ -157,14 +187,16 @@ public DefaultRepositorySystemSessionFactory(
SettingsDecrypter settingsDecrypter,
EventSpyDispatcher eventSpyDispatcher,
RuntimeInformation runtimeInformation,
TypeRegistry typeRegistry) {
TypeRegistry typeRegistry,
VersionScheme versionScheme) {
this.artifactHandlerManager = artifactHandlerManager;
this.repoSystem = repoSystem;
this.workspaceRepository = workspaceRepository;
this.settingsDecrypter = settingsDecrypter;
this.eventSpyDispatcher = eventSpyDispatcher;
this.runtimeInformation = runtimeInformation;
this.typeRegistry = typeRegistry;
this.versionScheme = versionScheme;
}

@Deprecated
Expand Down Expand Up @@ -208,6 +240,11 @@ public SessionBuilder newRepositorySessionBuilder(MavenExecutionRequest request)
session.setArtifactDescriptorPolicy(new SimpleArtifactDescriptorPolicy(
request.isIgnoreMissingArtifactDescriptor(), request.isIgnoreInvalidArtifactDescriptor()));

VersionFilter versionFilter = buildVersionFilter(configProps);
if (versionFilter != null) {
session.setVersionFilter(versionFilter);
}

session.setArtifactTypeRegistry(RepositoryUtils.newArtifactTypeRegistry(artifactHandlerManager));

session.setWorkspaceReader(
Expand Down Expand Up @@ -407,6 +444,73 @@ public SessionBuilder newRepositorySessionBuilder(MavenExecutionRequest request)
return session;
}

private VersionFilter buildVersionFilter(Map<Object, Object> configProps) {
ArrayList<VersionFilter> filters = new ArrayList<>();
String filterExpression = (String) configProps.get(MAVEN_VERSION_FILTERS);
if (filterExpression != null) {
List<String> expressions = Arrays.stream(filterExpression.split(","))
.filter(s -> s != null && !s.trim().isEmpty())
.collect(Collectors.toList());
for (String expression : expressions) {
if ("h".equals(expression)) {
filters.add(new HighestVersionFilter());
} else if (expression.startsWith("h(") && expression.endsWith(")")) {
int num = Integer.parseInt(expression.substring(2, expression.length() - 1));
filters.add(new HighestVersionFilter(num));
} else if ("l".equals(expression)) {
filters.add(new LowestVersionFilter());
} else if (expression.startsWith("l(") && expression.endsWith(")")) {
int num = Integer.parseInt(expression.substring(2, expression.length() - 1));
filters.add(new LowestVersionFilter(num));
} else if ("s".equals(expression)) {
filters.add(new ContextualSnapshotVersionFilter());
} else if (expression.startsWith("e(") && expression.endsWith(")")) {
Artifact artifact = new DefaultArtifact(expression.substring(2, expression.length() - 1));
VersionRange versionRange =
artifact.getVersion().contains(",") ? parseVersionRange(artifact.getVersion()) : null;
Predicate<Artifact> predicate = a -> {
if (artifact.getGroupId().equals(a.getGroupId())
&& artifact.getArtifactId().equals(a.getArtifactId())) {
if (versionRange != null) {
Version v = parseVersion(a.getVersion());
return !versionRange.containsVersion(v);
} else {
return !artifact.getVersion().equals(a.getVersion());
}
}
return true;
};
filters.add(new PredicateVersionFilter(predicate));
} else {
throw new IllegalArgumentException("Unsupported filter expression: " + expression);
}
}
}
if (filters.isEmpty()) {
return null;
} else if (filters.size() == 1) {
return filters.get(0);
} else {
return ChainedVersionFilter.newInstance(filters);
}
}

private Version parseVersion(String spec) {
try {
return versionScheme.parseVersion(spec);
} catch (InvalidVersionSpecificationException e) {
throw new RuntimeException(e);
}
}

private VersionRange parseVersionRange(String spec) {
try {
return versionScheme.parseVersionRange(spec);
} catch (InvalidVersionSpecificationException e) {
throw new RuntimeException(e);
}
}

private Map<?, ?> getPropertiesFromRequestedProfiles(MavenExecutionRequest request) {
HashSet<String> activeProfileId =
new HashSet<>(request.getProfileActivation().getRequiredActiveProfileIds());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.eclipse.aether.ConfigurationProperties;
import org.eclipse.aether.repository.RepositoryPolicy;
import org.eclipse.aether.version.VersionScheme;
import org.junit.jupiter.api.Test;

import static org.codehaus.plexus.testing.PlexusExtension.getBasedir;
Expand Down Expand Up @@ -76,6 +77,9 @@ public class DefaultRepositorySystemSessionFactoryTest {
@Inject
protected DefaultTypeRegistry defaultTypeRegistry;

@Inject
protected VersionScheme versionScheme;

@Test
void isNoSnapshotUpdatesTest() throws InvalidRepositoryException {
DefaultRepositorySystemSessionFactory systemSessionFactory = new DefaultRepositorySystemSessionFactory(
Expand All @@ -85,7 +89,8 @@ void isNoSnapshotUpdatesTest() throws InvalidRepositoryException {
settingsDecrypter,
eventSpyDispatcher,
information,
defaultTypeRegistry);
defaultTypeRegistry,
versionScheme);

MavenExecutionRequest request = new DefaultMavenExecutionRequest();
request.setLocalRepository(getLocalRepository());
Expand All @@ -108,7 +113,8 @@ void isSnapshotUpdatesTest() throws InvalidRepositoryException {
settingsDecrypter,
eventSpyDispatcher,
information,
defaultTypeRegistry);
defaultTypeRegistry,
versionScheme);

MavenExecutionRequest request = new DefaultMavenExecutionRequest();
request.setLocalRepository(getLocalRepository());
Expand Down Expand Up @@ -143,7 +149,8 @@ void wagonProviderConfigurationTest() throws InvalidRepositoryException {
settingsDecrypter,
eventSpyDispatcher,
information,
defaultTypeRegistry);
defaultTypeRegistry,
versionScheme);

PlexusConfiguration plexusConfiguration = (PlexusConfiguration) systemSessionFactory
.newRepositorySession(request)
Expand Down Expand Up @@ -186,7 +193,8 @@ void httpConfigurationWithHttpHeadersTest() throws InvalidRepositoryException {
settingsDecrypter,
eventSpyDispatcher,
information,
defaultTypeRegistry);
defaultTypeRegistry,
versionScheme);

Map<String, String> headers = (Map<String, String>) systemSessionFactory
.newRepositorySession(request)
Expand Down Expand Up @@ -223,7 +231,8 @@ void connectTimeoutConfigurationTest() throws InvalidRepositoryException {
settingsDecrypter,
eventSpyDispatcher,
information,
defaultTypeRegistry);
defaultTypeRegistry,
versionScheme);

int connectionTimeout = (Integer) systemSessionFactory
.newRepositorySession(request)
Expand Down Expand Up @@ -264,7 +273,8 @@ void connectionTimeoutFromHttpConfigurationTest() throws InvalidRepositoryExcept
settingsDecrypter,
eventSpyDispatcher,
information,
defaultTypeRegistry);
defaultTypeRegistry,
versionScheme);

int connectionTimeout = (Integer) systemSessionFactory
.newRepositorySession(request)
Expand Down Expand Up @@ -299,7 +309,8 @@ void requestTimeoutConfigurationTest() throws InvalidRepositoryException {
settingsDecrypter,
eventSpyDispatcher,
information,
defaultTypeRegistry);
defaultTypeRegistry,
versionScheme);

int requestTimeout = (Integer) systemSessionFactory
.newRepositorySession(request)
Expand Down Expand Up @@ -340,7 +351,8 @@ void readTimeoutFromHttpConfigurationTest() throws InvalidRepositoryException {
settingsDecrypter,
eventSpyDispatcher,
information,
defaultTypeRegistry);
defaultTypeRegistry,
versionScheme);

int requestTimeout = (Integer) systemSessionFactory
.newRepositorySession(request)
Expand All @@ -358,7 +370,8 @@ void transportConfigurationTest() throws InvalidRepositoryException {
settingsDecrypter,
eventSpyDispatcher,
information,
defaultTypeRegistry);
defaultTypeRegistry,
versionScheme);

MavenExecutionRequest request = new DefaultMavenExecutionRequest();
request.setLocalRepository(getLocalRepository());
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ under the License.
<plexusInterpolationVersion>1.26</plexusInterpolationVersion>
<plexusTestingVersion>1.0.0</plexusTestingVersion>
<plexusXmlVersion>4.0.1</plexusXmlVersion>
<resolverVersion>2.0.0-alpha-5</resolverVersion>
<resolverVersion>2.0.0-alpha-6</resolverVersion>
cstamas marked this conversation as resolved.
Show resolved Hide resolved
<securityDispatcherVersion>2.0</securityDispatcherVersion>
<sisuVersion>0.9.0.M2</sisuVersion>
<slf4jVersion>2.0.11</slf4jVersion>
Expand Down