Apply patch (FELIX-3514) to add back support for dynamic importing.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1356036 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
index b5d0e7e..35ec2b8 100644
--- a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
+++ b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
@@ -195,7 +195,7 @@
try
{
checkPackageSpaceConsistency(
- rc, false, allCandidates.getWrappedHost(target),
+ rc, allCandidates.getWrappedHost(target),
allCandidates, resourcePkgMap, new HashMap());
}
catch (ResolutionException ex)
@@ -280,11 +280,35 @@
return wireMap;
}
-/*
- TODO: RFC-112 - Modify dynamic import handling to be like obr-resolver prototype.
+ /**
+ * Resolves a dynamic requirement for the specified host resource using the specified
+ * {@link ResolveContext}. The dynamic requirement may contain wild cards in its filter
+ * for the package name. The matching candidates are used to resolve the requirement and
+ * the resolve context is not asked to find providers for the dynamic requirement.
+ * The host resource is expected to not be a fragment, to already be resolved and
+ * have an existing wiring provided by the resolve context.
+ * <p>
+ * This operation may resolve additional resources in order to resolve the dynamic
+ * requirement. The returned map will contain entries for each resource that got resolved
+ * in addition to the specified host resource. The wire list for the host resource
+ * will only contain a single wire which is for the dynamic requirement.
+ * @param rc the resolve context
+ * @param host the hosting resource
+ * @param dynamicReq the dynamic requirement
+ * @param matches a list of matching capabilities
+ * @param ondemandFragments collection of on demand fragments that will attach to any host that is a candidate
+ * @return The new resources and wires required to satisfy the specified
+ * dynamic requirement. The returned map is the property of the caller
+ * and can be modified by the caller.
+ * @throws ResolutionException
+ */
public Map<Resource, List<Wire>> resolve(
- ResolveContext rc, Resouce resource, String pkgName)
+ ResolveContext rc, Resource host, Requirement dynamicReq,
+ List<Capability> matches, Collection<Resource> ondemandFragments)
+ throws ResolutionException
{
+ Map<Resource, List<Wire>> wireMap = new HashMap<Resource, List<Wire>>();
+
// We can only create a dynamic import if the following
// conditions are met:
// 1. The specified resource is resolved.
@@ -292,19 +316,27 @@
// 3. The package in question is not accessible via require-bundle.
// 4. The package in question is not exported by the resource.
// 5. The package in question matches a dynamic import of the resource.
- // The following call checks all of these conditions and returns
- // the associated dynamic import and matching capabilities.
- Candidates allCandidates =
- getDynamicImportCandidates(rc, resource, pkgName);
- if (allCandidates != null)
+ if (!matches.isEmpty() && rc.getWirings().containsKey(host))
{
- Collection<Resource> ondemandFragments = (rc instanceof ResolveContextImpl)
- ? ((ResolveContextImpl) rc).getOndemandResources() : Collections.EMPTY_LIST;
+ // Make sure all matching candidates are packages.
+ for (Capability cap : matches)
+ {
+ if (!cap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+ {
+ throw new IllegalArgumentException(
+ "Matching candidate does not provide a package name.");
+ }
+ }
- Map<Resource, List<ResolverWire>> wireMap =
- new HashMap<Resource, List<ResolverWire>>();
- Map<Resource, Packages> resourcePkgMap =
- new HashMap<Resource, Packages>();
+ // Make copy of args in case we want to modify them.
+ ondemandFragments = new ArrayList<Resource>(ondemandFragments);
+
+ // Create all candidates pre-populated with the single candidate set
+ // for the resolving dynamic import of the host.
+ Candidates allCandidates = new Candidates();
+ allCandidates.populateDynamic(rc, host, dynamicReq, matches);
+
+ Map<Resource, Packages> resourcePkgMap = new HashMap<Resource, Packages>();
boolean retry;
do
@@ -328,7 +360,7 @@
// Record the initial candidate permutation.
m_usesPermutations.add(allCandidates);
- ResolveException rethrow = null;
+ ResolutionException rethrow = null;
do
{
@@ -347,20 +379,20 @@
// execute code, so we don't need to check for
// this case like we do for a normal resolve.
- calculatePackageSpaces(
- allCandidates.getWrappedHost(resource), allCandidates, resourcePkgMap,
- new HashMap(), new HashSet());
+ calculatePackageSpaces(rc,
+ allCandidates.getWrappedHost(host), allCandidates,
+ resourcePkgMap, new HashMap(), new HashSet());
//System.out.println("+++ PACKAGE SPACES START +++");
//dumpResourcePkgMap(resourcePkgMap);
//System.out.println("+++ PACKAGE SPACES END +++");
try
{
- checkPackageSpaceConsistency(
- false, allCandidates.getWrappedHost(resource),
+ checkDynamicPackageSpaceConsistency(rc,
+ allCandidates.getWrappedHost(host),
allCandidates, resourcePkgMap, new HashMap());
}
- catch (ResolveException ex)
+ catch (ResolutionException ex)
{
rethrow = ex;
}
@@ -374,14 +406,21 @@
// again; otherwise, rethrow the resolve exception.
if (rethrow != null)
{
- Resource faultyResource =
- getDeclaredResource(rethrow.getResource()));
- if (rethrow.getRequirement() instanceof WrappedRequirement)
+ Collection<Requirement> exReqs = rethrow.getUnresolvedRequirements();
+ Requirement faultyReq = ((exReqs == null) || (exReqs.isEmpty()))
+ ? null : exReqs.iterator().next();
+ Resource faultyResource = (faultyReq == null)
+ ? null : getDeclaredResource(faultyReq.getResource());
+ // If the faulty requirement is wrapped, then it may
+ // be from a fragment, so consider the fragment faulty
+ // instead of the host.
+ if (faultyReq instanceof WrappedRequirement)
{
faultyResource =
- ((WrappedRequirement) rethrow.getRequirement())
- .getOriginalRequirement().getResource());
+ ((WrappedRequirement) faultyReq)
+ .getDeclaredRequirement().getResource();
}
+ // Try to ignore the faulty resource if it is not mandatory.
if (ondemandFragments.remove(faultyResource))
{
retry = true;
@@ -395,9 +434,8 @@
// resolve, so populate the wire map.
else
{
- wireMap = populateDynamicWireMap(
- resource, pkgName, resourcePkgMap, wireMap, allCandidates);
- return wireMap;
+ wireMap = populateDynamicWireMap(rc,
+ host, dynamicReq, resourcePkgMap, wireMap, allCandidates);
}
}
finally
@@ -410,106 +448,9 @@
while (retry);
}
- return null;
+ return wireMap;
}
- private static Candidates getDynamicImportCandidates(
- ResolveContext rc, Resource resource, String pkgName)
- {
- // Unresolved resources cannot dynamically import, nor can the default
- // package be dynamically imported.
- if ((resource.getWiring() == null) || pkgName.length() == 0)
- {
- return null;
- }
-
- // If the resource doesn't have dynamic imports, then just return
- // immediately.
- List<Requirement> dynamics =
- Util.getDynamicRequirements(resource.getWiring().getRequirements(null));
- if ((dynamics == null) || dynamics.isEmpty())
- {
- return null;
- }
-
- // If the resource exports this package, then we cannot
- // attempt to dynamically import it.
- for (Capability cap : resource.getWiring().getCapabilities(null))
- {
- if (cap.getNamespace().equals(Resource.PACKAGE_NAMESPACE)
- && cap.getAttributes().get(Resource.PACKAGE_NAMESPACE).equals(pkgName))
- {
- return null;
- }
- }
-
- // If this resource already imports or requires this package, then
- // we cannot dynamically import it.
- if (((WiringImpl) resource.getWiring()).hasPackageSource(pkgName))
- {
- return null;
- }
-
- // Determine if any providers of the package exist.
- Map<String, Object> attrs = Collections.singletonMap(
- PackageNamespace.PACKAGE_NAMESPACE, (Object) pkgName);
- RequirementImpl req = new RequirementImpl(
- resource,
- PackageNamespace.PACKAGE_NAMESPACE,
- Collections.EMPTY_MAP,
- attrs);
- List<Capability> candidates = rc.findProviders(req, false);
-
- // Try to find a dynamic requirement that matches the capabilities.
- RequirementImpl dynReq = null;
- for (int dynIdx = 0;
- (candidates.size() > 0) && (dynReq == null) && (dynIdx < dynamics.size());
- dynIdx++)
- {
- for (Iterator<Capability> itCand = candidates.iterator();
- (dynReq == null) && itCand.hasNext(); )
- {
- Capability cap = itCand.next();
- if (CapabilitySet.matches(
- (CapabilityImpl) cap,
- ((RequirementImpl) dynamics.get(dynIdx)).getFilter()))
- {
- dynReq = (RequirementImpl) dynamics.get(dynIdx);
- }
- }
- }
-
- // If we found a matching dynamic requirement, then filter out
- // any candidates that do not match it.
- if (dynReq != null)
- {
- for (Iterator<Capability> itCand = candidates.iterator();
- itCand.hasNext(); )
- {
- Capability cap = itCand.next();
- if (!CapabilitySet.matches(
- (CapabilityImpl) cap, dynReq.getFilter()))
- {
- itCand.remove();
- }
- }
- }
- else
- {
- candidates.clear();
- }
-
- Candidates allCandidates = null;
-
- if (candidates.size() > 0)
- {
- allCandidates = new Candidates();
- allCandidates.populateDynamic(rc, resource, dynReq, candidates);
- }
-
- return allCandidates;
- }
-*/
private void calculatePackageSpaces(
ResolveContext rc,
Resource resource,
@@ -538,8 +479,10 @@
}
}
- // Create parallel arrays for requirement and proposed candidate
+ // Create parallel lists for requirement and proposed candidate
// capability or actual capability if resource is resolved or not.
+ // We use parallel lists so we can calculate the packages spaces for
+ // resolved and unresolved resources in an identical fashion.
List<Requirement> reqs = new ArrayList();
List<Capability> caps = new ArrayList();
boolean isDynamicImporting = false;
@@ -578,6 +521,11 @@
// Since the resource is resolved, it could be dynamically importing,
// so check to see if there are candidates for any of its dynamic
// imports.
+ //
+ // NOTE: If the resource is dynamically importing, the fact that
+ // the dynamic import is added here last to the parallel reqs/caps
+ // list is used later when checking to see if the package being
+ // dynamically imported shadows an existing provider.
for (Requirement req
: Util.getDynamicRequirements(wiring.getResourceRequirements(null)))
{
@@ -588,7 +536,6 @@
{
continue;
}
-
// Grab first (i.e., highest priority) candidate.
Capability cap = candCaps.get(0);
reqs.add(req);
@@ -634,6 +581,30 @@
Requirement req = reqs.get(i);
Capability cap = caps.get(i);
calculateExportedPackages(rc, cap.getResource(), allCandidates, resourcePkgMap);
+
+ // If this resource is dynamically importing, then the last requirement
+ // is the dynamic import being resolved, since it is added last to the
+ // parallel lists above. For the dynamically imported package, make
+ // sure that the resource doesn't already have a provider for that
+ // package, which would be illegal and shouldn't be allowed.
+ if (isDynamicImporting && ((i + 1) == reqs.size()))
+ {
+ String pkgName = (String)
+ cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
+ if (resourcePkgs.m_exportedPkgs.containsKey(pkgName)
+ || resourcePkgs.m_importedPkgs.containsKey(pkgName)
+ || resourcePkgs.m_requiredPkgs.containsKey(pkgName))
+ {
+ throw new IllegalArgumentException(
+ "Resource "
+ + resource
+ + " cannot dynamically import package '"
+ + pkgName
+ + "' since it already has access to it."
+ );
+ }
+ }
+
mergeCandidatePackages(
rc, resource, req, cap, resourcePkgMap, allCandidates,
new HashMap<Resource, List<Capability>>());
@@ -970,17 +941,27 @@
private void checkPackageSpaceConsistency(
ResolveContext rc,
- boolean isDynamicImporting,
Resource resource,
Candidates allCandidates,
Map<Resource, Packages> resourcePkgMap,
Map<Resource, Object> resultCache) throws ResolutionException
{
- if (rc.getWirings().containsKey(resource) && !isDynamicImporting)
+ if (rc.getWirings().containsKey(resource))
{
return;
}
- else if(resultCache.containsKey(resource))
+ checkDynamicPackageSpaceConsistency(
+ rc, resource, allCandidates, resourcePkgMap, resultCache);
+ }
+
+ private void checkDynamicPackageSpaceConsistency(
+ ResolveContext rc,
+ Resource resource,
+ Candidates allCandidates,
+ Map<Resource, Packages> resourcePkgMap,
+ Map<Resource, Object> resultCache) throws ResolutionException
+ {
+ if (resultCache.containsKey(resource))
{
return;
}
@@ -1245,7 +1226,7 @@
try
{
checkPackageSpaceConsistency(
- rc, false, importBlame.m_cap.getResource(),
+ rc, importBlame.m_cap.getResource(),
allCandidates, resourcePkgMap, resultCache);
}
catch (ResolutionException ex)
@@ -1270,7 +1251,7 @@
Candidates allCandidates, Requirement req, List<Candidates> permutations)
{
List<Capability> candidates = allCandidates.getCandidates(req);
- if (candidates.size() > 1)
+ if ((candidates != null) && (candidates.size() > 1))
{
Candidates perm = allCandidates.copy();
candidates = perm.getCandidates(req);
@@ -1283,7 +1264,7 @@
Candidates allCandidates, Requirement req, List<Candidates> permutations)
{
List<Capability> candidates = allCandidates.getCandidates(req);
- if (candidates.size() > 1)
+ if ((candidates != null) && (candidates.size() > 1))
{
// Check existing permutations to make sure we haven't
// already permutated this requirement. This check for