diff --git a/BUILDING.md b/BUILDING.md index d0f01554c93ab..cba100d390a3c 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -97,6 +97,70 @@ will have the `origin` attribute been set to `Generated by Gradle`. >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..7aeb2266ba113 --- /dev/null +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/dependencies/rules/ComponentMetadataRulesPlugin.java @@ -0,0 +1,502 @@ +/* + * 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("com.amazonaws:aws-java-sdk-ec2", 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:

+ * + * + */ +@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 570bccc394a52..65afc5992537c 100644 --- a/distribution/packages/build.gradle +++ b/distribution/packages/build.gradle @@ -42,6 +42,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 80ccf245487c5..785e06c9b4962 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -2080,11 +2080,6 @@ - - - - - @@ -4641,36 +4636,11 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/settings.gradle b/settings.gradle index bca83a9bfa944..e0dff39ccd3bf 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.0.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 ebd9bca7a6e55..babfc0542996d 100644 --- a/test/fixtures/hdfs-fixture/build.gradle +++ b/test/fixtures/hdfs-fixture/build.gradle @@ -18,11 +18,13 @@ def hdfsVersionAttr = Attribute.of('hdfs.major.version', Integer) configurations { hdfs2 { + transitive = false attributes { attribute(patched, true) } } hdfs3 { + transitive = false attributes { attribute(patched, true) } @@ -63,8 +65,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 } @@ -105,30 +121,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') }