Adil Hussain
Adil Hussain

Reputation: 32171

Failing to understand Gradle's default dependency resolution strategy

I have built and uploaded a Java library to a private Artifactory space. Let's say the library has a Gradle identifier of com.mycompany:MyLibrary:1.0.0, has been uploaded to https://artifactory.mycompany.com/artifactory/libs-release and does not exist in JCenter. This library has a dependency on another library which is not uploaded to https://artifactory.mycompany.com/artifactory/libs-release but is openly available from JCenter. Let's say this second library has a Gradle identifier of com.google.code.findbugs:jsr305:3.0.0.

As a consumer of the Java library in a Java application, my list of dependencies is as follows:

dependencies {
    implementation 'com.mycompany:MyLibrary:1.0.0'
}

Notably, I do not need to explicitly declare the com.google.code.findbugs:jsr305:3.0.0 dependency since it will be resolved transitively.

Further, for reasons of security, I want to order my private Artifactory repository above JCenter, as follows:

repositories {
    maven {
        url 'https://artifactory.mycompany.com/artifactory/libs-release'
    }
    jcenter()
}

However, when I try to build my Java application, I am met with the following error:

Execution failed for task ':bootJar'.
> Could not resolve all files for configuration ':runtimeClasspath'.
   > Could not find jsr305-3.0.0.jar (com.google.code.findbugs:jsr305:3.0.0).
     Searched in the following locations:
         https://artifactory.mycompany.com/artifactory/libs-release/com/google/code/findbugs/jsr305/3.0.0/jsr305-3.0.0.jar

It appears from this error that the jsr305-3.0.0 artifact was only searched for in my private Artifactory repository and not in JCenter. I would have expected Gradle to search for the jsr305-3.0.0 artifact in JCenter if it could not be found in my private Artifactory repository.

If I reorder the repositories so that JCenter is declared first, as follows...

repositories {
    jcenter()
    maven {
        url 'https://artifactory.mycompany.com/artifactory/libs-release'
    }
}

... then the build completes successfully.

Is what I am experiencing the expected behaviour for how Gradle resolves dependencies or is this a bug? If it's the expected behaviour, is there anything I can do to change the dependency resolution strategy so that I can declare my private Artifactory repository above JCenter?

Upvotes: 0

Views: 134

Answers (2)

Adil Hussain
Adil Hussain

Reputation: 32171

So it turns that at some point in the past my private Artifactory instance created a partial cache of JCenter. I'm not sure how this cache came to be but it seems to include all of the artifacts which my library depends on. Further, the cache only includes the pom and sources jar of each artifact and not its binary jar, as follows:

enter image description here

So what's happening when a consumer of my library orders his/her repositories as follows...

repositories {
    maven {
        url 'https://artifactory.mycompany.com/artifactory/libs-release'
    }
    jcenter()
}

... is that Gradle is finding the pom file for com.google.code.findbugs:jsr305:3.0.0 in https://artifactory.mycompany.com/artifactory/libs-release and it's expecting to find the binary jar there also. When it does not find the binary jar there, it fails the build. Note in particular that Gradle will only proceed to search for an artifact in the next repository in the list (JCenter in this example) if it does not find the pom file for it in any of the previous repositories that it has inspected.

The solution for me is to either remove the JCenter cache from my private Artifactory instance or to make the cache complete so that it includes the binary jar file for each artifact that it holds.

Upvotes: 0

PrasadU
PrasadU

Reputation: 2438

The metadata in JCenter is better than the metadata in your custom private Artifactory space.

Gradle checks all repositories to find the best metadata and uses that.

Check out the following quotes from the Declaring Repositories page in the Gradle documentation.

Firstly, from the Declaring Multiple Repositories section:

The order of declaration determines how Gradle will check for dependencies at runtime. If Gradle finds a module descriptor in a particular repository, it will attempt to download all of the artifacts for that module from the same repository. You can learn more about the inner workings of dependency downloads.

Secondly, from the Supported Repository Types section:

As Gradle prefers to use modules whose descriptor has been created from real meta-data rather than being generated, flat directory repositories cannot be used to override artifacts with real meta-data from other repositories declared in the build.

For example, if Gradle finds only jmxri-1.2.1.jar in a flat directory repository, but jmxri-1.2.1.pom in another repository that supports meta-data, it will use the second repository to provide the module.

Upvotes: 0

Related Questions