Hi Colin, thank you for the detailed report. Based on your proposed solution, I've pushed a slightly different fix to our git repo. Could you give it a try?
thanks, Maarten Op maandag 18 mei 2026 om 18:38:37 CEST schreef Colin Chambers <[email protected]>: Hi This (long) email reports a defect, plus the source code explanation for the defect, plus a proposed source code change (which I have tested) to correct the defect. This email is formatted in a fixed-width font and should be read in a wide window (about 100 characters). FAILURE REPORT: I believe the "install" Ant task of Apache Ivy 2.5.3 has a defect whereby a request to install a module from the Maven repository fails when the module has a POM parent module in the Maven repository. The following request is an example demonstrating the failure: <ivy:install organisation="ch.qos.logback" module="logback-classic" revision="1.5.19" from="maven-central" to="third-party-local" transitive="true" overwrite="true"/> This produces the following failure: configure: [ivy:configure] :: Apache Ivy 2.5.3 - 20241223125031 :: https://ant.apache.org/ivy/ :: [ivy:configure] :: loading settings :: file = /home/xxx/ivysettings.xml install-logback-classic: [echo] Install logback-classic 1.5.19 [ivy:install] :: installing ch.qos.logback#logback-classic;1.5.19 :: [ivy:install] :: resolving dependencies :: [ivy:install] :: downloading artifacts to cache :: [ivy:install] :: installing in third-party-local :: [ivy:install] :: install resolution report :: [ivy:install] :: resolution report :: resolve 0ms :: artifacts dl 0ms --------------------------------------------------------------------- | | modules || artifacts | | conf | number| search|dwnlded|evicted|| number|dwnlded| --------------------------------------------------------------------- | default | 1 | 0 | 0 | 0 || 0 | 0 | --------------------------------------------------------------------- [ivy:install] [ivy:install] :: problems summary :: [ivy:install] :::: WARNINGS [ivy:install] io problem while parsing ivy file: https://repo1.maven.org/maven2/ch/ qos/logback/logback-classic/1.5.19/logback-classic-1.5.19.pom (java.io.IOException: Impossible to load parent for file:/home/xxx/ .ivy2/cache/ch.qos.logback/logback-classic/ivy-1.5.19.xml.original. Parent=ch.qos.logback#logback-parent;1.5.19) [ivy:install] module not found: ch.qos.logback#logback-classic;1.5.19 [ivy:install] ==== maven-central: tried [ivy:install] https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.5.19 /logback-classic-1.5.19.pom [ivy:install] :::::::::::::::::::::::::::::::::::::::::::::: [ivy:install] :: UNRESOLVED DEPENDENCIES :: [ivy:install] :::::::::::::::::::::::::::::::::::::::::::::: [ivy:install] :: ch.qos.logback#logback-classic;1.5.19: not found [ivy:install] :::::::::::::::::::::::::::::::::::::::::::::: [ivy:install] [ivy:install] :: USE VERBOSE OR DEBUG MESSAGE LEVEL FOR MORE DETAILS BUILD FAILED The "maven-central" resolver is defined as: <ibiblio name="maven-central" usepoms="true" useMavenMetadata="true" m2compatible="true"/> The "ch.qos.logback#logback-parent;1.5.19" module is present in the repository. EXPLANATION FOR FAILURE: I believe the reason for failure is that the "default" resolver is used to find the parent module, instead of the specified "from" resolver being used. Source code referenced below was obtained by git clone of latest sources. A request to parse the parent module is made in the 'parseDescriptor()' method of org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser:line 188: final ResolvedModuleRevision parentModule = parseOtherPom(ivySettings, parentModRevID, true); if (parentModule == null) { throw new IOException("Impossible to load parent for " + res.getName() + ". Parent=" + parentModRevID); } The resolver for the parent module is requested in the 'parseOtherPom()' method of: org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser:line 453: DependencyResolver resolver = ivySettings.getResolver(parentModRevID); if (resolver == null) { // TODO: Throw exception here? return null; } dd = toSystem(dd, ivySettings.getContextNamespace()); return resolver.getDependency(dd, data); This effectively invokes: org.apache.ivy.core.settings.IvySettings:line 922: public synchronized DependencyResolver getResolver(ModuleRevisionId mrid) { DependencyResolver r = getDictatorResolver(); if (r != null) { return r; } String resolverName = getResolverName(mrid); return getResolver(resolverName); } The 'getDictatorResolver()' call returns "null", causing 'getResolverName(mrid)' to be called. This is: org.apache.ivy.core.settings.IvySettings:line 965: public synchronized String getResolverName(ModuleRevisionId mrid) { ModuleSettings ms = moduleSettings.getRule(mrid, new Filter<ModuleSettings>() { public boolean accept(ModuleSettings o) { return o.getResolverName() != null; } }); return ms == null ? defaultResolverName : ms.getResolverName(); } The 'moduleSettings.getRule(mrid,...)' returns "null", causing 'defaultResolverName' to be returned. This is the name of the default resolver. The calling method (see from "line 922" above) then does 'getResolver(resolverName)' to obtain a reference to the default resolver and returns this to 'parseOtherPom()' (see from "line 453" above). This calls 'resolver.getDependency(dd,data)' which invokes the 'getDependency()' method of org.apache.ivy.plugins.resolver.BasicResolver:line 183: public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data) throws ParseException { IvyContext context = IvyContext.pushNewCopyContext(); try { ... This obtains a "null" at line 256: ResolvedResource artifactRef = findFirstArtifactRef(nsMd, nsDd, data); checkInterrupted(); if (artifactRef == null) { throw new UnresolvedDependencyException("\t" + getName() + ": no ivy file nor artifact found for " + systemMrid, false); } causing it to throw the 'UnresolvedDependencyException', which is caught at line 328: } catch (UnresolvedDependencyException ex) { if (!ex.getMessage().isEmpty()) { if (ex.isError()) { Message.error(ex.getMessage()); } else { Message.verbose(ex.getMessage()); } } return data.getCurrentResolvedModuleRevision(); 'data.getCurrentResolvedModuleRevision()' returns "null" and this is returned to the calling method 'parseOtherPom()' (see from "line 453" above) which returns this result (null) to its calling method 'parseDescriptor()' (see 188" above and reproduced here): org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser:line 188: final ResolvedModuleRevision parentModule = parseOtherPom(ivySettings, parentModRevID, true); if (parentModule == null) { throw new IOException("Impossible to load parent for " + res.getName() + ". Parent=" + parentModRevID); } This causes the "Impossible to load parent" failure to be generated. PROPOSED CORRECTION FOR DEFECT: (1) Set a "settings" dictator resolver for the "install" task. In the 'install() method of the InstallEngine class, insert the lines commented as "+CC": org.apache.ivy.core.install.InstallEngine:line 79 // build module file declaring the dependency Message.info(":: installing " + mrid + " ::"); DependencyResolver oldDictator = resolveEngine.getDictatorResolver(); DependencyResolver oldSettingsDictator = settings.getDictatorResolver(); // +CC boolean log = settings.logNotConvertedExclusionRule(); try { settings.setLogNotConvertedExclusionRule(true); resolveEngine.setDictatorResolver(fromResolver); settings.setDictatorResolver(fromResolver ); // +CC org.apache.ivy.core.install.InstallEngine:line 194 } finally { // IVY-834: log the problems if there were any... Message.sumupProblems(); settings.setDictatorResolver(oldSettingsDictator ); // +CC resolveEngine.setDictatorResolver(oldDictator); settings.setLogNotConvertedExclusionRule(log); } (2) Make 'DictatorResolver' methods available in the 'InstallEngineSettings' interface. In the InstallEngineSettings interface insert the lines commented as "+CC": org.apache.ivy.core.install.InstallEngineSettings:line 28 public interface InstallEngineSettings extends ParserSettings { void setDictatorResolver(DependencyResolver resolver); // +CC DependencyResolver getDictatorResolver(); // +CC DependencyResolver getResolver(String from); (3) Make the 'getDictatorResolver()' method of the IvySettings class public. In the IvySettings class change the access modifier of 'getDictatorResolver()' from "private" to "public". org.apache.ivy.core.settings.IvySettings:line 912 BEFORE MODIFICATION private DependencyResolver getDictatorResolver() { if (dictatorResolver == null) { return null; } ... AFTER MODIFICATION public DependencyResolver getDictatorResolver() { // CC modified if (dictatorResolver == null) { return null; } (END OF DEFECT CORRECTION) Regards Colin Chambers --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
