Dependency resolution on pom file with jdk version activated profile

Hello,

I came across an issue with dependency resolution. When dependencies are declared in pom inside profile block which is activated based on jdk version.
To be concrete it is this pom: https://repository.apache.org/content/groups/public/org/apache/cxf/cxf-parent/3.3.4/cxf-parent-3.3.4.pom
even when i have compilation set to java 11 dependencies in the bottom like
org.apache.geronimo.specs
geronimo-jta_1.1_spec
are not added to runtime classpath.
Is there any easy way how to tell gradle to also activate these profiles according to java version?
I found out about this problem by accident because I am migrating project from maven to gradle and I was comparing all resolved dependencies and I noticed these are missing. And I would like to prevent similar issues in the future due to missing dependencies.
I am using Gradle 6.0.1

Thanks.

I’m also facing the same issue w/ Gradle 7.3 for cxf and some jersey-commons pom.xml

Current solution is to add these JDK based profile dependencies manually but this gets tedious whenever there are transitive dependencies.

Is there a way to this on Gradle 7.3+? I couldn’t find anything.

Unfortunately only a very narrow range of profile activations is supported still.

If this blog entry is still valid: Gradle's Support for Maven POM Profiles
then only profiles that are activated by default and profiles that are activated by absence of a system property.

I requested support for jdk activated profiles here: https://github.com/gradle/gradle/issues/19224
you might want to thumbs-up it to show that you also like to have it.

How I currently deal with it is this in the settings script:

dependencyResolutionManagement {
    components {
        // work-around for https://github.com/gradle/gradle/issues/19224
        withModule("org.apache.santuario:xmlsec") {
            check(id.version == "2.2.1") {
                "Please configure Java 9+ dependencies for $id"
            }
            listOf(
                "java9Compile" to "compile",
                "java9Runtime" to "runtime"
            ).forEach { (variant, base) ->
                addVariant(variant, base) {
                    attributes {
                        attribute(TARGET_JVM_VERSION_ATTRIBUTE, 9)
                    }
                    withDependencies {
                        add("jakarta.xml.bind:jakarta.xml.bind-api:2.3.2")
                    }
                }
            }
        }
    }
}

which works by adding new variants for Java 9+ that have the additional dependencies and the consuming configuration have the target jvm version attribute automatically (should also work with 6.0.1 I think) and thus pick the correct configuration. If the project is completely Java 9+ you can of course simplify it and just add the dependencies to the existing variants like:

dependencyResolutionManagement {
    components {
        // work-around for https://github.com/gradle/gradle/issues/19224
        withModule("org.apache.santuario:xmlsec") {
            check(id.version == "2.2.1") {
                "Please configure Java 9+ dependencies for $id"
            }
            listOf("compile", "runtime").forEach { variant ->
                withVariant(variant) {
                    withDependencies {
                        add("jakarta.xml.bind:jakarta.xml.bind-api:2.3.2")
                    }
                }
            }
        }
    }
}

Remember to add runtime dependencies only to “runtime” and compile dependencies to both, “compile” and “runtime”.

For something like CXF it could for example look something like

