Over a million developers have joined DZone.

Gradle: Overruling Third-Party Dependencies

DZone's Guide to

Gradle: Overruling Third-Party Dependencies

· DevOps Zone ·
Free Resource

DevOps involves integrating development, testing, deployment and release cycles into a collaborative process. Learn more about the 4 steps to an effective DevSecOps infrastructure.

We all hate dependency hell, and on any Java project of any size, you'll hit it eventually. My project, for Aviso, is a large Clojure code base, with tons of dependencies on third-party libraries, many of which are Java libraries with their own third-party dependencies. Despite using Gradle as our build tool, we don't get a free pass, we sometimes end up with conflicts.

It often plays out like this: module A as a dependency on library L, which has a transitive dependency on library Q. That's OK, module A has a consistent class path when it builds.

Meanwhile, module B has a dependency on library M, which has a transitive dependency on library Q ... but a different version. That's OK, module B also has a consistent class path when it builds.

However, inside IntelliJ, you see both version of library Q in the "External Libraries" folder of the Project explorer. That's unfortunate and can cause confusion when navigating your code.

Worse yet, in the final application, combining modules A and B, you will be executing one module with a different version of library Q than your tests. That alone makes me a touch nervous.

Fortunately, Gradle provides a quite reasonable way of dealing with this. The hard way would be to just turn off all transitive dependencies. But I consider that throwing out the baby with the bathwater.

Instead, we can selectively override transitive dependency, consistently across all modules. And we can do this in a single place, in our top-levelbuild.gradle:

def versionOverrides = [
    "asm:asm": "3.3.1",
    "bultitude:bultitude": "0.1.7",
    "commons-codec:commons-codec": "1.7",
    "commons-io:commons-io": "2.4",
    "io.aviso:pretty": "0.1.9-SNAPSHOT",
    "joda-time:joda-time": "2.1",
    "org.clojure:core.incubator": "0.1.1",
    "org.clojure:tools.macro": "0.1.1",
    "org.clojure:tools.namespace": "0.1.1",
    "org.codehaus.groovy:groovy-all": "1.8.6",
    "org.jsoup:jsoup": "1.7.1",
    "org.yaml:snakeyaml": "1.12",
    "slingshot:slingshot": "0.10.3"

subprojects {

    configurations.all {

        resolutionStrategy.eachDependency { DependencyResolveDetails details ->

            def overrideVersion = versionOverrides[details.requested.group + ":" + details.requested.name]

            if (overrideVersion != null && details.requested.version != overrideVersion) {
                logger.info "Overriding dependency ${details.requested.group}:${details.requested.name} version ${details.requested.version} --> $overrideVersion"
                details.useVersion overrideVersion

This one small change affects every child project; we have a single place to maintain and resolve these version conflicts and don't have to chase down which module (among the 37 currently in our overall project) is the culprit for introducing a conflict. When we see a conflict, we add a new mapping to versionOverrides and we are done.

This is a huge example of how powerful Gradle's Groovy DSL is; because the build script is also executable code, there's room to put logic in place that simply can't be defined declaratively.

Our change hooks into the dependency resolution logic associated with each Gradle configuration (a configuration is essentially a way of declaring the class path for compiling, testing, or executing Java code).

Gradle kindly exposes a step inside the overall process of analyzing the dependencies; this code hooks into this step. It sees the requesteddependency, and if it's in the override map, forces the version number to a specific value. In fact, this mechanism is powerful enough to replacedependencies, but that's beyond our immediate needs.

This is one of the reasons I use Gradle in preference to Maven: Gradle has the tools to cleanly and easily address my specific problems and particular edge-cases.

Read the 4-part DevOps testing eBook to learn how to detect problems earlier in your DevOps testing processes.

java ,high-perf ,gradle

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}