diff --git a/BUILDING.md b/BUILDING.md
index d9bbc1505ff95..1246102faf91d 100644
--- a/BUILDING.md
+++ b/BUILDING.md
@@ -97,6 +97,70 @@ will have the `origin` attribute been set to `Generated by Gradle`.
> If you want to add a level of verification you can manually confirm the checksum (e.g. by looking it up on the website of the library)
> Please replace the content of the `origin` attribute by `official site` in that case.
+##### Handling transitive dependencies
+
+Dependency management is a critical aspect of maintaining a secure and reliable build system, requiring explicit control over what we rely on. The Elasticsearch build mainly uses component metadata rules declared in the `ComponentMetadataRulesPlugin`
+plugin to manage transitive dependencies and avoid version conflicts.
+This approach ensures we have explicit control over all dependencies used in the build.
+
+###### General Guidelines
+
+1. **Avoid unused transitive dependencies** - Dependencies that are not actually used by our code should be excluded to reduce the attack surface and avoid potential conflicts.
+
+2. **Prefer versions declared in `build-tools-internal/version.properties`** - All dependency versions should be centrally managed in this file to ensure consistency across the entire build.
+
+3. **Libraries required to compile our code should be direct dependencies** - If we directly use a library in our source code, it should be declared as a direct dependency rather than relying on it being transitively available.
+
+###### Component Metadata Rules
+
+We use two main types of component metadata rules at this point to manage transitive dependencies:
+
+- **`ExcludeAllTransitivesRule`** - Excludes all transitive dependencies for libraries where we want complete control over dependencies or the transitive dependencies are unused.
+
+- **`ExcludeOtherGroupsTransitiveRule`** - Excludes transitive dependencies that don't belong to the same group as the direct dependency, while keeping same-group dependencies.
+-
+- **`ExcludeByGroup`** - Excludes transitive dependencies that match a specific groupId while keeping all other transitive dependencies with different groupIds.
+
+Examples from the `ComponentMetadataRulesPlugin`:
+
+```gradle
+// Exclude all transitives - used when transitive deps are unused or problematic
+components.withModule("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor", ExcludeAllTransitivesRule.class);
+
+// Exclude other groups - used when we want same-group deps but not external ones
+components.withModule("com.azure:azure-core", ExcludeOtherGroupsTransitiveRule.class);
+
+// Exclude only specific groups - used when we want exclude specific group of transitive deps.
+components.withModule("org.apache.logging.log4j:log4j-api", ExcludeByGroup.class, rule -> {
+ rule.params(List.of("biz.aQute.bnd", "org.osgi"));
+});
+```
+
+###### Common Scenarios
+
+**Version Conflicts**: When a transitive dependency brings in a different version than what we use:
+```gradle
+// brings in jackson-databind and jackson-annotations not used
+components.withModule("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor", ExcludeAllTransitivesRule.class);
+```
+
+**Unused Dependencies**: When transitive dependencies are not actually used:
+```gradle
+// brings in azure-core-http-netty. not used
+components.withModule("com.azure:azure-core-http-netty", ExcludeAllTransitivesRule.class);
+```
+
+**Mismatching Version Dependencies**: When other versions are required:
+```gradle
+// brings in org.slf4j:slf4j-api:1.7.25. We use 2.0.6
+components.withModule("org.apache.directory.api:api-asn1-ber", ExcludeOtherGroupsTransitiveRule.class);
+```
+
+When adding or updating dependencies, ensure that any required transitive dependencies are either:
+1. Already available as direct dependencies with compatible versions
+2. Added as direct dependencies if they're actually used by our code
+3. Properly excluded if they're not needed
+
#### Custom plugin and task implementations
Build logic that is used across multiple subprojects should be considered to be moved into a Gradle plugin with according Gradle task implementation.
diff --git a/build-conventions/build.gradle b/build-conventions/build.gradle
index 9416c3028e8ee..3ecedee9d6e44 100644
--- a/build-conventions/build.gradle
+++ b/build-conventions/build.gradle
@@ -72,6 +72,9 @@ repositories {
}
dependencies {
+ constraints {
+ api("org.slf4j:slf4j-api:2.0.6")
+ }
api buildLibs.maven.model
api buildLibs.shadow.plugin
api buildLibs.apache.rat
diff --git a/build-tools-internal/build.gradle b/build-tools-internal/build.gradle
index 6f5dc5e0ca62c..6f1d8c52eafd1 100644
--- a/build-tools-internal/build.gradle
+++ b/build-tools-internal/build.gradle
@@ -40,6 +40,10 @@ gradlePlugin {
id = 'elasticsearch.build-complete'
implementationClass = 'org.elasticsearch.gradle.internal.ElasticsearchBuildCompletePlugin'
}
+ componentMetadataRules {
+ id = 'elasticsearch.component-metadata-rules'
+ implementationClass = 'org.elasticsearch.gradle.internal.dependencies.rules.ComponentMetadataRulesPlugin'
+ }
distro {
id = 'elasticsearch.distro'
implementationClass = 'org.elasticsearch.gradle.internal.distribution.ElasticsearchDistributionPlugin'
@@ -281,6 +285,9 @@ dependencies {
testImplementation buildLibs.asm
integTestImplementation buildLibs.asm
api(buildLibs.snakeyaml)
+ api("org.slf4j:slf4j-api:2.0.6") {
+ because("Align with what we use in production")
+ }
}
// Forcefully downgrade the jackson platform as used in production
api enforcedPlatform(buildLibs.jackson.platform)
diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaBasePlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaBasePlugin.java
index 70f6cecb8e725..4cedd69d9b700 100644
--- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaBasePlugin.java
+++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaBasePlugin.java
@@ -16,18 +16,14 @@
import org.elasticsearch.gradle.internal.test.MutedTestPlugin;
import org.elasticsearch.gradle.internal.test.TestUtil;
import org.elasticsearch.gradle.test.SystemPropertyCommandLineArgumentProvider;
-import org.elasticsearch.gradle.util.GradleUtils;
import org.gradle.api.JavaVersion;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ResolutionStrategy;
import org.gradle.api.file.FileCollection;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.provider.Provider;
-import org.gradle.api.tasks.SourceSet;
-import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.compile.AbstractCompile;
import org.gradle.api.tasks.compile.CompileOptions;
import org.gradle.api.tasks.compile.GroovyCompile;
@@ -67,8 +63,6 @@ public void apply(Project project) {
project.getPluginManager().apply(ElasticsearchTestBasePlugin.class);
project.getPluginManager().apply(PrecommitTaskPlugin.class);
project.getPluginManager().apply(MutedTestPlugin.class);
-
- configureConfigurations(project);
configureCompile(project);
configureInputNormalization(project);
configureNativeLibraryPath(project);
@@ -77,54 +71,6 @@ public void apply(Project project) {
project.getExtensions().getExtraProperties().set("versions", VersionProperties.getVersions());
}
- /**
- * Makes dependencies non-transitive.
- *
- * Gradle allows setting all dependencies as non-transitive very easily.
- * Sadly this mechanism does not translate into maven pom generation. In order
- * to effectively make the pom act as if it has no transitive dependencies,
- * we must exclude each transitive dependency of each direct dependency.
- *
- * Determining the transitive deps of a dependency which has been resolved as
- * non-transitive is difficult because the process of resolving removes the
- * transitive deps. To sidestep this issue, we create a configuration per
- * direct dependency version. This specially named and unique configuration
- * will contain all of the transitive dependencies of this particular
- * dependency. We can then use this configuration during pom generation
- * to iterate the transitive dependencies and add excludes.
- */
- public static void configureConfigurations(Project project) {
- // we are not shipping these jars, we act like dumb consumers of these things
- if (project.getPath().startsWith(":test:fixtures") || project.getPath().equals(":build-tools")) {
- return;
- }
- // fail on any conflicting dependency versions
- project.getConfigurations().all(configuration -> {
- if (configuration.getName().endsWith("Fixture")) {
- // just a self contained test-fixture configuration, likely transitive and hellacious
- return;
- }
- configuration.resolutionStrategy(ResolutionStrategy::failOnVersionConflict);
- });
-
- // disable transitive dependency management
- SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
- sourceSets.all(sourceSet -> disableTransitiveDependenciesForSourceSet(project, sourceSet));
- }
-
- private static void disableTransitiveDependenciesForSourceSet(Project project, SourceSet sourceSet) {
- List sourceSetConfigurationNames = List.of(
- sourceSet.getApiConfigurationName(),
- sourceSet.getImplementationConfigurationName(),
- sourceSet.getCompileOnlyConfigurationName(),
- sourceSet.getRuntimeOnlyConfigurationName()
- );
-
- project.getConfigurations()
- .matching(c -> sourceSetConfigurationNames.contains(c.getName()))
- .configureEach(GradleUtils::disableTransitiveDependencies);
- }
-
/**
* Adds compiler settings to the project
*/
diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaModulePathPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaModulePathPlugin.java
index ec4663bf9fda2..2fedf792e8c77 100644
--- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaModulePathPlugin.java
+++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/ElasticsearchJavaModulePathPlugin.java
@@ -21,6 +21,7 @@
import org.gradle.api.artifacts.result.ResolvedComponentResult;
import org.gradle.api.artifacts.result.ResolvedDependencyResult;
import org.gradle.api.attributes.LibraryElements;
+import org.gradle.api.attributes.Usage;
import org.gradle.api.file.FileCollection;
import org.gradle.api.logging.Logger;
import org.gradle.api.plugins.JavaPlugin;
@@ -75,12 +76,13 @@ void configureCompileModulePath(Project project) {
it.extendsFrom(compileClasspath);
it.setCanBeResolved(true);
it.setCanBeConsumed(false); // we don't want this configuration used by dependent projects
- it.attributes(
- attrs -> attrs.attribute(
+ it.attributes(attrs -> {
+ attrs.attribute(
LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
project.getObjects().named(LibraryElements.class, LibraryElements.CLASSES)
- )
- );
+ );
+ attrs.attribute(Usage.USAGE_ATTRIBUTE, project.getObjects().named(Usage.class, Usage.JAVA_API));
+ });
}).getIncoming().artifactView(it -> {
it.componentFilter(cf -> {
var visited = new HashSet();
diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ComponentMetadataRulesPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ComponentMetadataRulesPlugin.java
new file mode 100644
index 0000000000000..46a4f5728856f
--- /dev/null
+++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ComponentMetadataRulesPlugin.java
@@ -0,0 +1,501 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the "Elastic License
+ * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+package org.elasticsearch.gradle.internal.dependencies.rules;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.artifacts.dsl.ComponentMetadataHandler;
+import org.gradle.api.initialization.Settings;
+
+import java.util.List;
+
+/**
+ * A settings plugin that configures component metadata rules for dependency management.
+ * This plugin centralizes the configuration of transitive dependency exclusion rules.
+ * Since we want to use the same rules in serverless an stack setup we cannot just put
+ * this into settings.gradle but encapsulate it in a plugin that can be reused in the
+ * context of serverless.
+ */
+public class ComponentMetadataRulesPlugin implements Plugin {
+
+ @Override
+ public void apply(Settings settings) {
+ ComponentMetadataHandler components = settings.getDependencyResolutionManagement().getComponents();
+
+ // Azure dependencies
+ components.withModule("com.azure:azure-core", ExcludeOtherGroupsTransitiveRule.class);
+ // brings in azure-core-http-netty. not used
+ components.withModule("com.azure:azure-core-http-netty", ExcludeAllTransitivesRule.class);
+ components.withModule("com.azure:azure-core-http-okhttp", ExcludeOtherGroupsTransitiveRule.class);
+
+ // brings in net.java.dev.jna:jna-platform:5.6.0. We use 5.12.1.
+ // brings in com.azure:azure-core-http-netty:1.15.1
+ components.withModule("com.azure:azure-identity", ExcludeAllTransitivesRule.class);
+
+ // brings in com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.17.2. We use 2.15.0
+ components.withModule("com.azure:azure-storage-blob", ExcludeOtherGroupsTransitiveRule.class);
+ components.withModule("com.azure:azure-storage-blob-batch", ExcludeOtherGroupsTransitiveRule.class);
+ components.withModule("com.azure:azure-storage-common", ExcludeOtherGroupsTransitiveRule.class);
+ components.withModule("com.azure:azure-storage-internal-avro", ExcludeAllTransitivesRule.class);
+
+ // Testing dependencies
+ components.withModule("com.carrotsearch.randomizedtesting:randomizedtesting-runner", ExcludeAllTransitivesRule.class);
+
+ // Jackson dependencies
+ components.withModule("com.fasterxml.jackson.core:jackson-core", ExcludeOtherGroupsTransitiveRule.class);
+ // brings in jackson-databind and jackson-annotations not used
+ components.withModule("com.fasterxml.jackson.dataformat:jackson-dataformat-cbor", ExcludeAllTransitivesRule.class);
+ components.withModule("com.fasterxml.jackson.dataformat:jackson-dataformat-smile", ExcludeAllTransitivesRule.class);
+
+ // brings woodstox-core 6.7.0, we use 6.5.1
+ components.withModule("com.fasterxml.jackson.dataformat:jackson-dataformat-xml", ExcludeOtherGroupsTransitiveRule.class);
+
+ // brings in snakeyaml with wrong version
+ components.withModule("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml", ExcludeOtherGroupsTransitiveRule.class);
+
+ components.withModule("com.fasterxml.jackson.module:jackson-module-jaxb-annotations", ExcludeOtherGroupsTransitiveRule.class);
+
+ // Google dependencies
+ components.withModule("com.google.api-client:google-api-client", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.api.grpc:proto-google-cloud-storage-v2", ExcludeOtherGroupsTransitiveRule.class);
+
+ // brings com.google.api.grpc:proto-google-common-protos:2.9.2 we
+ components.withModule("com.google.api.grpc:proto-google-iam-v1", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.api:api-common", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.api:gax", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.api:gax-httpjson", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.apis:google-api-services-storage", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.auth:google-auth-library-oauth2-http", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.cloud:google-cloud-core", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.cloud:google-cloud-core-http", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.cloud:google-cloud-storage", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.code.gson:gson", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.guava:guava", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.http-client:google-http-client", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.http-client:google-http-client-appengine", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.http-client:google-http-client-gson", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.http-client:google-http-client-jackson2", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.jimfs:jimfs", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.oauth-client:google-oauth-client", ExcludeAllTransitivesRule.class);
+ components.withModule("com.google.protobuf:protobuf-java-util", ExcludeAllTransitivesRule.class);
+ components.withModule("com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer", ExcludeAllTransitivesRule.class);
+
+ // Other dependencies
+ components.withModule("com.maxmind.geoip2:geoip2", ExcludeAllTransitivesRule.class);
+
+ // Microsoft dependencies
+ components.withModule("com.microsoft.azure:azure-core", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.azure:azure-svc-mgmt-compute", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.azure:msal4j", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.azure:msal4j-persistence-extension", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.graph:microsoft-graph", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.graph:microsoft-graph-core", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.kiota:microsoft-kiota-abstractions", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.kiota:microsoft-kiota-authentication-azure", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.kiota:microsoft-kiota-http-okHttp", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.kiota:microsoft-kiota-serialization-form", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.kiota:microsoft-kiota-serialization-json", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.kiota:microsoft-kiota-serialization-multipart", ExcludeAllTransitivesRule.class);
+ components.withModule("com.microsoft.kiota:microsoft-kiota-serialization-text", ExcludeAllTransitivesRule.class);
+
+ // Network dependencies
+ components.withModule("com.networknt:json-schema-validator", ExcludeAllTransitivesRule.class);
+ components.withModule("com.nimbusds:oauth2-oidc-sdk", ExcludeAllTransitivesRule.class);
+ components.withModule("com.squareup.okhttp3:okhttp", ExcludeAllTransitivesRule.class);
+ components.withModule("com.squareup.okio:okio", ExcludeAllTransitivesRule.class);
+ components.withModule("com.squareup.okio:okio-jvm", ExcludeAllTransitivesRule.class);
+
+ // Jakarta/javax mail dependencies
+ components.withModule("com.sun.mail:jakarta.mail", ExcludeAllTransitivesRule.class);
+ components.withModule("com.sun.xml.bind:jaxb-impl", ExcludeAllTransitivesRule.class);
+
+ // Mapbox dependencies
+ components.withModule("com.wdtinc:mapbox-vector-tile", ExcludeAllTransitivesRule.class);
+
+ // Dropwizard dependencies
+ components.withModule("io.dropwizard.metrics:metrics-core", ExcludeAllTransitivesRule.class);
+
+ // gRPC dependencies
+ components.withModule("io.grpc:grpc-api", ExcludeAllTransitivesRule.class);
+
+ // Netty dependencies
+ components.withModule("io.netty:netty-buffer", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-codec", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-codec-dns", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-codec-http", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-codec-http2", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-codec-socks", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-handler", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-handler-proxy", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-resolver", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-resolver-dns", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-transport", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-transport-classes-epoll", ExcludeAllTransitivesRule.class);
+ components.withModule("io.netty:netty-transport-native-unix-common", ExcludeAllTransitivesRule.class);
+
+ // OpenCensus dependencies
+ components.withModule("io.opencensus:opencensus-api", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opencensus:opencensus-contrib-http-util", ExcludeAllTransitivesRule.class);
+
+ // OpenTelemetry dependencies
+ components.withModule("io.opentelemetry:opentelemetry-api", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opentelemetry:opentelemetry-context", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opentelemetry:opentelemetry-exporter-common", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opentelemetry:opentelemetry-exporter-otlp", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opentelemetry:opentelemetry-exporter-otlp-common", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opentelemetry:opentelemetry-exporter-sender-jdk", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opentelemetry:opentelemetry-sdk", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opentelemetry:opentelemetry-sdk-common", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opentelemetry:opentelemetry-sdk-metrics", ExcludeAllTransitivesRule.class);
+ components.withModule("io.opentelemetry:opentelemetry-semconv", ExcludeAllTransitivesRule.class);
+
+ // Project Reactor dependencies
+ components.withModule("io.projectreactor.netty:reactor-netty-core", ExcludeAllTransitivesRule.class);
+ components.withModule("io.projectreactor.netty:reactor-netty-http", ExcludeAllTransitivesRule.class);
+ components.withModule("io.projectreactor:reactor-core", ExcludeAllTransitivesRule.class);
+
+ // S2 Geometry dependencies
+ components.withModule("io.sgr:s2-geometry-library-java", ExcludeAllTransitivesRule.class);
+
+ // Jakarta XML dependencies
+ components.withModule("jakarta.xml.bind:jakarta.xml.bind-api", ExcludeAllTransitivesRule.class);
+
+ // javax mail dependencies
+ components.withModule("javax.mail:mail", ExcludeAllTransitivesRule.class);
+ components.withModule("javax.xml.bind:jaxb-api", ExcludeAllTransitivesRule.class);
+
+ // Testing dependencies
+ components.withModule("junit:junit", ExcludeAllTransitivesRule.class);
+
+ // JNA dependencies
+ components.withModule("net.java.dev.jna:jna-platform", ExcludeAllTransitivesRule.class);
+
+ // JSON dependencies
+ components.withModule("net.minidev:accessors-smart", ExcludeAllTransitivesRule.class);
+ components.withModule("net.minidev:json-smart", ExcludeAllTransitivesRule.class);
+
+ // EhCache dependencies
+ components.withModule("net.sf.ehcache:ehcache", ExcludeAllTransitivesRule.class);
+
+ // Shibboleth dependencies
+ components.withModule("net.shibboleth.utilities:java-support", ExcludeAllTransitivesRule.class);
+
+ // Apache Arrow dependencies
+ components.withModule("org.apache.arrow:arrow-format", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.arrow:arrow-memory-core", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.arrow:arrow-vector", ExcludeAllTransitivesRule.class);
+
+ // Apache Commons dependencies
+ components.withModule("org.apache.commons:commons-compress", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.commons:commons-text", ExcludeAllTransitivesRule.class);
+
+ // org.apache.directory.api:api-asn1-ber brings in org.slf4j:slf4j-api:1.7.25. We use 2.0.6
+ components.withModule("org.apache.directory.api:api-asn1-ber", ExcludeOtherGroupsTransitiveRule.class);
+
+ // org.apache.directory.api:api-ldap-client-api brings in org.apache.mina:mina-core:2.0.16. We use 2.2.4
+ components.withModule("org.apache.directory.api:api-ldap-client-api", ExcludeOtherGroupsTransitiveRule.class);
+ components.withModule("org.apache.directory.api:api-ldap-codec-core", ExcludeAllTransitivesRule.class);
+
+ // "org.apache.directory.api:api-ldap-codec-standalone brings in org.apache.mina:mina-core:2.0.16. We use 2.2.4
+ components.withModule("org.apache.directory.api:api-ldap-codec-standalone", ExcludeOtherGroupsTransitiveRule.class);
+
+ // TODO: For org.apache.directory.api dependencies we use partially 1.0.1 and partially 1.0.0. We should align these.
+ components.withModule("org.apache.directory.api:api-ldap-extras-aci", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.directory.api:api-ldap-schema-data", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.directory.api:api-ldap-extras-codec-api", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.directory.api:api-ldap-extras-sp", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.directory.api:api-ldap-extras-util", ExcludeAllTransitivesRule.class);
+
+ // org.apache.directory.api:api-ldap-model brings in org.apache.mina:mina-core:2.0.17. We use 2.2.4
+ // org.apache.directory.api:api-ldap-model brings in org.apache.servicemix.bundles:org.apache.servicemix.bundles.antlr:2.7.7_5. We
+ // use 2.7.7_5.
+ // org.apache.directory.api:api-ldap-model brings in commons-codec:commons-lang:commons-lang:2.6. We use 2.6.
+ // org.apache.directory.api:api-ldap-model brings in commons-collections:commons-collections:3.2.2. We use 3.3.2.
+ // org.apache.directory.api:api-ldap-model brings in commons-codec:commons-codec:1.10. We use 1.15.
+ // TODO exclude matching third party deps from being excluded
+ components.withModule("org.apache.directory.api:api-ldap-model", ExcludeOtherGroupsTransitiveRule.class);
+
+ // org.apache.directory.api:api-ldap-model brings in org.apache.mina:mina-core:2.0.17. We use 2.2.4
+ components.withModule("org.apache.directory.api:api-ldap-net-mina", ExcludeOtherGroupsTransitiveRule.class);
+
+ // org.apache.directory.api:api-asn1-ber brings in org.slf4j:slf4j-api:1.7.25. We use 2.0.6
+ // TODO: For org.apache.directory.api dependencies we use partially 1.0.1 and partially 1.0.0. We should align these.
+ components.withModule("org.apache.directory.api:api-util", ExcludeAllTransitivesRule.class);
+
+ // Apache Directory Server dependencies
+ components.withModule("org.apache.directory.jdbm:apacheds-jdbm1", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.directory.mavibot:mavibot", ExcludeAllTransitivesRule.class);
+ // org.apache.directory.server:apacheds-core-annotations brings in org.apache.directory.api:api-ldap-model:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-core-annotations brings in org.apache.directory.api:api-util:1.0.0. We use 1.0.1.
+ components.withModule("org.apache.directory.server:apacheds-core-annotations", ExcludeAllTransitivesRule.class);
+
+ // brings in org.slf4j:slf4j-api:1.7.25. We use 2.0.6
+ components.withModule("org.apache.directory.server:apacheds-core", ExcludeAllTransitivesRule.class);
+ // brings in org.apache.directory.server:apacheds-core:2.0.0-M24
+ components.withModule("org.apache.directory.server:apacheds-interceptor-kerberos", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.directory.server:apacheds-core-shared", ExcludeOtherGroupsTransitiveRule.class);
+ // brings in org.apache.directory.server:apacheds-core-shared:2.0.0-M24
+ components.withModule("org.apache.directory.server:apacheds-ldif-partition", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.directory.server:apacheds-protocol-shared", ExcludeOtherGroupsTransitiveRule.class);
+ components.withModule("org.apache.directory.server:ldap-client-test", ExcludeOtherGroupsTransitiveRule.class);
+
+ // org.apache.directory.server:apacheds-core-api brings in org.apache.directory.api:api-asn1-api:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-core-api brings in org.apache.directory.api:api-i18n:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-core-api brings in org.apache.directory.api:api-ldap-model:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-core-api brings in org.apache.directory.api:api-util:1.0.0. We use 1.0.1.
+ components.withModule("org.apache.directory.server:apacheds-core-api", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.directory.server:apacheds-i18n", ExcludeAllTransitivesRule.class);
+ // org.apache.directory.server:apacheds-jdbm-partition brings in org.apache.directory.api:api-ldap-model:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-jdbm-partition brings in org.apache.directory.api:api-util:1.0.0. We use 1.0.1.
+ components.withModule("org.apache.directory.server:apacheds-jdbm-partition", ExcludeAllTransitivesRule.class);
+ // org.apache.directory.server:apacheds-kerberos-codec brings in org.apache.directory.api:api-asn1-api:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-kerberos-codec brings in org.apache.directory.api:api-asn1-ber:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-kerberos-codec brings in org.apache.directory.api:api-i18n:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-kerberos-codec brings in org.apache.directory.api:api-ldap-model:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-kerberos-codec brings in org.apache.directory.api:api-util:1.0.0. We use 1.0.1.
+ components.withModule("org.apache.directory.server:apacheds-kerberos-codec", ExcludeAllTransitivesRule.class);
+ // org.apache.directory.server:apacheds-mavibot-partition brings in org.apache.directory.api:api-ldap-model:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-mavibot-partition brings in org.apache.directory.api:api-util:1.0.0. We use 1.0.1.
+ components.withModule("org.apache.directory.server:apacheds-mavibot-partition", ExcludeAllTransitivesRule.class);
+ // org.apache.directory.server:apacheds-protocol-kerberos brings in org.apache.directory.api:api-asn1-api:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-protocol-kerberos brings in org.apache.directory.api:api-ldap-model:1.0.0. We use 1.0.1.
+ components.withModule("org.apache.directory.server:apacheds-protocol-kerberos", ExcludeAllTransitivesRule.class);
+ // org.apache.directory.server:apacheds-protocol-ldap brings in org.apache.directory.api:api-asn1-ber:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-protocol-ldap brings in org.apache.directory.api:api-ldap-model:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-protocol-ldap brings in org.apache.directory.api:api-util:1.0.0. We use 1.0.1.
+ components.withModule("org.apache.directory.server:apacheds-protocol-ldap", ExcludeAllTransitivesRule.class);
+ // org.apache.directory.server:apacheds-server-annotations brings in org.apache.directory.api:api-ldap-model:1.0.0. We use 1.0.1.
+ components.withModule("org.apache.directory.server:apacheds-server-annotations", ExcludeAllTransitivesRule.class);
+ // org.apache.directory.server:apacheds-test-framework brings in org.apache.directory.api:api-ldap-model:1.0.0. We use 1.0.1.
+ components.withModule("org.apache.directory.server:apacheds-test-framework", ExcludeAllTransitivesRule.class);
+ // org.apache.directory.server:apacheds-xdbm-partition brings in org.apache.directory.api:api-ldap-model:1.0.0. We use 1.0.1.
+ // org.apache.directory.server:apacheds-xdbm-partition brings in org.apache.directory.api:api-util:1.0.0. We use 1.0.1.
+ components.withModule("org.apache.directory.server:apacheds-xdbm-partition", ExcludeAllTransitivesRule.class);
+
+ // org.apache.directory.api:api-ldap-client-api brings in org.apache.mina:mina-core:2.0.16. We use 2.2.4
+ components.withModule("org.apache.directory.server:apacheds-interceptors-authn", ExcludeAllTransitivesRule.class);
+
+ // Hadoop dependencies
+ components.withModule("org.apache.hadoop:hadoop-client-api", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.hadoop:hadoop-client-runtime", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.hadoop:hadoop-common", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.hadoop:hadoop-hdfs", ExcludeAllTransitivesRule.class);
+
+ // Apache HTTP dependencies
+ components.withModule("org.apache.httpcomponents.client5:httpclient5", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.httpcomponents:fluent-hc", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.httpcomponents:httpasyncclient", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.httpcomponents:httpclient", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.httpcomponents:httpclient-cache", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.httpcomponents:httpcore-nio", ExcludeAllTransitivesRule.class);
+
+ // Apache James dependencies
+ components.withModule("org.apache.james:apache-mime4j-core", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.james:apache-mime4j-dom", ExcludeAllTransitivesRule.class);
+
+ // Apache Kerby dependencies
+ components.withModule("org.apache.kerby:kerb-admin", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.kerby:kerb-client", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.kerby:kerb-common", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.kerby:kerb-identity", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.kerby:kerb-server", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.kerby:kerb-simplekdc", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.kerby:kerb-util", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.kerby:kerby-config", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.kerby:kerby-pkix", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.kerby:ldap-backend", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.kerby:token-provider", ExcludeAllTransitivesRule.class);
+
+ // Apache Log4j dependencies
+ // We want to remove log4j-api compile only dependency on biz.aQute.bnd and org.osgi group but
+ // keep other compile only dependencies like spotbugs, errorprone and jspecify
+ components.withModule("org.apache.logging.log4j:log4j-api", ExcludeByGroup.class, config -> {
+ config.params(List.of("biz.aQute.bnd", "org.osgi"));
+ });
+ components.withModule("org.apache.logging.log4j:log4j-1.2-api", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.logging.log4j:log4j-core", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.logging.log4j:log4j-jcl", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.logging.log4j:log4j-slf4j-impl", ExcludeAllTransitivesRule.class);
+
+ // lucene-analysis-morfologik brings in org.carrot2:morfologik-stemming:2.1.9. we use 2.1.1
+ // lucene-analysis-morfologik brings in org.carrot2:morfologik-polish:2.1.9. we use none.
+ // lucene-analysis-morfologik brings in ua.net.nlp:morfologik-ukrainian-search:4.9.1 we use 3.7.5.
+ components.withModule("org.apache.lucene:lucene-analysis-morfologik", ExcludeOtherGroupsTransitiveRule.class);
+
+ // lucene-analysis-icu brings in com.ibm.icu:icu4j:74.2 we use 68.2.
+ components.withModule("org.apache.lucene:lucene-analysis-icu", ExcludeOtherGroupsTransitiveRule.class);
+
+ // lucene-analysis-phonetic brings in commons-codec:1.17. We use 1.15
+ components.withModule("org.apache.lucene:lucene-analysis-phonetic", ExcludeOtherGroupsTransitiveRule.class);
+
+ // lucene-spatial-extras brings in different version of spatial4j
+ // lucene-spatial-extras brings in different version of s2-geometry-library-java
+ components.withModule("org.apache.lucene:lucene-spatial-extras", ExcludeOtherGroupsTransitiveRule.class);
+
+ // lucene-expressions brings in org.antlr:antlr4-runtime:4.13.2
+ // lucene-expressions brings in org.ow2.asm:asm:9.6
+ // lucene-expressions brings in org.ow2.asm:asm-commons:9.6
+ components.withModule("org.apache.lucene:lucene-expressions", ExcludeOtherGroupsTransitiveRule.class);
+ components.withModule("org.apache.lucene:lucene-test-framework", ExcludeOtherGroupsTransitiveRule.class);
+
+ // Apache Mina dependencies
+ components.withModule("org.apache.mina:mina-core", ExcludeAllTransitivesRule.class);
+
+ // Apache PDFBox dependencies
+ components.withModule("org.apache.pdfbox:fontbox", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.pdfbox:pdfbox", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.pdfbox:pdfbox-io", ExcludeAllTransitivesRule.class);
+
+ // Apache POI dependencies
+ components.withModule("org.apache.poi:poi", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.poi:poi-ooxml", ExcludeAllTransitivesRule.class);
+ components.withModule("org.apache.poi:poi-scratchpad", ExcludeAllTransitivesRule.class);
+
+ // Apache Santuario dependencies
+ components.withModule("org.apache.santuario:xmlsec", ExcludeAllTransitivesRule.class);
+
+ // org.apache.tika:tika-core brings in org.slf4j:slf4j-api:2.0.17. We use 2.0.6
+ // org.apache.tika:tika-core brings in commons-io:commons-io:2.20.0. We use 2.5
+ components.withModule("org.apache.tika:tika-core", ExcludeOtherGroupsTransitiveRule.class);
+
+ // org.apache.tika:tika-langdetect-tika brings in com.optimaize.languagedetector:language-detector:0.6.
+ components.withModule("org.apache.tika:tika-langdetect-tika", ExcludeOtherGroupsTransitiveRule.class);
+ // org.apache.tika:tika-parser-apple-module brings in com.googlecode.plist:dd-plist:1.28.
+ components.withModule("org.apache.tika:tika-parser-apple-module", ExcludeOtherGroupsTransitiveRule.class);
+ // org.apache.tika:tika-parser-microsoft-module brings in com.healthmarketscience.jackcess:jackcess-encrypt:4.0.3.
+ // org.apache.tika:tika-parser-microsoft-module brings in com.healthmarketscience.jackcess:jackcess:4.0.8.
+ // org.apache.tika:tika-parser-microsoft-module brings in com.pff:java-libpst:0.9.3.
+ // org.apache.tika:tika-parser-microsoft-module brings in commons-logging:commons-logging:1.3.5. We use 1.2.
+ // org.apache.tika:tika-parser-microsoft-module brings in org.bouncycastle:bcjmail-jdk18on:1.81.
+ // org.apache.tika:tika-parser-microsoft-module brings in org.bouncycastle:bcprov-jdk18on:1.81. We use 1.78.1/1.79.
+ // org.apache.tika:tika-parser-microsoft-module brings in tika-parser-mail-commons.
+ components.withModule("org.apache.tika:tika-parser-microsoft-module", ExcludeAllTransitivesRule.class);
+ // org.apache.tika:tika-parser-miscoffice-module brings in org.glassfish.jaxb:jaxb-runtime:4.0.5.
+ components.withModule("org.apache.tika:tika-parser-miscoffice-module", ExcludeOtherGroupsTransitiveRule.class);
+ // org.apache.tika:tika-parser-pdf-module brings in org.apache.pdfbox:pdfbox-tools:3.0.5.
+ // org.apache.tika:tika-parser-pdf-module brings in org.bouncycastle:bcjmail-jdk18on:1.81. Closest we use is
+ // bcprov-jdk18on:1.78.1/1.79.
+ // org.apache.tika:tika-parser-pdf-module brings in org.bouncycastle:bcprov-jdk18on:1.81. We use 1.78.1/1.79.
+ // org.apache.tika:tika-parser-pdf-module brings in org.glassfish.jaxb:jaxb-runtime:4.0.5.
+ components.withModule("org.apache.tika:tika-parser-pdf-module", ExcludeOtherGroupsTransitiveRule.class);
+ // org.apache.tika:tika-parser-text-module brings in com.github.albfernandez:juniversalchardet:2.5.0..
+ // org.apache.tika:tika-parser-text-module brings in org.apache.commons:commons-csv:1.14.1. We use 1.0.
+ components.withModule("org.apache.tika:tika-parser-text-module", ExcludeOtherGroupsTransitiveRule.class);
+ // org.apache.tika:tika-parser-xmp-commons brings in org.apache.pdfbox:xmpbox:3.0.5.
+ components.withModule("org.apache.tika:tika-parser-xmp-commons", ExcludeOtherGroupsTransitiveRule.class);
+
+ // Apache XMLBeans dependencies
+ components.withModule("org.apache.xmlbeans:xmlbeans", ExcludeAllTransitivesRule.class);
+
+ // BouncyCastle dependencies
+ components.withModule("org.bouncycastle:bcpg-fips", ExcludeAllTransitivesRule.class);
+ components.withModule("org.bouncycastle:bcpkix-jdk18on", ExcludeAllTransitivesRule.class);
+ components.withModule("org.bouncycastle:bcutil-jdk18on", ExcludeAllTransitivesRule.class);
+
+ // Carrot2 dependencies
+ components.withModule("org.carrot2:morfologik-stemming", ExcludeAllTransitivesRule.class);
+
+ // Kotlin dependencies
+ components.withModule("org.jetbrains.kotlin:kotlin-stdlib", ExcludeAllTransitivesRule.class);
+
+ // JONI dependencies
+ components.withModule("org.jruby.joni:joni", ExcludeAllTransitivesRule.class);
+
+ // JUnit dependencies
+ components.withModule("org.junit.jupiter:junit-jupiter", ExcludeAllTransitivesRule.class);
+
+ // Mockito dependencies
+ components.withModule("org.mockito:mockito-core", ExcludeAllTransitivesRule.class);
+ components.withModule("org.mockito:mockito-subclass", ExcludeAllTransitivesRule.class);
+
+ // JMH dependencies
+ components.withModule("org.openjdk.jmh:jmh-core", ExcludeAllTransitivesRule.class);
+
+ // OpenSAML dependencies
+ components.withModule("org.opensaml:opensaml-core", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-messaging-api", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-messaging-impl", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-profile-api", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-profile-impl", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-saml-api", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-saml-impl", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-security-api", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-security-impl", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-soap-api", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-soap-impl", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-storage-api", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-storage-impl", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-xmlsec-api", ExcludeAllTransitivesRule.class);
+ components.withModule("org.opensaml:opensaml-xmlsec-impl", ExcludeAllTransitivesRule.class);
+
+ // OrbisGIS dependencies
+ components.withModule("org.orbisgis:cts", ExcludeAllTransitivesRule.class);
+ components.withModule("org.orbisgis:h2gis", ExcludeAllTransitivesRule.class);
+ components.withModule("org.orbisgis:h2gis-utilities", ExcludeAllTransitivesRule.class);
+
+ // ASM dependencies
+ components.withModule("org.ow2.asm:asm-analysis", ExcludeAllTransitivesRule.class);
+ components.withModule("org.ow2.asm:asm-commons", ExcludeAllTransitivesRule.class);
+ components.withModule("org.ow2.asm:asm-tree", ExcludeAllTransitivesRule.class);
+ components.withModule("org.ow2.asm:asm-util", ExcludeAllTransitivesRule.class);
+
+ // Reactive Streams dependencies
+ components.withModule("org.reactivestreams:reactive-streams-tck", ExcludeAllTransitivesRule.class);
+
+ // SLF4J dependencies
+ components.withModule("org.slf4j:jcl-over-slf4j", ExcludeAllTransitivesRule.class);
+ components.withModule("org.slf4j:slf4j-nop", ExcludeAllTransitivesRule.class);
+ components.withModule("org.slf4j:slf4j-simple", ExcludeAllTransitivesRule.class);
+
+ // SubEtha SMTP dependencies
+ components.withModule("org.subethamail:subethasmtp", ExcludeAllTransitivesRule.class);
+
+ // Testcontainers dependencies
+ components.withModule("org.testcontainers:testcontainers", ExcludeAllTransitivesRule.class);
+
+ // AWS SDK dependencies
+ components.withModule("com.amazonaws:aws-java-sdk-core", ExcludeAllTransitivesRule.class);
+ components.withModule("com.amazonaws:aws-java-sdk-s3", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:bedrockruntime", ExcludeAllTransitivesRule.class);
+
+ components.withModule("software.amazon.awssdk:apache-client", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:arns", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:auth", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:aws-core", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:aws-json-protocol", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:aws-query-protocol", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:aws-xml-protocol", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:checksums", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:checksums-spi", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:ec2", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:endpoints-spi", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:http-auth", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:http-auth-aws", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:http-auth-aws-eventstream", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:http-auth-spi", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:http-client-spi", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:identity-spi", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:imds", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:json-utils", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:metrics-spi", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:netty-nio-client", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:profiles", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:protocol-core", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:regions", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:retries", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:retries-spi", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:s3", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:sdk-core", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:services", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:sts", ExcludeAllTransitivesRule.class);
+ components.withModule("software.amazon.awssdk:utils", ExcludeAllTransitivesRule.class);
+ }
+}
diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ExcludeAllTransitivesRule.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ExcludeAllTransitivesRule.java
new file mode 100644
index 0000000000000..2db24923b5110
--- /dev/null
+++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ExcludeAllTransitivesRule.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the "Elastic License
+ * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+package org.elasticsearch.gradle.internal.dependencies.rules;
+
+import org.gradle.api.artifacts.CacheableRule;
+import org.gradle.api.artifacts.ComponentMetadataContext;
+import org.gradle.api.artifacts.ComponentMetadataRule;
+
+/**
+ * A Gradle component metadata rule that excludes all transitive dependencies.
+ *
+ * The rule operates on all variants of a component.
+ *
+ * Example Usage:
+ * {@code
+ * dependencies {
+ * components {
+ * withModule("com.azure:azure-core-http-netty"", ExcludeAllTransitivesRule)
+ * }
+ * }
+ * }
+ *
+ */
+@CacheableRule
+public abstract class ExcludeAllTransitivesRule implements ComponentMetadataRule {
+
+ @Override
+ public void execute(ComponentMetadataContext context) {
+ context.getDetails().allVariants(variant -> {
+ variant.withDependencies(dependencies -> {
+ // Exclude all transitive dependencies
+ dependencies.clear();
+ });
+ });
+ }
+}
diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ExcludeByGroup.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ExcludeByGroup.java
new file mode 100644
index 0000000000000..bff2a2b46c10b
--- /dev/null
+++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ExcludeByGroup.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the "Elastic License
+ * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+package org.elasticsearch.gradle.internal.dependencies.rules;
+
+import org.gradle.api.artifacts.CacheableRule;
+import org.gradle.api.artifacts.ComponentMetadataContext;
+import org.gradle.api.artifacts.ComponentMetadataRule;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * A Gradle component metadata rule that excludes transitive dependencies of a specific groups.
+ *
+ * The rule operates on all variants of a component.
+ *
+ * Example Usage:
+ * {@code
+ * dependencies {
+ * components {
+ * withModule("org.apache.logging.log4j:log4j-api", ExcludeByGroup) {
+ * params(["biz.aQute.bnd", "org.osgi"])
+ * }
+ * }
+ * }
+ * }
+ *
+ */
+@CacheableRule
+public class ExcludeByGroup implements ComponentMetadataRule {
+
+ private final List groupIds;
+
+ @Inject
+ public ExcludeByGroup(List groupIds) {
+ this.groupIds = groupIds;
+ }
+
+ @Override
+ public void execute(ComponentMetadataContext context) {
+ context.getDetails()
+ .allVariants(v -> v.withDependencies(dependencies -> dependencies.removeIf(dep -> groupIds.contains(dep.getGroup()))));
+ }
+}
diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ExcludeOtherGroupsTransitiveRule.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ExcludeOtherGroupsTransitiveRule.java
new file mode 100644
index 0000000000000..4ebf5a84c14c5
--- /dev/null
+++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ExcludeOtherGroupsTransitiveRule.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the "Elastic License
+ * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
+ * Public License v 1"; you may not use this file except in compliance with, at
+ * your election, the "Elastic License 2.0", the "GNU Affero General Public
+ * License v3.0 only", or the "Server Side Public License, v 1".
+ */
+
+package org.elasticsearch.gradle.internal.dependencies.rules;
+
+import org.gradle.api.artifacts.CacheableRule;
+import org.gradle.api.artifacts.ComponentMetadataContext;
+import org.gradle.api.artifacts.ComponentMetadataRule;
+
+/**
+ * A Gradle component metadata rule that excludes transitive dependencies with different group IDs
+ * than the parent component.
+ *
+ * This rule helps prevent dependency conflicts and reduces the dependency tree size by ensuring
+ * that only dependencies from the same Maven group ID are included transitively. This is particularly
+ * useful for large projects where different modules should not pull in external dependencies from
+ * other organizations or groups.
+ *
+ * The rule operates on all variants of a component and removes any transitive dependency whose
+ * group ID differs from the component's own group ID.
+ *
+ * Example Usage:
+ * {@code
+ * dependencies {
+ * components {
+ * withModule("com.azure:azure-core", ExcludeOtherGroupsTransitiveRule)
+ * }
+ * }
+ * }
+ *
+ * Example Behavior:
+ * If component {@code com.example:my-lib:1.0} has transitive dependencies:
+ *
+ * - {@code com.example:another-lib:1.0} - KEPT (same group)
+ * - {@code org.apache.commons:commons-lang3:3.12.0} - REMOVED (different group)
+ * - {@code com.example:utils:1.5} - KEPT (same group)
+ *
+ *
+ */
+@CacheableRule
+public abstract class ExcludeOtherGroupsTransitiveRule implements ComponentMetadataRule {
+
+ @Override
+ public void execute(ComponentMetadataContext context) {
+ context.getDetails().allVariants(variant -> {
+ variant.withDependencies(dependencies -> {
+ // Exclude transitive dependencies with a different groupId than the parent
+ dependencies.removeIf(dep -> dep.getGroup().equals(context.getDetails().getId().getGroup()) == false);
+ });
+ });
+ }
+}
diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/util/GradleUtils.java b/build-tools/src/main/java/org/elasticsearch/gradle/util/GradleUtils.java
index 8068a8fd9801f..b71da87a477b5 100644
--- a/build-tools/src/main/java/org/elasticsearch/gradle/util/GradleUtils.java
+++ b/build-tools/src/main/java/org/elasticsearch/gradle/util/GradleUtils.java
@@ -14,8 +14,6 @@
import org.gradle.api.Task;
import org.gradle.api.UnknownTaskException;
import org.gradle.api.artifacts.Configuration;
-import org.gradle.api.artifacts.ModuleDependency;
-import org.gradle.api.artifacts.ProjectDependency;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginExtension;
@@ -195,16 +193,6 @@ public static boolean isModuleProject(String projectPath) {
return projectPath.contains("modules:") || projectPath.startsWith(":x-pack:plugin");
}
- public static void disableTransitiveDependencies(Configuration config) {
- config.getDependencies().all(dep -> {
- if (dep instanceof ModuleDependency
- && dep instanceof ProjectDependency == false
- && dep.getGroup().startsWith("org.elasticsearch") == false) {
- ((ModuleDependency) dep).setTransitive(false);
- }
- });
- }
-
public static String projectPath(String taskPath) {
return taskPath.lastIndexOf(':') == 0 ? ":" : taskPath.substring(0, taskPath.lastIndexOf(':'));
}
diff --git a/distribution/packages/build.gradle b/distribution/packages/build.gradle
index 183545ead187a..ed31aa863eb1a 100644
--- a/distribution/packages/build.gradle
+++ b/distribution/packages/build.gradle
@@ -43,6 +43,13 @@ import java.util.regex.Pattern
* dpkg -c path/to/elasticsearch.deb
*/
+buildscript {
+ dependencies {
+ constraints {
+ classpath "org.slf4j:slf4j-api:2.0.6"
+ }
+ }
+}
plugins {
alias(buildLibs.plugins.ospackage)
}
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 2ea93ec253098..5ee90c1dc99ef 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -2175,11 +2175,6 @@
-
-
-
-
-
@@ -4686,36 +4681,11 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/settings.gradle b/settings.gradle
index 48f18281398a5..bd1ae4cc2ad2f 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,7 +1,6 @@
-import org.elasticsearch.gradle.internal.toolchain.OracleOpenJdkToolchainResolver
-import org.elasticsearch.gradle.internal.toolchain.ArchivedOracleJdkToolchainResolver
import org.elasticsearch.gradle.internal.toolchain.AdoptiumJdkToolchainResolver
-import org.elasticsearch.gradle.internal.toolchain.EarlyAccessCatalogJdkToolchainResolver
+import org.elasticsearch.gradle.internal.toolchain.ArchivedOracleJdkToolchainResolver
+import org.elasticsearch.gradle.internal.toolchain.OracleOpenJdkToolchainResolver
pluginManagement {
repositories {
@@ -17,6 +16,7 @@ pluginManagement {
plugins {
id "com.gradle.develocity" version "4.1.1"
id 'elasticsearch.java-toolchain'
+ id 'elasticsearch.component-metadata-rules'
}
enableFeaturePreview "STABLE_CONFIGURATION_CACHE"
diff --git a/test/fixtures/hdfs-fixture/build.gradle b/test/fixtures/hdfs-fixture/build.gradle
index 72a6428c4912b..7798c10b6efaa 100644
--- a/test/fixtures/hdfs-fixture/build.gradle
+++ b/test/fixtures/hdfs-fixture/build.gradle
@@ -17,11 +17,13 @@ def patched = Attribute.of('patched', Boolean)
def hdfsVersionAttr = Attribute.of('hdfs.major.version', Integer)
configurations {
hdfs2 {
+ transitive = false
attributes {
attribute(patched, true)
}
}
hdfs3 {
+ transitive = false
attributes {
attribute(patched, true)
}
@@ -62,8 +64,22 @@ dependencies {
matchingArtifacts = ["hadoop-common"]
}
}
+ implementation("org.slf4j:slf4j-api:${versions.slf4j}")
- compileOnly("org.apache.hadoop:hadoop-minicluster:2.8.5")
+ compileOnly("commons-io:commons-io:2.16.1")
+ compileOnly("org.apache.hadoop:hadoop-common:2.8.5")
+ compileOnly("org.apache.hadoop:hadoop-annotations:2.8.5")
+ compileOnly("org.apache.hadoop:hadoop-hdfs:2.8.5")
+ compileOnly("org.apache.hadoop:hadoop-hdfs:2.8.5:tests")
+ compileOnly("org.apache.hadoop:hadoop-hdfs-client:2.8.5") {
+ exclude group: "org.apache.hadoop", module: "hadoop-common"
+ }
+ compileOnly("org.apache.hadoop:hadoop-hdfs:2.8.5:tests") {
+ transitive = false
+ }
+ compileOnly("org.apache.hadoop:hadoop-minicluster:2.8.5") {
+ transitive = false
+ }
api("com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}") {
transitive = false
}
@@ -104,30 +120,58 @@ dependencies {
[group: "org.slf4j", module: "slf4j-api"],
]
- hdfs2("org.apache.hadoop:hadoop-minicluster:2.8.5") {
- commonExcludes.each { exclude it }
- exclude group: "org.apache.commons", module: "commons-math3"
- exclude group: "xmlenc", module: "xmlenc"
- exclude group: "net.java.dev.jets3t", module: "jets3t"
- exclude group: "org.apache.directory.server", module: "apacheds-i18n"
- exclude group: "xerces", module: "xercesImpl"
- }
-
- hdfs3("org.apache.hadoop:hadoop-minicluster:3.3.1") {
- commonExcludes.each { exclude it }
- exclude group: "dnsjava", module: "dnsjava"
- exclude group: "com.google.inject.extensions", module: "guice-servlet"
- exclude group: "com.google.inject", module: "guice"
- exclude group: "com.microsoft.sqlserver", module: "mssql-jdbc"
- exclude group: "com.sun.jersey.contribs", module: "jersey-guice"
- exclude group: "com.zaxxer", module: "HikariCP-java7"
- exclude group: "com.sun.jersey", module: "jersey-server"
- exclude group: "org.bouncycastle", module: "bcpkix-jdk15on"
- exclude group: "org.bouncycastle", module: "bcprov-jdk15on"
- exclude group: "org.ehcache", module: "ehcache"
- exclude group: "org.apache.geronimo.specs", module: "geronimo-jcache_1.0_spec"
- exclude group: "org.xerial.snappy", module: "snappy-java"
- }
+ // port this to a component metadata rule for minicluster 2 and 3
+ hdfs2("commons-io:commons-io:2.16.1")
+ hdfs2("com.google.protobuf:protobuf-java:3.25.5")
+ hdfs2("org.apache.htrace:htrace-core4:4.0.1-incubating")
+ hdfs2("org.mortbay.jetty:jetty:6.1.26")
+ hdfs2("org.mortbay.jetty:jetty-util:6.1.26")
+ hdfs2("org.mortbay.jetty:jetty-sslengine:6.1.26")
+ hdfs2("org.mortbay.jetty:servlet-api:2.5-20081211")
+ hdfs2("org.codehaus.jackson:jackson-mapper-asl:1.9.13")
+ hdfs2("org.codehaus.jackson:jackson-core-asl:1.9.13")
+ hdfs2("com.sun.jersey:jersey-servlet:1.19")
+ hdfs2("com.sun.jersey:jersey-server:1.19")
+ hdfs2("com.sun.jersey:jersey-core:1.19")
+ hdfs2("commons-collections:commons-collections:3.2.1")
+ hdfs2("commons-configuration:commons-configuration:1.6")
+ hdfs2("commons-lang:commons-lang:2.6")
+ hdfs2("org.apache.hadoop:hadoop-auth:2.8.5")
+ hdfs2("org.apache.hadoop:hadoop-common:2.8.5")
+ hdfs2("org.apache.hadoop:hadoop-common:2.8.5:tests")
+ hdfs2("org.apache.hadoop:hadoop-hdfs:2.8.5")
+ hdfs2("org.apache.hadoop:hadoop-hdfs:2.8.5:tests")
+ hdfs2("org.apache.hadoop:hadoop-annotations:2.8.5")
+ hdfs2("org.apache.hadoop:hadoop-hdfs-client:2.8.5")
+ hdfs2("org.apache.hadoop:hadoop-minicluster:2.8.5")
+
+ hdfs3("org.apache.hadoop:hadoop-minicluster:3.3.1")
+ hdfs3("org.apache.hadoop:hadoop-common:3.3.1")
+ hdfs3("org.apache.hadoop:hadoop-common:3.3.1:tests")
+ hdfs3("org.apache.hadoop:hadoop-hdfs:3.3.1")
+ hdfs3("org.apache.hadoop:hadoop-hdfs:3.3.1:tests")
+ hdfs3("org.apache.hadoop:hadoop-hdfs-client:3.3.1")
+ hdfs3("org.apache.hadoop:hadoop-yarn-api:3.3.1")
+ hdfs3("org.apache.hadoop:hadoop-auth:3.3.1")
+ hdfs3("org.apache.hadoop.thirdparty:hadoop-shaded-guava:1.1.1")
+ hdfs3("org.apache.hadoop.thirdparty:hadoop-shaded-protobuf_3_7:1.1.1")
+ hdfs3("org.apache.htrace:htrace-core4:4.1.0-incubating")
+ hdfs3("commons-collections:commons-collections:3.2.2")
+ hdfs3("commons-io:commons-io:2.8.0")
+ hdfs3("org.apache.commons:commons-configuration2:2.1.1")
+ hdfs3("org.apache.commons:commons-lang3:3.7")
+ hdfs3("org.apache.commons:commons-text:1.4")
+ hdfs3("com.fasterxml.woodstox:woodstox-core:6.7.0")
+ hdfs3("org.codehaus.woodstox:stax2-api:4.2.2")
+ hdfs3("org.eclipse.jetty:jetty-servlet:9.4.40.v20210413")
+ hdfs3("org.eclipse.jetty:jetty-server:9.4.40.v20210413")
+ hdfs3("org.eclipse.jetty:jetty-util:9.4.40.v20210413")
+ hdfs3("org.eclipse.jetty:jetty-webapp:9.4.40.v20210413")
+ hdfs3("org.eclipse.jetty:jetty-http:9.4.40.v20210413")
+ hdfs3("org.eclipse.jetty:jetty-security:9.4.40.v20210413")
+ hdfs3("org.eclipse.jetty:jetty-io:9.4.40.v20210413")
+ hdfs3("javax.servlet:javax.servlet-api:3.1.0")
+ hdfs3("com.sun.jersey:jersey-servlet:1.19")
}
tasks.named("shadowJar").configure {
diff --git a/test/fixtures/url-fixture/build.gradle b/test/fixtures/url-fixture/build.gradle
index 4813aa11f233c..1ab1443c94f9b 100644
--- a/test/fixtures/url-fixture/build.gradle
+++ b/test/fixtures/url-fixture/build.gradle
@@ -10,6 +10,6 @@ apply plugin: 'elasticsearch.java'
description = 'Fixture for URL external service'
dependencies {
- api project(':server')
- api project(':test:framework')
+ implementation project(':server')
+ implementation project(':test:framework')
}