dependencyResolutionManagement {
    components {
        // work-around for https://github.com/gradle/gradle/issues/19224
        listOf(
            "org.apache.cxf:cxf-core" to "3.4.2",
            "org.apache.cxf:cxf-rt-bindings-soap" to "3.4.2",
            "org.apache.cxf:cxf-rt-bindings-xml" to "3.4.2",
            "org.apache.cxf:cxf-rt-databinding-jaxb" to "3.4.2",
            "org.apache.cxf:cxf-rt-frontend-jaxws" to "3.4.2",
            "org.apache.cxf:cxf-rt-frontend-simple" to "3.4.2",
            "org.apache.cxf:cxf-rt-transports-http" to "3.4.2",
            "org.apache.cxf:cxf-rt-ws-addr" to "3.4.2",
            "org.apache.cxf:cxf-rt-ws-policy" to "3.4.2",
            "org.apache.cxf:cxf-rt-wsdl" to "3.4.2"
        ).forEach { (module, version) ->
            withModule(module) {
                check(id.version == version) {
                    "Please configure Java 9+ dependencies for $id"
                }
                listOf("compile", "runtime").forEach { variant ->
                    withVariant(variant) {
                        withDependencies {
                            add("jakarta.annotation:jakarta.annotation-api:1.3.5")
                            add("jakarta.xml.ws:jakarta.xml.ws-api:2.3.3")
                            add("jakarta.jws:jakarta.jws-api:2.1.0")
                            add("jakarta.xml.soap:jakarta.xml.soap-api:1.4.2")
                            add("com.sun.activation:jakarta.activation:1.2.2")
                            add("org.jboss.spec.javax.rmi:jboss-rmi-api_1.0_spec:1.0.6.Final")
                            add("org.apache.geronimo.specs:geronimo-jta_1.1_spec:1.1.1")
                        }
                    }
                }
                withVariant("runtime") {
                    withDependencies {
                        add("com.sun.xml.messaging.saaj:saaj-impl:1.5.2")
                    }
                }
            }
        }
    }
}

Or actually as the dependencies are defined in the parent, assuming all in that group extend from it:

dependencyResolutionManagement {
    components {
        // work-around for https://github.com/gradle/gradle/issues/19224
        all {
            if (id.group != "org.apache.cxf") {
                return@all
            }
            check(id.version == "3.4.2") {
                "Please configure Java 9+ dependencies for $id"
            }
            listOf("compile", "runtime").forEach { variant ->
                withVariant(variant) {
                    withDependencies {
                        add("jakarta.annotation:jakarta.annotation-api:1.3.5")
                        add("jakarta.xml.ws:jakarta.xml.ws-api:2.3.3")
                        add("jakarta.jws:jakarta.jws-api:2.1.0")
                        add("jakarta.xml.soap:jakarta.xml.soap-api:1.4.2")
                        add("com.sun.activation:jakarta.activation:1.2.2")
                        add("org.jboss.spec.javax.rmi:jboss-rmi-api_1.0_spec:1.0.6.Final")
                        add("org.apache.geronimo.specs:geronimo-jta_1.1_spec:1.1.1")
                    }
                }
            }
            withVariant("runtime") {
                withDependencies {
                    add("com.sun.xml.messaging.saaj:saaj-impl:1.5.2")
                }
            }
        }
    }
}

All shown variations complain if a different version is resolved so that you can review the added dependencies. If you add a new dependency that has jdk enabled dependencies this will not be observed of course.

Thanks for the descriptive reply. As you pointed out, the issue w/ this is the fine tuning required whenever we add new dependencies which will be a hassle to deal with. I don’t like the idea of jdk based profiles but hopefully Gradle adds support for them since it seems like they’re not going anywhere anytime soon.

One addition, you can of course add something like this to check for jdk activated POMs.
The actual check would need improvement to properly parse the POM, to not catch other <jdk> tags (there are some), and only bail out if there are dependencies in the profile, and also considers the parent POMs (probably need to extract the information and get it through a detached configuration) and does not check ones that are already handled by a component metadata rule, …:

val checkForJdkActivatedProfilesWithDependencies by tasks.registering {
    doLast {
        dependencies
            .createArtifactResolutionQuery()
            .forComponents(configurations.filter { it.isCanBeResolved }.flatMap { configuration ->
                configuration.incoming.resolutionResult.allDependencies.mapNotNull {
                    (it as? ResolvedDependencyResult)?.selected?.id
                }
            }.distinct())
            .withArtifacts(MavenModule::class.java, MavenPomArtifact::class.java)
            .execute()
            .resolvedComponents
            .asSequence()
            .flatMap { it.getArtifacts(MavenPomArtifact::class.java).asSequence() }
            .filterIsInstance<ResolvedArtifactResult>()
            .forEach {
                check(!it.file.readText().contains("<jdk>")) {
                    "jdk activated Maven profile found in ${it.id}"
                }
            }
    }
}

tasks.check {
    dependsOn(checkForJdkActivatedProfilesWithDependencies)
}