Using an Ivy ChainResolver constructed from ivysettings.xml fails with StackOverflowError


(Richard Steele) #1

I’m trying to load and use our company’s standard ivysettings file for use with Gradle and seem to be running into problems with a chain resolver.

I’m using a trivial build:

import org.apache.ivy.core.settings.IvySettings
  apply plugin: 'java'
  IvySettings ivySettings = new IvySettings();
ivySettings.load(new File("ivysettings.xml"));
  repositories {
    add(ivySettings.getResolver('public'))
  // works!
    //add(ivySettings.getDefaultResolver()) // fails!
}
  dependencies {
    testCompile 'junit:junit:4.11'
}

and a trivial ivysettings.xml:

<ivysettings>
      <property name="ivy.shared.default.root"
           value="${ivy.default.ivy.user.dir}/shared" override="false" />
    <property name="ivy.shared.default.ivy.pattern"
    value="[organisation]/[module]/[revision]/[type]s/[artifact].[ext]" override="false" />
    <property name="ivy.shared.default.artifact.pattern" value="[organisation]/[module]/[revision]/[type]s/[artifact].[ext]" override="false" />
      <settings defaultResolver="default" />
      <resolvers>
          <filesystem name="shared">
            <ivy pattern="${ivy.shared.default.root}/${ivy.shared.default.ivy.pattern}" />
            <artifact pattern="${ivy.shared.default.root}/${ivy.shared.default.artifact.pattern}" />
        </filesystem>
          <ibiblio name="public" m2compatible="true" />
          <chain name="default">
            <resolver ref="shared" />
            <resolver ref="public" />
        </chain>
      </resolvers>
  </ivysettings>

When I use just the shared or the public repository things work as expected. However, when I use the chain repository, I get a failed build

Building > :compileTestJava > Resolving dependencies ':testCompile’org.gradle.listener.ListenerNotificationException: Failed to notify output event listener.

The full output and stack trace is available at https://gist.github.com/anonymous/5020860.

I’m running Windows 7 (32-bit), Java 7, Gradle 1.4.

This is a blocker for my company’s adoption of Gradle.

Thanks, Rich


(Richard Steele) #2

I found I can simplify the settings file even more:

<ivysettings>
      <resolvers>
          <chain name="default">
            <ibiblio m2compatible="true" />
        </chain>
      </resolvers>
  </ivysettings>

This is with loading the resolver named “default”:

import org.apache.ivy.core.settings.IvySettings
  apply plugin: 'java'
  IvySettings ivySettings = new IvySettings();
ivySettings.load(new File("ivysettings.xml"));
  repositories {
    add(ivySettings.getResolver('default'))
}
  dependencies {
    testCompile 'junit:junit:4.11'
}

I also ran with --info and --stacktrace and see that it’s ultimately a stack overflow error that’s causing the build failure; the detailed output is at https://gist.github.com/anonymous/5022193.

Thanks, Rich


#3

Thanks for the bug report: I’ve raised this as GRADLE-2692.

Over time, the dependency management in Gradle is moving further away from the initial implementation that was heavily based on Ivy. There have been many reasons for this, and the move has given us the control and flexibility we need to implement some very powerful features. The ultimate destination of our move away from Ivy will be to deprecate support for native Ivy DependencyResolvers altogether. This won’t happen soon, and it won’t happen until we provide an alternative mechanism to solve all of the primary use cases that currently require a custom DependencyResolver.

In light of this, it’s unlikely we’ll spend a lot of time fixing this problem. Patches will solid test coverage will be very welcome however. I guess your options going forward are:

  1. Supply a patch that fixes this issue and have it adopted into Gradle proper. 2. Instead of adding a single ChainResolver as a repository in Gradle, add each of the individual resolvers instead. Gradle will effectively chain each of these resolvers in a similar fashion to Ivy.

I would recommend the second option, since it will make your actual repository structure more transparent to Gradle, allowing for caching optimisations and other goodness that might not be available with a ChainResolver. The risk is that the exact dependencies resolved may not match due to some subtle differences in the way the resolvers are chained; we’d be interested in hearing if this was the case.


(Richard Steele) #4

Thanks for the response. I’ll have to reconsider whether we can reuse the existing ivy settings or, as you suggest, do our best to emulate it.

On the off chance I’d like to try and tackle the problem, any suggestions other than follow the stack trace? My guess is we’re in some kind of event-based recursive loop, but I haven’t looked all that hard–and Gradle (not to mention Groovy) is very new to me.


#5

Your best bet would be to run the build with ‘–debug’, and see if you can detect the log statements that repeat. This would help to track down where the loop is occurring. I wouldn’t be surprised if the ‘ListenerNotificationException’ is a bit of a red herring.

My best guess would be that something is going wrong when parsing the downloaded POM that triggers the same POM to be downloaded and parsed, recursively. You might want to check out the code for GradlePomModuleDescriptorParser, which is presented with a LoopBackDependencyResolver for resolving parent POM files. (This is just a guess, though).

If you use IntelliJ IDEA (or are happy to try), you will be able to debug Gradle itself pretty easily by following the instructions in the README. Running ‘./gradlew idea’ will create a usable IDEA project with a nice “Gradle” run configuration: you can then set the working directory and program arguments to debug your failing project right inside the IDE. Set a breakpoint in ‘ChainResolver.getDependency()’ and work your way from there.