Index: src/java/org/apache/ivy/plugins/conflict/LatestCompatibleConflictManager.java =================================================================== --- src/java/org/apache/ivy/plugins/conflict/LatestCompatibleConflictManager.java (revision 961511) +++ src/java/org/apache/ivy/plugins/conflict/LatestCompatibleConflictManager.java (working copy) @@ -188,7 +188,7 @@ Stack callerStack = new Stack(); callerStack.push(evicted); final Collection toBlacklist = blackListIncompatibleCaller( - settings.getVersionMatcher(), parent, selected, evicted, callerStack); + settings.getVersionMatcher(), parent, selected, evicted, callerStack, false); if (toBlacklist != null) { final StringBuffer blacklisted = new StringBuffer(); for (Iterator iterator = toBlacklist.iterator(); iterator.hasNext();) { @@ -215,6 +215,34 @@ } } + private boolean handleIncompatibleCaller(Stack callerStack, IvyNode node, IvyNode callerNode, + IvyNode conflictParent, IvyNode selectedNode, IvyNode evictedNode, + Collection blacklisted, VersionMatcher versionMatcher, boolean dynamicCaller) { + if (callerStack.subList(0, callerStack.size() - 1).contains(node)) { + // circular dependency found and handled: the current top of the stack (node) + // was already contained in the rest of the stack, the circle is closed, nothing + // else to do + return true; + } else { + if (callerNode == null) { + // we have reached the root without finding a way to change the blacklist a + // caller in a particular path, this is a strict conflict + return false; + } + callerStack.push(callerNode); + Collection sub = blackListIncompatibleCaller(versionMatcher, conflictParent, + selectedNode, evictedNode, callerStack, dynamicCaller); + callerStack.pop(); + if (sub == null) { + // propagate the fact that a path with unblacklistable caller has been found + return false; + } else { + blacklisted.addAll(sub); + return true; + } + } + } + /** * Tries to blacklist exactly one version for all callers paths. * @@ -234,7 +262,8 @@ private Collection/**/ blackListIncompatibleCaller( VersionMatcher versionMatcher, IvyNode conflictParent, IvyNode selectedNode, IvyNode evictedNode, - Stack/**/ callerStack) { + Stack/**/ callerStack, + boolean dynamicCaller) { Collection/**/ blacklisted = new ArrayList/**/(); IvyNode node = (IvyNode) callerStack.peek(); String rootModuleConf = conflictParent.getData().getReport().getConfiguration(); @@ -245,33 +274,19 @@ continue; } if (versionMatcher.isDynamic(callers[i].getAskedDependencyId(node.getData()))) { - blacklisted.add(new IvyNodeBlacklist( - conflictParent, selectedNode, evictedNode, node, rootModuleConf)); - } else { - if (callerStack.subList(0, callerStack.size() - 1).contains(node)) { - // circular dependency found and handled: the current top of the stack (node) - // was already contained in the rest of the stack, the circle is closed, nothing - // else to do - } else { - if (callerNode == null) { - // we have reached the root without finding a way to change the blacklist a - // caller in a particular path, this is a strict conflict - return null; - } - callerStack.push(callerNode); - Collection sub = blackListIncompatibleCaller( - versionMatcher, conflictParent, selectedNode, evictedNode, callerStack); - callerStack.pop(); - if (sub == null) { - // propagate the fact that a path with unblacklistable caller has been found - return null; - } else { - blacklisted.addAll(sub); - } + blacklisted.add(new IvyNodeBlacklist(conflictParent, selectedNode, evictedNode, + node, rootModuleConf)); + if (node.isEvicted(rootModuleConf) + && !handleIncompatibleCaller(callerStack, node, callerNode, conflictParent, + selectedNode, evictedNode, blacklisted, versionMatcher, true)) { + return null; } + } else if(!handleIncompatibleCaller(callerStack, node, callerNode, conflictParent, + selectedNode, evictedNode, blacklisted, versionMatcher, false)) { + return null; } } - if (blacklisted.isEmpty() + if (!dynamicCaller && blacklisted.isEmpty() && !callerStack.subList(0, callerStack.size() - 1).contains(node)) { return null; }