Bug 514149 - [resolver] issues resolving with substitutable exports
Fix by checking if removing a candidate will substitute the only
provider which satisfies a dependency.
Change-Id: Iae05477ce7ddef9ee036ac8e7cc5000c816d042f
Signed-off-by: Thomas Watson <tjwatson@us.ibm.com>
diff --git a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java
index 29b2b7b..44c96a3 100755
--- a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java
+++ b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/Candidates.java
@@ -1203,7 +1203,54 @@
public boolean canRemoveCandidate(Requirement req)
{
CandidateSelector candidates = m_candidateMap.get(req);
- return ((candidates != null) && (candidates.getRemainingCandidateCount() > 1 || Util.isOptional(req)));
+ if (candidates != null)
+ {
+ Capability current = candidates.getCurrentCandidate();
+ if (current != null)
+ {
+ // IMPLEMENTATION NOTE:
+ // Here we check for a req that is used for a substitutable export.
+ // If we find a substitutable req then an extra check is done to see
+ // if the substitutable capability is currently depended on as the
+ // only provider of some other requirement. If it is then we do not
+ // allow the candidate to be removed.
+ // This is done because of the way we attempt to reduce permutations
+ // checked by permuting all used requirements that conflict with a
+ // directly imported/required capability in one go.
+ // If we allowed these types of substitutable requirements to move
+ // to the next capability then the permutation would be thrown out
+ // because it would cause some other resource to not resolve.
+ // That in turn would throw out the complete permutation along with
+ // any follow on permutations that could have resulted.
+ // See ResolverImpl::checkPackageSpaceConsistency
+
+ // Check if the current candidate is substitutable by the req;
+ // This check is necessary here because
+ if (req.equals(m_subtitutableMap.get(current)))
+ {
+ // this is a substitute req,
+ // make sure there is not an existing dependency that would fail if we substitute
+ Set<Requirement> dependents = m_dependentMap.get(current);
+ if (dependents != null)
+ {
+ for (Requirement dependent : dependents)
+ {
+ CandidateSelector dependentSelector = m_candidateMap.get(dependent);
+ // If the dependent selector only has one capability left then we know it is
+ // using the current candidate and has no options left
+ if (dependentSelector != null && dependentSelector.getRemainingCandidateCount() <= 1)
+ {
+ // return false since we do not want to allow this requirement
+ // to substitute the capability
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return candidates.getRemainingCandidateCount() > 1 || Util.isOptional(req);
+ }
+ return false;
}
static class DynamicImportFailed extends ResolutionError {
diff --git a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ResolverImpl.java b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ResolverImpl.java
index 60beca8..dd7b7c5 100755
--- a/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ResolverImpl.java
+++ b/bundles/org.eclipse.osgi/felix/src/org/apache/felix/resolver/ResolverImpl.java
@@ -1288,8 +1288,6 @@
Packages pkgs = resourcePkgMap.get(resource);
ResolutionError rethrow = null;
- Candidates permutation = null;
- Set<Requirement> mutated = null;
// Check for conflicting imports from fragments.
// TODO: Is this only needed for imports or are generic and bundle requirements also needed?
@@ -1332,6 +1330,16 @@
}
}
+ // IMPLEMENTATION NOTE:
+ // Below we track the mutated reqs that have been permuted
+ // in a single candidates permutation. This permutation may contain a
+ // delta of several reqs which conflict with a directly imported/required candidates.
+ // When several reqs are permuted at the same time this reduces the number of solutions tried.
+ // See the method Candidates::canRemoveCandidate for a case where substitutions must be checked
+ // because of this code that may permute multiple reqs in on candidates permutation.
+ Set<Requirement> mutated = null;
+ Candidates permutation = null;
+
// Check if there are any uses conflicts with exported packages.
for (Entry<String, Blame> entry : pkgs.m_exportedPkgs.fast())
{