Reimplement resolver to improve "worst case" performance. (FELIX-2037, FELIX-2035)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@918500 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java b/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
index 8f89e6e..a158306 100644
--- a/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
+++ b/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
@@ -19,53 +19,105 @@
package org.apache.felix.framework;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import org.apache.felix.framework.searchpolicy.ResolveException;
-import org.apache.felix.framework.searchpolicy.Resolver;
+import java.util.Set;
+import java.util.TreeSet;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.framework.capabilityset.Directive;
+import org.apache.felix.framework.resolver.Module;
+import org.apache.felix.framework.capabilityset.Requirement;
+import org.apache.felix.framework.resolver.Wire;
+import org.apache.felix.framework.resolver.CandidateComparator;
+import org.apache.felix.framework.resolver.ResolveException;
+import org.apache.felix.framework.resolver.Resolver;
import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.util.VersionRange;
-import org.apache.felix.framework.util.manifestparser.R4Attribute;
-import org.apache.felix.framework.util.manifestparser.R4Directive;
-import org.apache.felix.framework.util.manifestparser.Requirement;
-import org.apache.felix.moduleloader.ICapability;
-import org.apache.felix.moduleloader.IModule;
-import org.apache.felix.moduleloader.IRequirement;
-import org.apache.felix.moduleloader.IWire;
import org.osgi.framework.BundlePermission;
import org.osgi.framework.Constants;
-import org.osgi.framework.PackagePermission;
import org.osgi.framework.Version;
public class FelixResolverState implements Resolver.ResolverState
{
private final Logger m_logger;
// List of all modules.
- private final List m_moduleList = new ArrayList();
- // Map of fragment symbolic names to list of fragment modules sorted by version.
- private final Map m_fragmentMap = new HashMap();
- // Maps a package name to a list of exporting capabilities.
- private final Map m_unresolvedPkgIndex = new HashMap();
- // Maps a package name to a list of exporting capabilities.
- private final Map m_resolvedPkgIndex = new HashMap();
- // Maps a module to a list of capabilities.
- private final Map m_resolvedCapMap = new HashMap();
+ private final List<Module> m_modules;
+ // Capability set for modules.
+ private final CapabilitySet m_modCapSet;
+ // Capability set for packages.
+ private final CapabilitySet m_pkgCapSet;
+ // Capability set for hosts.
+ private final CapabilitySet m_hostCapSet;
+ // Maps fragment symbolic names to list of fragment modules sorted by version.
+ private final Map<String, List<Module>> m_fragmentMap = new HashMap();
+ // Maps singleton symbolic names to list of modules sorted by version.
+ private final Map<String, List<Module>> m_singletons = new HashMap();
public FelixResolverState(Logger logger)
{
m_logger = logger;
+ m_modules = new ArrayList<Module>();
+
+ List indices = new ArrayList();
+ indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+ m_modCapSet = new CapabilitySet(indices);
+
+ indices = new ArrayList();
+ indices.add(Capability.PACKAGE_ATTR);
+ m_pkgCapSet = new CapabilitySet(indices);
+
+ indices = new ArrayList();
+ indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+ m_hostCapSet = new CapabilitySet(indices);
}
- public synchronized void addModule(IModule module)
+ public synchronized void addModule(Module module)
{
- if (Util.isFragment(module))
+ if (isSingleton(module))
{
- addFragment(module);
+ // Find the currently selected singleton, which is either the
+ // highest version or the resolved one.
+ List<Module> modules = m_singletons.get(module.getSymbolicName());
+ // Get the highest version.
+ Module current = ((modules != null) && !modules.isEmpty()) ? modules.get(0) : null;
+ // Now check to see if there is a resolved one instead.
+ for (int i = 0; (modules != null) && (i < modules.size()); i++)
+ {
+ if (modules.get(i).isResolved())
+ {
+ current = modules.get(i);
+ }
+ }
+
+ // Index the new singleton.
+ Module highest = indexModule(m_singletons, module);
+ // If the currently selected singleton is not resolved and
+ // the newly added singleton is a higher version, then select
+ // it instead.
+ if ((current != null) && !current.isResolved() && (current != highest))
+ {
+ if (Util.isFragment(current))
+ {
+ removeFragment(current);
+ }
+ else
+ {
+ removeHost(current);
+ }
+ }
+ else if (current != null)
+ {
+ module = null;
+ }
}
- else
+
+ if ((module != null) && Util.isFragment(module))
+ {
+ addFragment(module);
+ }
+ else if (module != null)
{
addHost(module);
}
@@ -76,8 +128,20 @@
//dumpPackageIndex(m_resolvedPkgIndex);
}
- public synchronized void removeModule(IModule module)
+ public synchronized void removeModule(Module module)
{
+ // If this module is a singleton, then remove it from the
+ // singleton map.
+ List<Module> modules = m_singletons.get(module.getSymbolicName());
+ if (modules != null)
+ {
+ modules.remove(module);
+ if (modules.size() == 0)
+ {
+ m_singletons.remove(module.getSymbolicName());
+ }
+ }
+
if (Util.isFragment(module))
{
removeFragment(module);
@@ -88,10 +152,75 @@
}
}
- private void addFragment(IModule fragment)
+ public void detachFragment(Module host, Module fragment)
+ {
+ List<Module> fragments = ((ModuleImpl) host).getFragments();
+ fragments.remove(fragment);
+ try
+ {
+ ((ModuleImpl) host).attachFragments(fragments);
+ }
+ catch (Exception ex)
+ {
+ // Try to clean up by removing all fragments.
+ try
+ {
+ ((ModuleImpl) host).attachFragments(null);
+ }
+ catch (Exception ex2)
+ {
+ }
+ m_logger.log(Logger.LOG_ERROR,
+ "Serious error attaching fragments.", ex);
+ }
+ }
+
+ public void checkSingleton(Module module)
+ {
+ // Check if this module is a singleton.
+ List<Module> modules = m_singletons.get(module.getSymbolicName());
+ if ((modules != null) && modules.contains(module))
+ {
+ // If it is, check if there is already a resolved singleton.
+ for (int i = 0; (modules != null) && (i < modules.size()); i++)
+ {
+ if (modules.get(i).isResolved())
+ {
+ throw new ResolveException(
+ "Only one singleton can be resolved at a time.", null, null);
+ }
+ }
+
+ // If not, check to see if it is the selected singleton.
+ Module current = (modules.size() > 0) ? modules.get(0) : null;
+ if ((current != null) && (current != module))
+ {
+ // If it is not the selected singleton, remove the selected
+ // singleton and select the specified one instead.
+ if (Util.isFragment(current))
+ {
+ removeFragment(current);
+ }
+ else
+ {
+ removeHost(current);
+ }
+ if (Util.isFragment(module))
+ {
+ addFragment(module);
+ }
+ else if (module != null)
+ {
+ addHost(module);
+ }
+ }
+ }
+ }
+
+ private void addFragment(Module fragment)
{
// TODO: FRAGMENT - This should check to make sure that the host allows fragments.
- IModule bestFragment = indexFragment(m_fragmentMap, fragment);
+ Module bestFragment = indexModule(m_fragmentMap, fragment);
// If the newly added fragment is the highest version for
// its given symbolic name, then try to merge it to any
@@ -107,20 +236,20 @@
List matchingHosts = getMatchingHosts(fragment);
for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
{
- IModule host = ((ICapability) matchingHosts.get(hostIdx)).getModule();
+ Module host = ((Capability) matchingHosts.get(hostIdx)).getModule();
// Get the fragments currently attached to the host so we
// can remove the older version of the current fragment, if any.
- IModule[] fragments = ((ModuleImpl) host).getFragments();
- List fragmentList = new ArrayList();
+ List<Module> fragments = ((ModuleImpl) host).getFragments();
+ List<Module> fragmentList = new ArrayList();
for (int fragIdx = 0;
- (fragments != null) && (fragIdx < fragments.length);
+ (fragments != null) && (fragIdx < fragments.size());
fragIdx++)
{
- if (!fragments[fragIdx].getSymbolicName().equals(
+ if (!fragments.get(fragIdx).getSymbolicName().equals(
bestFragment.getSymbolicName()))
{
- fragmentList.add(fragments[fragIdx]);
+ fragmentList.add(fragments.get(fragIdx));
}
}
@@ -130,7 +259,7 @@
(index < 0) && (listIdx < fragmentList.size());
listIdx++)
{
- IModule f = (IModule) fragmentList.get(listIdx);
+ Module f = fragmentList.get(listIdx);
if (bestFragment.getBundle().getBundleId()
< f.getBundle().getBundleId())
{
@@ -141,30 +270,21 @@
(index < 0) ? fragmentList.size() : index, bestFragment);
// Remove host's existing exported packages from index.
- ICapability[] caps = host.getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
+ List<Capability> caps = host.getCapabilities();
+ for (int i = 0; (caps != null) && (i < caps.size()); i++)
{
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ if (caps.get(i).getNamespace().equals(Capability.MODULE_NAMESPACE))
{
- // Get package name.
- String pkgName = (String)
- caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
- // Remove from "unresolved" package map.
- List capList = (List) m_unresolvedPkgIndex.get(pkgName);
- if (capList != null)
- {
- capList.remove(caps[i]);
- }
+ m_modCapSet.removeCapability(caps.get(i));
+ }
+ else if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ m_pkgCapSet.removeCapability(caps.get(i));
}
}
- // Check if fragment conflicts with existing metadata.
- checkForConflicts(host, fragmentList);
-
// Attach the fragments to the host.
- fragments = (fragmentList.size() == 0)
- ? null
- : (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
+ fragments = (fragmentList.size() == 0) ? null : fragmentList;
try
{
((ModuleImpl) host).attachFragments(fragments);
@@ -185,18 +305,22 @@
// Reindex the host's exported packages.
caps = host.getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
+ for (int i = 0; (caps != null) && (i < caps.size()); i++)
{
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ if (caps.get(i).getNamespace().equals(Capability.MODULE_NAMESPACE))
{
- indexPackageCapability(m_unresolvedPkgIndex, caps[i]);
+ m_modCapSet.addCapability(caps.get(i));
+ }
+ else if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ m_pkgCapSet.addCapability(caps.get(i));
}
}
}
}
}
- private void removeFragment(IModule fragment)
+ private void removeFragment(Module fragment)
{
// Get fragment list, which may be null for system bundle fragments.
List fragList = (List) m_fragmentMap.get(fragment.getSymbolicName());
@@ -216,71 +340,60 @@
List matchingHosts = getMatchingHosts(fragment);
for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
{
- IModule host = ((ICapability) matchingHosts.get(hostIdx)).getModule();
+ Module host = ((Capability) matchingHosts.get(hostIdx)).getModule();
// Check to see if the removed fragment was actually merged with
// the host, since it might not be if it wasn't the highest version.
// If it was, recalculate the fragments for the host.
- IModule[] fragments = ((ModuleImpl) host).getFragments();
- for (int fragIdx = 0;
- (fragments != null) && (fragIdx < fragments.length);
- fragIdx++)
+ List<Module> fragments = ((ModuleImpl) host).getFragments();
+ if (fragments.contains(fragment))
{
- if (!fragments[fragIdx].equals(fragment))
+ List fragmentList = getMatchingFragments(host);
+
+ // Remove host's existing exported packages from index.
+ List<Capability> caps = host.getCapabilities();
+ for (int i = 0; (caps != null) && (i < caps.size()); i++)
{
- List fragmentList = getMatchingFragments(host);
-
- // Remove host's existing exported packages from index.
- ICapability[] caps = host.getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
+ if (caps.get(i).getNamespace().equals(Capability.MODULE_NAMESPACE))
{
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- // Get package name.
- String pkgName = (String)
- caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
- // Remove from "unresolved" package map.
- List capList = (List) m_unresolvedPkgIndex.get(pkgName);
- if (capList != null)
- {
- capList.remove(caps[i]);
- }
- }
+ m_modCapSet.removeCapability(caps.get(i));
}
+ else if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ m_pkgCapSet.removeCapability(caps.get(i));
+ }
+ }
- // Check if fragment conflicts with existing metadata.
- checkForConflicts(host, fragmentList);
-
- // Attach the fragments to the host.
- fragments = (fragmentList.size() == 0)
- ? null
- : (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
+ // Attach the fragments to the host.
+ try
+ {
+ ((ModuleImpl) host).attachFragments(fragmentList);
+ }
+ catch (Exception ex)
+ {
+ // Try to clean up by removing all fragments.
try
{
- ((ModuleImpl) host).attachFragments(fragments);
+ ((ModuleImpl) host).attachFragments(null);
}
- catch (Exception ex)
+ catch (Exception ex2)
{
- // Try to clean up by removing all fragments.
- try
- {
- ((ModuleImpl) host).attachFragments(null);
- }
- catch (Exception ex2)
- {
- }
- m_logger.log(Logger.LOG_ERROR,
- "Serious error attaching fragments.", ex);
}
+ m_logger.log(Logger.LOG_ERROR,
+ "Serious error attaching fragments.", ex);
+ }
- // Reindex the host's exported packages.
- caps = host.getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
+ // Reindex the host's exported packages.
+ caps = host.getCapabilities();
+ for (int i = 0; (caps != null) && (i < caps.size()); i++)
+ {
+ if (caps.get(i).getNamespace().equals(Capability.MODULE_NAMESPACE))
{
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- indexPackageCapability(m_unresolvedPkgIndex, caps[i]);
- }
+ m_modCapSet.addCapability(caps.get(i));
+ }
+ else if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ m_pkgCapSet.addCapability(caps.get(i));
}
}
}
@@ -288,105 +401,20 @@
}
}
- public void unmergeFragment(IModule module)
+ public void unmergeFragment(Module fragment)
{
- if (!Util.isFragment(module))
+ if (!Util.isFragment(fragment))
{
return;
}
- // Get fragment list, which may be null for system bundle fragments.
- List fragList = (List) m_fragmentMap.get(module.getSymbolicName());
- if (fragList != null)
- {
- // Remove from fragment map.
- fragList.remove(module);
- if (fragList.size() == 0)
- {
- m_fragmentMap.remove(module.getSymbolicName());
- }
-
- // If we have any matching hosts, then remove fragment while
- // removing any older version of the new fragment. Also remove host's
- // existing capabilities from the package index and reindex its new
- // ones after attaching the fragment.
- List matchingHosts = getMatchingHosts(module);
- for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
- {
- IModule host = ((ICapability) matchingHosts.get(hostIdx)).getModule();
- // Find any unresolved hosts into which the fragment is merged
- // and unmerge it.
- IModule[] fragments = ((ModuleImpl) host).getFragments();
- for (int fragIdx = 0;
- !host.isResolved() && (fragments != null) && (fragIdx < fragments.length);
- fragIdx++)
- {
- if (!fragments[fragIdx].equals(module))
- {
- List fragmentList = getMatchingFragments(host);
-
- // Remove host's existing exported packages from index.
- ICapability[] caps = host.getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
- {
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- // Get package name.
- String pkgName = (String)
- caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
- // Remove from "unresolved" package map.
- List capList = (List) m_unresolvedPkgIndex.get(pkgName);
- if (capList != null)
- {
- capList.remove(caps[i]);
- }
- }
- }
-
- // Check if fragment conflicts with existing metadata.
- checkForConflicts(host, fragmentList);
-
- // Attach the fragments to the host.
- fragments = (fragmentList.size() == 0)
- ? null
- : (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
- try
- {
- ((ModuleImpl) host).attachFragments(fragments);
- }
- catch (Exception ex)
- {
- // Try to clean up by removing all fragments.
- try
- {
- ((ModuleImpl) host).attachFragments(null);
- }
- catch (Exception ex2)
- {
- }
- m_logger.log(Logger.LOG_ERROR,
- "Serious error attaching fragments.", ex);
- }
-
- // Reindex the host's exported packages.
- caps = host.getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
- {
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- indexPackageCapability(m_unresolvedPkgIndex, caps[i]);
- }
- }
- }
- }
- }
- }
+ removeFragment(fragment);
}
- private List getMatchingHosts(IModule fragment)
+ private List getMatchingHosts(Module fragment)
{
// Find the fragment's host requirement.
- IRequirement hostReq = getFragmentHostRequirement(fragment);
+ Requirement hostReq = getFragmentHostRequirement(fragment);
// Create a list of all matching hosts for this fragment.
List matchingHosts = new ArrayList();
@@ -399,32 +427,26 @@
return matchingHosts;
}
}
- for (int hostIdx = 0; (hostReq != null) && (hostIdx < m_moduleList.size()); hostIdx++)
+
+ Set<Capability> hostCaps = m_hostCapSet.match(hostReq.getFilter(), true);
+
+ for (Capability hostCap : hostCaps)
{
- IModule host = (IModule) m_moduleList.get(hostIdx);
// Only look at unresolved hosts, since we don't support
// dynamic attachment of fragments.
- if (host.isResolved()
- || ((BundleImpl) host.getBundle()).isStale()
- || ((BundleImpl) host.getBundle()).isRemovalPending())
+ if (hostCap.getModule().isResolved()
+ || ((BundleImpl) hostCap.getModule().getBundle()).isStale()
+ || ((BundleImpl) hostCap.getModule().getBundle()).isRemovalPending())
{
continue;
}
- // Find the host capability for the current host.
- ICapability hostCap = Util.getSatisfyingCapability(host, hostReq);
-
- // If there is no host capability in the current module,
- // then just ignore it.
- if (hostCap == null)
- {
- continue;
- }
-
if (sm != null)
{
- if (!((BundleProtectionDomain) host.getSecurityContext()).impliesDirect(new BundlePermission(host.getSymbolicName(),
- BundlePermission.HOST)))
+ if (!((BundleProtectionDomain) hostCap.getModule()
+ .getSecurityContext()).impliesDirect(
+ new BundlePermission(hostCap.getModule().getSymbolicName(),
+ BundlePermission.HOST)))
{
continue;
}
@@ -436,280 +458,28 @@
return matchingHosts;
}
- private void checkForConflicts(IModule host, List fragmentList)
- {
- if ((fragmentList == null) || (fragmentList.size() == 0))
- {
- return;
- }
-
- // Verify the fragments do not have conflicting imports.
- // For now, just check for duplicate imports, but in the
- // future we might want to make this more fine grained.
- // First get the host's imported packages.
- final int MODULE_IDX = 0, REQ_IDX = 1;
- Map ipMerged = new HashMap();
- Map rbMerged = new HashMap();
- IRequirement[] reqs = host.getRequirements();
- for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
- {
- if (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- ipMerged.put(
- ((Requirement) reqs[reqIdx]).getTargetName(),
- new Object[] { host, reqs[reqIdx] });
- }
- else if (reqs[reqIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- rbMerged.put(
- ((Requirement) reqs[reqIdx]).getTargetName(),
- new Object[] { host, reqs[reqIdx] });
- }
- }
- // Loop through each fragment verifying it does not conflict.
- // Add its package and bundle dependencies if they do not
- // conflict or remove the fragment if it does conflict.
- for (Iterator it = fragmentList.iterator(); it.hasNext(); )
- {
- IModule fragment = (IModule) it.next();
- reqs = fragment.getRequirements();
- Map ipFragment = new HashMap();
- Map rbFragment = new HashMap();
- for (int reqIdx = 0;
- (reqs != null) && (reqIdx < reqs.length);
- reqIdx++)
- {
- if (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
- || reqs[reqIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- String targetName = ((Requirement) reqs[reqIdx]).getTargetName();
- Map mergedReqMap =
- (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- ? ipMerged : rbMerged;
- Map fragmentReqMap =
- (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- ? ipFragment : rbFragment;
- Object[] existing = (Object[]) mergedReqMap.get(targetName);
- if (existing == null)
- {
- fragmentReqMap.put(targetName, new Object[] { fragment, reqs[reqIdx] });
- }
- else if (isRequirementConflicting(
- (Requirement) existing[REQ_IDX], (Requirement) reqs[reqIdx]))
- {
- ipFragment.clear();
- rbFragment.clear();
- it.remove();
- m_logger.log(
- Logger.LOG_DEBUG,
- "Excluding fragment " + fragment.getSymbolicName()
- + " from " + host.getSymbolicName()
- + " due to conflict with "
- + (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
- ? "imported package " : "required bundle ")
- + targetName + " from "
- + ((IModule) existing[MODULE_IDX]).getSymbolicName());
- // No need to finish processing current fragment.
- break;
- }
- else
- {
- // If there is an overlapping requirement for the existing
- // target, then try to calculate the intersecting requirement
- // and set the existing requirement to that instead. This
- // makes it so version ranges do not have to be exact, just
- // overlapping.
- Requirement intersection = calculateVersionIntersection(
- (Requirement) existing[REQ_IDX], (Requirement) reqs[reqIdx]);
- if (intersection != existing[REQ_IDX])
- {
- existing[REQ_IDX] = intersection;
- }
- }
- }
- }
-
- // Merge non-conflicting requirements into overall set
- // of requirements and continue checking for conflicts
- // with the next fragment.
- for (Iterator it2 = ipFragment.entrySet().iterator(); it2.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) it2.next();
- ipMerged.put(entry.getKey(), entry.getValue());
- }
- for (Iterator it2 = rbFragment.entrySet().iterator(); it2.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) it2.next();
- rbMerged.put(entry.getKey(), entry.getValue());
- }
- }
- }
-
- private boolean isRequirementConflicting(
- Requirement existing, Requirement additional)
- {
- // If the namespace is not the same, then they do NOT conflict.
- if (!existing.getNamespace().equals(additional.getNamespace()))
- {
- return false;
- }
- // If the target name is not the same, then they do NOT conflict.
- if (!existing.getTargetName().equals(additional.getTargetName()))
- {
- return false;
- }
- // If the existing version range floor is greater than the additional
- // version range's floor, then they are inconflict since we cannot
- // widen the constraint.
- if (!existing.getTargetVersionRange().intersects(
- additional.getTargetVersionRange()))
- {
- return true;
- }
- // If optionality is not the same, then they conflict, unless
- // the existing requirement is not optional, then it doesn't matter
- // what subsequent requirements are since non-optional is stronger
- // than optional.
- if (existing.isOptional() && !additional.isOptional())
- {
- return true;
- }
- // Verify directives are the same.
- final R4Directive[] exDirs = (existing.getDirectives() == null)
- ? new R4Directive[0] : existing.getDirectives();
- final R4Directive[] addDirs = (additional.getDirectives() == null)
- ? new R4Directive[0] : additional.getDirectives();
- // Put attributes in a map, since ordering is arbitrary.
- final Map exDirMap = new HashMap();
- for (int i = 0; i < exDirs.length; i++)
- {
- exDirMap.put(exDirs[i].getName(), exDirs[i]);
- }
- // If attribute values do not match, then they conflict.
- for (int i = 0; i < addDirs.length; i++)
- {
- // Ignore resolution directive, since we've already tested it above.
- if (!addDirs[i].getName().equals(Constants.RESOLUTION_DIRECTIVE))
- {
- final R4Directive exDir = (R4Directive) exDirMap.get(addDirs[i].getName());
- if ((exDir == null) ||
- !exDir.getValue().equals(addDirs[i].getValue()))
- {
- return true;
- }
- }
- }
- // Verify attributes are the same.
- final R4Attribute[] exAttrs = (existing.getAttributes() == null)
- ? new R4Attribute[0] : existing.getAttributes();
- final R4Attribute[] addAttrs = (additional.getAttributes() == null)
- ? new R4Attribute[0] : additional.getAttributes();
- // Put attributes in a map, since ordering is arbitrary.
- final Map exAttrMap = new HashMap();
- for (int i = 0; i < exAttrs.length; i++)
- {
- exAttrMap.put(exAttrs[i].getName(), exAttrs[i]);
- }
- // If attribute values do not match, then they conflict.
- for (int i = 0; i < addAttrs.length; i++)
- {
- // Ignore version property, since we've already tested it above.
- if (!(additional.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
- && addAttrs[i].getName().equals(ICapability.VERSION_PROPERTY))
- && !(additional.getNamespace().equals(ICapability.MODULE_NAMESPACE)
- && addAttrs[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE)))
- {
- final R4Attribute exAttr = (R4Attribute) exAttrMap.get(addAttrs[i].getName());
- if ((exAttr == null) ||
- !exAttr.getValue().equals(addAttrs[i].getValue()) ||
- (exAttr.isMandatory() != addAttrs[i].isMandatory()))
- {
- return true;
- }
- }
- }
- // They do no conflict.
- return false;
- }
-
- static Requirement calculateVersionIntersection(
- Requirement existing, Requirement additional)
- {
- Requirement intersection = existing;
- int existVersionIdx = -1, addVersionIdx = -1;
-
- // Find the existing version attribute.
- for (int i = 0; (existVersionIdx < 0) && (i < existing.getAttributes().length); i++)
- {
- if ((existing.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
- && existing.getAttributes()[i].getName().equals(ICapability.VERSION_PROPERTY))
- || (existing.getNamespace().equals(ICapability.MODULE_NAMESPACE)
- && existing.getAttributes()[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE)))
- {
- existVersionIdx = i;
- }
- }
-
- // Find the additional version attribute.
- for (int i = 0; (addVersionIdx < 0) && (i < additional.getAttributes().length); i++)
- {
- if ((additional.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
- && additional.getAttributes()[i].getName().equals(ICapability.VERSION_PROPERTY))
- || (additional.getNamespace().equals(ICapability.MODULE_NAMESPACE)
- && additional.getAttributes()[i].getName().equals(Constants.BUNDLE_VERSION_ATTRIBUTE)))
- {
- addVersionIdx = i;
- }
- }
-
- // Use the additional requirement's version range if it
- // has one and the existing requirement does not.
- if ((existVersionIdx == -1) && (addVersionIdx != -1))
- {
- intersection = additional;
- }
- // If both requirements have version ranges, then create
- // a new requirement with an intersecting version range.
- else if ((existVersionIdx != -1) && (addVersionIdx != -1))
- {
- VersionRange vr = ((VersionRange) existing.getAttributes()[existVersionIdx].getValue())
- .intersection((VersionRange) additional.getAttributes()[addVersionIdx].getValue());
- R4Attribute[] attrs = existing.getAttributes();
- R4Attribute[] newAttrs = new R4Attribute[attrs.length];
- System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
- newAttrs[existVersionIdx] = new R4Attribute(
- attrs[existVersionIdx].getName(), vr, false);
- intersection = new Requirement(
- existing.getNamespace(),
- existing.getDirectives(),
- newAttrs);
- }
-
- return intersection;
- }
-
- private void addHost(IModule host)
+ private void addHost(Module host)
{
// When a module is added, we first need to pre-merge any potential fragments
// into the host and then second create an aggregated list of unresolved
// capabilities to simplify later processing when resolving bundles.
- m_moduleList.add(host);
+ m_modules.add(host);
+ List<Capability> caps = Util.getCapabilityByNamespace(host, Capability.HOST_NAMESPACE);
+ if (caps.size() > 0)
+ {
+ m_hostCapSet.addCapability(caps.get(0));
+ }
//
// First, merge applicable fragments.
//
- List fragmentList = getMatchingFragments(host);
+ List<Module> fragments = getMatchingFragments(host);
// Attach any fragments we found for this host.
- if (fragmentList.size() > 0)
+ if (fragments.size() > 0)
{
- // Check if fragment conflicts with existing metadata.
- checkForConflicts(host, fragmentList);
-
// Attach the fragments to the host.
- IModule[] fragments =
- (IModule[]) fragmentList.toArray(new IModule[fragmentList.size()]);
try
{
((ModuleImpl) host).attachFragments(fragments);
@@ -733,52 +503,47 @@
// Second, index module's capabilities.
//
- ICapability[] caps = host.getCapabilities();
+ caps = host.getCapabilities();
// Add exports to unresolved package map.
- for (int i = 0; (caps != null) && (i < caps.length); i++)
+ for (int i = 0; (caps != null) && (i < caps.size()); i++)
{
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ if (caps.get(i).getNamespace().equals(Capability.MODULE_NAMESPACE))
{
- indexPackageCapability(m_unresolvedPkgIndex, caps[i]);
+ m_modCapSet.addCapability(caps.get(i));
+ }
+ else if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ m_pkgCapSet.addCapability(caps.get(i));
}
}
}
- private void removeHost(IModule host)
+ private void removeHost(Module host)
{
// We need remove the host's exports from the "resolved" and
// "unresolved" package maps, remove its dependencies on fragments
// and exporters, and remove it from the module list.
- m_moduleList.remove(host);
-
- // Remove exports from package maps.
- ICapability[] caps = host.getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
+ m_modules.remove(host);
+ List<Capability> caps = Util.getCapabilityByNamespace(host, Capability.HOST_NAMESPACE);
+ if (caps.size() > 0)
{
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- // Get package name.
- String pkgName = (String)
- caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
- // Remove from "unresolved" package map.
- List capList = (List) m_unresolvedPkgIndex.get(pkgName);
- if (capList != null)
- {
- capList.remove(caps[i]);
- }
-
- // Remove from "resolved" package map.
- capList = (List) m_resolvedPkgIndex.get(pkgName);
- if (capList != null)
- {
- capList.remove(caps[i]);
- }
- }
+ m_hostCapSet.removeCapability(caps.get(0));
}
- // Remove the module from the "resolved" map.
- m_resolvedCapMap.remove(host);
+ // Remove exports from package maps.
+ caps = host.getCapabilities();
+ for (int i = 0; (caps != null) && (i < caps.size()); i++)
+ {
+ if (caps.get(i).getNamespace().equals(Capability.MODULE_NAMESPACE))
+ {
+ m_modCapSet.removeCapability(caps.get(i));
+ }
+ else if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ m_pkgCapSet.removeCapability(caps.get(i));
+ }
+ }
// Set fragments to null, which will remove the module from all
// of its dependent fragment modules.
@@ -795,11 +560,11 @@
((ModuleImpl) host).setWires(null);
}
- private List getMatchingFragments(IModule host)
+ private List getMatchingFragments(Module host)
{
// Find the host capability for the current host.
- ICapability[] caps = Util.getCapabilityByNamespace(host, ICapability.HOST_NAMESPACE);
- ICapability hostCap = (caps.length == 0) ? null : caps[0];
+ List<Capability> caps = Util.getCapabilityByNamespace(host, Capability.HOST_NAMESPACE);
+ Capability hostCap = (caps.size() == 0) ? null : caps.get(0);
// If we have a host capability, then loop through all fragments trying to
// find ones that match.
@@ -816,10 +581,10 @@
{
Map.Entry entry = (Map.Entry) it.next();
List fragments = (List) entry.getValue();
- IModule fragment = null;
+ Module fragment = null;
for (int i = 0; (fragment == null) && (i < fragments.size()); i++)
{
- IModule f = (IModule) fragments.get(i);
+ Module f = (Module) fragments.get(i);
if (!((BundleImpl) f.getBundle()).isStale()
&& !((BundleImpl) f.getBundle()).isRemovalPending())
{
@@ -839,11 +604,11 @@
continue;
}
}
- IRequirement hostReq = getFragmentHostRequirement(fragment);
+ Requirement hostReq = getFragmentHostRequirement(fragment);
// If we have a host requirement, then loop through each host and
// see if it matches the host requirement.
- if ((hostReq != null) && hostReq.isSatisfied(hostCap))
+ if ((hostReq != null) && CapabilitySet.matches(hostCap, hostReq.getFilter()))
{
// Now add the new fragment in bundle ID order.
int index = -1;
@@ -851,7 +616,7 @@
(index < 0) && (listIdx < fragmentList.size());
listIdx++)
{
- IModule existing = (IModule) fragmentList.get(listIdx);
+ Module existing = (Module) fragmentList.get(listIdx);
if (fragment.getBundle().getBundleId()
< existing.getBundle().getBundleId())
{
@@ -866,16 +631,16 @@
return fragmentList;
}
- public synchronized IModule findHost(IModule rootModule) throws ResolveException
+ public synchronized Module findHost(Module rootModule) throws ResolveException
{
- IModule newRootModule = rootModule;
+ Module newRootModule = rootModule;
if (Util.isFragment(rootModule))
{
List matchingHosts = getMatchingHosts(rootModule);
- IModule currentBestHost = null;
+ Module currentBestHost = null;
for (int hostIdx = 0; hostIdx < matchingHosts.size(); hostIdx++)
{
- IModule host = ((ICapability) matchingHosts.get(hostIdx)).getModule();
+ Module host = ((Capability) matchingHosts.get(hostIdx)).getModule();
if (currentBestHost == null)
{
currentBestHost = host;
@@ -897,16 +662,16 @@
return newRootModule;
}
- private IRequirement getFragmentHostRequirement(IModule fragment)
+ private static Requirement getFragmentHostRequirement(Module fragment)
{
// Find the fragment's host requirement.
- IRequirement[] reqs = fragment.getRequirements();
- IRequirement hostReq = null;
- for (int reqIdx = 0; (hostReq == null) && (reqIdx < reqs.length); reqIdx++)
+ List<Requirement> reqs = fragment.getRequirements();
+ Requirement hostReq = null;
+ for (int reqIdx = 0; (hostReq == null) && (reqIdx < reqs.size()); reqIdx++)
{
- if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
+ if (reqs.get(reqIdx).getNamespace().equals(Capability.HOST_NAMESPACE))
{
- hostReq = reqs[reqIdx];
+ hostReq = reqs.get(reqIdx);
}
}
return hostReq;
@@ -918,60 +683,31 @@
* to capture additional capabilities.
* @param module The module being refresh, which should always be the system bundle.
**/
- synchronized void refreshSystemBundleModule(IModule module)
+ synchronized void refreshSystemBundleModule(Module module)
{
// The system bundle module should always be resolved, so we only need
// to update the resolved capability map.
- ICapability[] caps = module.getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
+ List<Capability> caps = module.getCapabilities();
+ for (int i = 0; (caps != null) && (i < caps.size()); i++)
{
- List resolvedCaps = (List) m_resolvedCapMap.get(module);
- if (resolvedCaps == null)
+ if (caps.get(i).getNamespace().equals(Capability.MODULE_NAMESPACE))
{
- m_resolvedCapMap.put(module, resolvedCaps = new ArrayList());
+ m_modCapSet.addCapability(caps.get(i));
}
- if (!resolvedCaps.contains(caps[i]))
+ else if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
{
- resolvedCaps.add(caps[i]);
- }
-
- // If the capability is a package, then add the exporter module
- // of the wire to the "resolved" package index and remove it
- // from the "unresolved" package index.
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- // Add to "resolved" package index.
- indexPackageCapability(m_resolvedPkgIndex, caps[i]);
+ m_pkgCapSet.addCapability(caps.get(i));
}
}
}
- private void dumpPackageIndex(Map pkgIndex)
+// TODO: FELIX3 - Try to eliminate this.
+ public synchronized List<Module> getModules()
{
- for (Iterator i = pkgIndex.entrySet().iterator(); i.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) i.next();
- List capList = (List) entry.getValue();
- if (capList.size() > 0)
- {
- if (!((capList.size() == 1) && ((ICapability) capList.get(0)).getModule().getId().equals("0")))
- {
- System.out.println(" " + entry.getKey());
- for (int j = 0; j < capList.size(); j++)
- {
- System.out.println(" " + ((ICapability) capList.get(j)).getModule());
- }
- }
- }
- }
+ return m_modules;
}
- public synchronized IModule[] getModules()
- {
- return (IModule[]) m_moduleList.toArray(new IModule[m_moduleList.size()]);
- }
-
- public synchronized void moduleResolved(IModule module)
+ public synchronized void moduleResolved(Module module)
{
if (module.isResolved())
{
@@ -983,40 +719,22 @@
// module and not another module. If it points to another module
// then the capability should be ignored, since the framework
// decided to honor the import and discard the export.
- ICapability[] caps = module.getCapabilities();
+ List<Capability> caps = module.getCapabilities();
- // First remove all existing capabilities from the "unresolved" map.
- for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
- {
- if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- // Get package name.
- String pkgName = (String)
- caps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
- // Remove the module's capability for the package.
- List capList = (List) m_unresolvedPkgIndex.get(pkgName);
- capList.remove(caps[capIdx]);
- }
- }
-
- // Next create a copy of the module's capabilities so we can
+ // Create a copy of the module's capabilities so we can
// null out any capabilities that should be ignored.
- ICapability[] capsCopy = (caps == null) ? null : new ICapability[caps.length];
- if (capsCopy != null)
- {
- System.arraycopy(caps, 0, capsCopy, 0, caps.length);
- }
+ List<Capability> capsCopy = (caps == null) ? null : new ArrayList(caps);
// Loop through the module's capabilities to determine which ones
// can be ignored by seeing which ones satifies the wire requirements.
// TODO: RB - Bug here because a requirement for a package need not overlap the
// capability for that package and this assumes it does. This might
// require us to introduce the notion of a substitutable capability.
- IWire[] wires = module.getWires();
- for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
+ List<Wire> wires = module.getWires();
+ for (int capIdx = 0; (capsCopy != null) && (capIdx < caps.size()); capIdx++)
{
// Loop through all wires to see if the current capability
// satisfies any of the wire requirements.
- for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
+ for (int wireIdx = 0; (wires != null) && (wireIdx < wires.size()); wireIdx++)
{
// If one of the module's capabilities satifies the requirement
// for an existing wire, this means the capability was
@@ -1024,9 +742,10 @@
// the module's capability was not used. Therefore, we should
// null it here so it doesn't get added the list of resolved
// capabilities for this module.
- if (wires[wireIdx].getRequirement().isSatisfied(capsCopy[capIdx]))
+ if (CapabilitySet.matches(
+ caps.get(capIdx), wires.get(wireIdx).getRequirement().getFilter()))
{
- capsCopy[capIdx] = null;
+ capsCopy.remove(caps.get(capIdx));
break;
}
}
@@ -1034,276 +753,72 @@
// Now loop through all capabilities and add them to the "resolved"
// capability and package index maps, ignoring any that were nulled out.
- for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
+// TODO: FELIX3 - This is actually reversed, we need to remove exports that were imported.
+/*
+ for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.size()); capIdx++)
{
- if (capsCopy[capIdx] != null)
+ if (capsCopy.get(capIdx).getNamespace().equals(Capability.MODULE_NAMESPACE))
{
- List resolvedCaps = (List) m_resolvedCapMap.get(module);
- if (resolvedCaps == null)
- {
- m_resolvedCapMap.put(module, resolvedCaps = new ArrayList());
- }
- if (!resolvedCaps.contains(capsCopy[capIdx]))
- {
- resolvedCaps.add(capsCopy[capIdx]);
- }
-
- // If the capability is a package, then add the exporter module
- // of the wire to the "resolved" package index and remove it
- // from the "unresolved" package index.
- if (capsCopy[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- // Add to "resolved" package index.
- indexPackageCapability(m_resolvedPkgIndex, capsCopy[capIdx]);
- }
+ m_modCapSet.addCapability(capsCopy.get(capIdx));
+ }
+ else if (capsCopy.get(capIdx).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ m_pkgCapSet.addCapability(capsCopy.get(capIdx));
}
}
+*/
}
-
-//System.out.println("UNRESOLVED PACKAGES:");
-//dumpPackageIndex(m_unresolvedPkgIndex);
-//System.out.println("RESOLVED PACKAGES:");
-//dumpPackageIndex(m_resolvedPkgIndex);
}
- public synchronized List getResolvedCandidates(IRequirement req, IModule reqModule)
+ public Set<Capability> getCandidates(Module module, Requirement req, boolean obeyMandatory)
{
- // Synchronized on the module manager to make sure that no
- // modules are added, removed, or resolved.
- List candidates = new ArrayList();
- if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
- && (((Requirement) req).getTargetName() != null))
- {
- String pkgName = ((Requirement) req).getTargetName();
- List capList = (List) m_resolvedPkgIndex.get(pkgName);
+ Set<Capability> result = new TreeSet(new CandidateComparator());
- for (int capIdx = 0; (capList != null) && (capIdx < capList.size()); capIdx++)
- {
- ICapability cap = (ICapability) capList.get(capIdx);
- if (req.isSatisfied(cap))
- {
- if (System.getSecurityManager() != null)
- {
- if (reqModule != ((ICapability) capList.get(capIdx)).getModule())
- {
- if ((!((BundleProtectionDomain)((ICapability)
- capList.get(capIdx)).getModule().getSecurityContext()).impliesDirect(
- new PackagePermission(((Requirement) req).getTargetName(), PackagePermission.EXPORTONLY))) ||
- !((reqModule == null) ||
- ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
- new PackagePermission(((Requirement) req).getTargetName(), ((ICapability)
- capList.get(capIdx)).getModule().getBundle(),PackagePermission.IMPORT))
- ))
- {
- continue;
- }
- }
- }
- candidates.add(cap);
- }
- }
- }
- else
+ if (req.getNamespace().equals(Capability.MODULE_NAMESPACE))
{
- Iterator i = m_resolvedCapMap.entrySet().iterator();
- while (i.hasNext())
- {
- Map.Entry entry = (Map.Entry) i.next();
- IModule module = (IModule) entry.getKey();
- List caps = (List) entry.getValue();
- for (int capIdx = 0; (caps != null) && (capIdx < caps.size()); capIdx++)
- {
- ICapability cap = (ICapability) caps.get(capIdx);
- if (req.isSatisfied(cap))
- {
- if (System.getSecurityManager() != null)
- {
- if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) && (
- !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
- new PackagePermission((String) cap.getProperties().get(ICapability.PACKAGE_PROPERTY), PackagePermission.EXPORTONLY)) ||
- !((reqModule == null) ||
- ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
- new PackagePermission((String) cap.getProperties().get(ICapability.PACKAGE_PROPERTY), cap.getModule().getBundle(),PackagePermission.IMPORT))
- )))
- {
- if (reqModule != cap.getModule())
- {
- continue;
- }
- }
- if (req.getNamespace().equals(ICapability.MODULE_NAMESPACE) && (
- !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
- new BundlePermission(cap.getModule().getSymbolicName(), BundlePermission.PROVIDE)) ||
- !((reqModule == null) ||
- ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
- new BundlePermission(reqModule.getSymbolicName(), BundlePermission.REQUIRE))
- )))
- {
- continue;
- }
- }
- candidates.add(cap);
- }
- }
- }
+ result.addAll(m_modCapSet.match(req.getFilter(), obeyMandatory));
}
- Collections.sort(candidates);
- return candidates;
- }
-
- public synchronized List getUnresolvedCandidates(IRequirement req, IModule reqModule)
- {
- // Get all matching unresolved capabilities.
- List candidates = new ArrayList();
- if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
- (((Requirement) req).getTargetName() != null))
+ else if (req.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
{
- List capList = (List) m_unresolvedPkgIndex.get(((Requirement) req).getTargetName());
- for (int capIdx = 0; (capList != null) && (capIdx < capList.size()); capIdx++)
- {
- // If compatible and it is not currently resolved, then add
- // the unresolved candidate to the list.
- if (req.isSatisfied((ICapability) capList.get(capIdx)))
- {
- if (System.getSecurityManager() != null)
- {
- if (reqModule != ((ICapability) capList.get(capIdx)).getModule())
- {
- if (!((BundleProtectionDomain)((ICapability)
- capList.get(capIdx)).getModule().getSecurityContext()).impliesDirect(
- new PackagePermission(((Requirement) req).getTargetName(), PackagePermission.EXPORTONLY)) ||
- !((reqModule == null) ||
- ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
- new PackagePermission(((Requirement) req).getTargetName(), ((ICapability)
- capList.get(capIdx)).getModule().getBundle(),PackagePermission.IMPORT))
- ))
- {
- continue;
- }
- }
- }
- candidates.add(capList.get(capIdx));
- }
- }
- }
- else
- {
- IModule[] modules = getModules();
- for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
- {
- // Get the module's export package for the target package.
- ICapability cap = Util.getSatisfyingCapability(modules[modIdx], req);
- // If compatible and it is not currently resolved, then add
- // the unresolved candidate to the list.
- if ((cap != null) && !modules[modIdx].isResolved())
- {
- if (System.getSecurityManager() != null)
- {
- if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) && (
- !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
- new PackagePermission((String) cap.getProperties().get(ICapability.PACKAGE_PROPERTY), PackagePermission.EXPORTONLY)) ||
- !((reqModule == null) ||
- ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
- new PackagePermission((String) cap.getProperties().get(ICapability.PACKAGE_PROPERTY), cap.getModule().getBundle(),PackagePermission.IMPORT))
- )))
- {
- if (reqModule != cap.getModule())
- {
- continue;
- }
- }
- if (req.getNamespace().equals(ICapability.MODULE_NAMESPACE) && (
- !((BundleProtectionDomain) cap.getModule().getSecurityContext()).impliesDirect(
- new BundlePermission(cap.getModule().getSymbolicName(), BundlePermission.PROVIDE)) ||
- !((reqModule == null) ||
- ((BundleProtectionDomain) reqModule.getSecurityContext()).impliesDirect(
- new BundlePermission(reqModule.getSymbolicName(), BundlePermission.REQUIRE))
- )))
- {
- continue;
- }
- }
- candidates.add(cap);
- }
- }
+ result.addAll(m_pkgCapSet.match(req.getFilter(), obeyMandatory));
}
- // Create list of compatible providers.
- Collections.sort(candidates);
- return candidates;
+ return result;
}
//
// Utility methods.
//
- private void indexPackageCapability(Map map, ICapability capability)
+ /**
+ * Returns true if the specified module is a singleton
+ * (i.e., directive singleton:=true in Bundle-SymbolicName).
+ *
+ * @param module the module to check for singleton status.
+ * @return true if the module is a singleton, false otherwise.
+ **/
+ private static boolean isSingleton(Module module)
{
- if (capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ final List<Capability> modCaps =
+ Util.getCapabilityByNamespace(
+ module, Capability.MODULE_NAMESPACE);
+ if (modCaps == null || modCaps.size() == 0)
{
- String pkgName = (String)
- capability.getProperties().get(ICapability.PACKAGE_PROPERTY);
- List capList = (List) map.get(pkgName);
-
- // We want to add the capability into the list of exporters
- // in sorted order (descending version and ascending bundle
- // identifier). Insert using a simple binary search algorithm.
- if (capList == null)
- {
- capList = new ArrayList();
- capList.add(capability);
- }
- else
- {
- Version version = (Version)
- capability.getProperties().get(ICapability.VERSION_PROPERTY);
- Version middleVersion = null;
- int top = 0, bottom = capList.size() - 1, middle = 0;
- while (top <= bottom)
- {
- middle = (bottom - top) / 2 + top;
- middleVersion = (Version)
- ((ICapability) capList.get(middle))
- .getProperties().get(ICapability.VERSION_PROPERTY);
- // Sort in reverse version order.
- int cmp = middleVersion.compareTo(version);
- if (cmp < 0)
- {
- bottom = middle - 1;
- }
- else if (cmp == 0)
- {
- // Sort further by ascending bundle ID.
- long middleId = ((ICapability) capList.get(middle))
- .getModule().getBundle().getBundleId();
- long exportId = capability.getModule().getBundle().getBundleId();
- if (middleId < exportId)
- {
- top = middle + 1;
- }
- else
- {
- bottom = middle - 1;
- }
- }
- else
- {
- top = middle + 1;
- }
- }
-
- // Ignore duplicates.
- if ((top >= capList.size()) || (capList.get(top) != capability))
- {
- capList.add(top, capability);
- }
- }
-
- map.put(pkgName, capList);
+ return false;
}
+ final List<Directive> dirs = modCaps.get(0).getDirectives();
+ for (int dirIdx = 0; (dirs != null) && (dirIdx < dirs.size()); dirIdx++)
+ {
+ if (dirs.get(dirIdx).getName().equalsIgnoreCase(Constants.SINGLETON_DIRECTIVE)
+ && Boolean.valueOf((String) dirs.get(dirIdx).getValue()).booleanValue())
+ {
+ return true;
+ }
+ }
+ return false;
}
- private IModule indexFragment(Map map, IModule module)
+ private static Module indexModule(Map map, Module module)
{
List modules = (List) map.get(module.getSymbolicName());
@@ -1324,7 +839,7 @@
while (top <= bottom)
{
middle = (bottom - top) / 2 + top;
- middleVersion = ((IModule) modules.get(middle)).getVersion();
+ middleVersion = ((Module) modules.get(middle)).getVersion();
// Sort in reverse version order.
int cmp = middleVersion.compareTo(version);
if (cmp < 0)
@@ -1334,7 +849,7 @@
else if (cmp == 0)
{
// Sort further by ascending bundle ID.
- long middleId = ((IModule) modules.get(middle)).getBundle().getBundleId();
+ long middleId = ((Module) modules.get(middle)).getBundle().getBundleId();
long exportId = module.getBundle().getBundleId();
if (middleId < exportId)
{
@@ -1360,6 +875,6 @@
map.put(module.getSymbolicName(), modules);
- return (IModule) modules.get(0);
+ return (Module) modules.get(0);
}
}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
index bb8b007..12aab90 100644
--- a/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
@@ -18,8 +18,9 @@
*/
package org.apache.felix.framework;
-import org.apache.felix.framework.searchpolicy.*;
-import org.apache.felix.moduleloader.*;
+import org.apache.felix.framework.resolver.ResourceNotFoundException;
+import org.apache.felix.framework.resolver.Content;
+import org.apache.felix.framework.capabilityset.SimpleFilter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
@@ -31,7 +32,7 @@
import java.security.ProtectionDomain;
import java.security.SecureClassLoader;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
@@ -42,29 +43,37 @@
import java.util.Vector;
import org.apache.felix.framework.Felix.FelixResolver;
import org.apache.felix.framework.cache.JarContent;
+import org.apache.felix.framework.capabilityset.Attribute;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.Directive;
+import org.apache.felix.framework.resolver.Module;
+import org.apache.felix.framework.capabilityset.Requirement;
+import org.apache.felix.framework.resolver.Wire;
+import org.apache.felix.framework.resolver.ResolveException;
+import org.apache.felix.framework.resolver.WireImpl;
+import org.apache.felix.framework.resolver.WireModuleImpl;
import org.apache.felix.framework.util.CompoundEnumeration;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.SecureAction;
import org.apache.felix.framework.util.SecurityManagerEx;
import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.util.manifestparser.Capability;
+import org.apache.felix.framework.util.manifestparser.CapabilityImpl;
import org.apache.felix.framework.util.manifestparser.ManifestParser;
import org.apache.felix.framework.util.manifestparser.R4Library;
-import org.apache.felix.framework.util.manifestparser.Requirement;
+import org.apache.felix.framework.util.manifestparser.RequirementImpl;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.BundleReference;
import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.Version;
-public class ModuleImpl implements IModule
+public class ModuleImpl implements Module
{
private final Logger m_logger;
private final Map m_configMap;
private final FelixResolver m_resolver;
private final String m_id;
- private final IContent m_content;
+ private final Content m_content;
private final Map m_headerMap;
private final URLStreamHandler m_streamHandler;
@@ -73,28 +82,28 @@
private final String m_symbolicName;
private final Version m_version;
- private final ICapability[] m_capabilities;
- private ICapability[] m_cachedCapabilities = null;
- private final IRequirement[] m_requirements;
- private IRequirement[] m_cachedRequirements = null;
- private final IRequirement[] m_dynamicRequirements;
- private IRequirement[] m_cachedDynamicRequirements = null;
- private final R4Library[] m_nativeLibraries;
+ private final List<Capability> m_capabilities;
+ private List<Capability> m_cachedCapabilities = null;
+ private final List<Requirement> m_requirements;
+ private List<Requirement> m_cachedRequirements = null;
+ private final List<Requirement> m_dynamicRequirements;
+ private List<Requirement> m_cachedDynamicRequirements = null;
+ private final List<R4Library> m_nativeLibraries;
private final int m_declaredActivationPolicy;
- private final String[] m_activationIncludes;
- private final String[] m_activationExcludes;
+ private final List<String> m_activationIncludes;
+ private final List<String> m_activationExcludes;
private final Bundle m_bundle;
- private IModule[] m_fragments = null;
- private IWire[] m_wires = null;
- private IModule[] m_dependentHosts = new IModule[0];
- private IModule[] m_dependentImporters = new IModule[0];
- private IModule[] m_dependentRequirers = new IModule[0];
+ private List<Module> m_fragments = null;
+ private List<Wire> m_wires = null;
+ private List<Module> m_dependentHosts = new ArrayList<Module>(0);
+ private List<Module> m_dependentImporters = new ArrayList<Module>(0);
+ private List<Module> m_dependentRequirers = new ArrayList<Module>(0);
private volatile boolean m_isResolved = false;
- private IContent[] m_contentPath;
- private IContent[] m_fragmentContents = null;
+ private Content[] m_contentPath;
+ private Content[] m_fragmentContents = null;
private ModuleClassLoader m_classLoader;
private boolean m_isActivationTriggered = false;
private ProtectionDomain m_protectionDomain = null;
@@ -184,7 +193,7 @@
public ModuleImpl(
Logger logger, Map configMap, FelixResolver resolver,
- Bundle bundle, String id, Map headerMap, IContent content,
+ Bundle bundle, String id, Map headerMap, Content content,
URLStreamHandler streamHandler, String[] bootPkgs,
boolean[] bootPkgWildcards)
throws BundleException
@@ -269,163 +278,110 @@
return m_version;
}
- public synchronized ICapability[] getCapabilities()
+ public synchronized List<Capability> getCapabilities()
{
if (m_cachedCapabilities == null)
{
List capList = (m_capabilities == null)
- ? new ArrayList() : new ArrayList(Arrays.asList(m_capabilities));
+ ? new ArrayList<Capability>()
+ : new ArrayList<Capability>(m_capabilities);
for (int fragIdx = 0;
- (m_fragments != null) && (fragIdx < m_fragments.length);
+ (m_fragments != null) && (fragIdx < m_fragments.size());
fragIdx++)
{
- ICapability[] caps = m_fragments[fragIdx].getCapabilities();
+ List<Capability> caps = m_fragments.get(fragIdx).getCapabilities();
for (int capIdx = 0;
- (caps != null) && (capIdx < caps.length);
+ (caps != null) && (capIdx < caps.size());
capIdx++)
{
- if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ if (caps.get(capIdx).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
{
capList.add(
- new Capability(
+ new CapabilityImpl(
this,
- caps[capIdx].getNamespace(),
- ((Capability) caps[capIdx]).getDirectives(),
- ((Capability) caps[capIdx]).getAttributes()));
+ caps.get(capIdx).getNamespace(),
+ caps.get(capIdx).getDirectives(),
+ caps.get(capIdx).getAttributes()));
}
}
}
- m_cachedCapabilities = (ICapability[])
- capList.toArray(new ICapability[capList.size()]);
+ m_cachedCapabilities = Collections.unmodifiableList(capList);
}
return m_cachedCapabilities;
}
- public synchronized IRequirement[] getRequirements()
+ public synchronized List<Requirement> getRequirements()
{
if (m_cachedRequirements == null)
{
- List allReqs = new ArrayList();
- Map pkgMap = new HashMap();
- Map rbMap = new HashMap();
- for (int i = 0; (m_requirements != null) && i < m_requirements.length; i++)
- {
- if (m_requirements[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- pkgMap.put(
- ((Requirement) m_requirements[i]).getTargetName(),
- m_requirements[i]);
- }
- else if (m_requirements[i].getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- rbMap.put(
- ((Requirement) m_requirements[i]).getTargetName(),
- m_requirements[i]);
- }
- else
- {
- allReqs.add(m_requirements[i]);
- }
- }
-
- // Aggregate host and fragment bundle and package requirements.
+ List<Requirement> reqList = (m_requirements == null)
+ ? new ArrayList() : new ArrayList(m_requirements);
for (int fragIdx = 0;
- (m_fragments != null) && (fragIdx < m_fragments.length);
+ (m_fragments != null) && (fragIdx < m_fragments.size());
fragIdx++)
{
- IRequirement[] reqs = m_fragments[fragIdx].getRequirements();
+ List<Requirement> reqs = m_fragments.get(fragIdx).getRequirements();
for (int reqIdx = 0;
- (reqs != null) && (reqIdx < reqs.length);
+ (reqs != null) && (reqIdx < reqs.size());
reqIdx++)
{
- if (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ if (reqs.get(reqIdx).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
+ || reqs.get(reqIdx).getNamespace().equals(Capability.MODULE_NAMESPACE))
{
- // If the current fragment requirement overlaps a previously
- // added requirement, then calculate a new intersecting requirement.
- Requirement req = (Requirement) pkgMap.get(
- ((Requirement) reqs[reqIdx]).getTargetName());
- if (req != null)
- {
- req = FelixResolverState.calculateVersionIntersection(
- req, (Requirement) reqs[reqIdx]);
- }
- else
- {
- req = (Requirement) reqs[reqIdx];
- }
- pkgMap.put(req.getTargetName(), req);
- }
- else if (reqs[reqIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- // If the current fragment requirement overlaps a previously
- // added requirement, then calculate a new intersecting requirement.
- Requirement req = (Requirement) pkgMap.get(
- ((Requirement) reqs[reqIdx]).getTargetName());
- if (req != null)
- {
- req = FelixResolverState.calculateVersionIntersection(
- req, (Requirement) reqs[reqIdx]);
- }
- else
- {
- req = (Requirement) reqs[reqIdx];
- }
- rbMap.put(req.getTargetName(), req);
+ reqList.add(
+ new FragmentRequirement(
+ reqs.get(reqIdx), m_fragments.get(fragIdx)));
}
}
}
- allReqs.addAll(pkgMap.values());
- allReqs.addAll(rbMap.values());
- m_cachedRequirements = (IRequirement[])
- allReqs.toArray(new IRequirement[allReqs.size()]);
+ m_cachedRequirements = Collections.unmodifiableList(reqList);
}
return m_cachedRequirements;
}
- public synchronized IRequirement[] getDynamicRequirements()
+ public synchronized List<Requirement> getDynamicRequirements()
{
if (m_cachedDynamicRequirements == null)
{
- List reqList = (m_dynamicRequirements == null)
- ? new ArrayList() : new ArrayList(Arrays.asList(m_dynamicRequirements));
+ List<Requirement> reqList = (m_dynamicRequirements == null)
+ ? new ArrayList() : new ArrayList(m_dynamicRequirements);
for (int fragIdx = 0;
- (m_fragments != null) && (fragIdx < m_fragments.length);
+ (m_fragments != null) && (fragIdx < m_fragments.size());
fragIdx++)
{
- IRequirement[] reqs = m_fragments[fragIdx].getDynamicRequirements();
+ List<Requirement> reqs = m_fragments.get(fragIdx).getDynamicRequirements();
for (int reqIdx = 0;
- (reqs != null) && (reqIdx < reqs.length);
+ (reqs != null) && (reqIdx < reqs.size());
reqIdx++)
{
- if (reqs[reqIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ if (reqs.get(reqIdx).getNamespace().equals(Capability.PACKAGE_NAMESPACE))
{
- reqList.add(reqs[reqIdx]);
+ reqList.add(reqs.get(reqIdx));
}
}
}
- m_cachedDynamicRequirements = (IRequirement[])
- reqList.toArray(new IRequirement[reqList.size()]);
+ m_cachedDynamicRequirements = Collections.unmodifiableList(reqList);
}
return m_cachedDynamicRequirements;
}
- public synchronized R4Library[] getNativeLibraries()
+ public synchronized List<R4Library> getNativeLibraries()
{
- R4Library[] result = null;
+ List<R4Library> result = null;
if (m_isResolved)
{
- List nativeList = (m_nativeLibraries == null)
- ? new ArrayList() : new ArrayList(Arrays.asList(m_nativeLibraries));
+ List<R4Library> nativeList = (m_nativeLibraries == null)
+ ? new ArrayList() : new ArrayList(m_nativeLibraries);
for (int fragIdx = 0;
- (m_fragments != null) && (fragIdx < m_fragments.length);
+ (m_fragments != null) && (fragIdx < m_fragments.size());
fragIdx++)
{
- R4Library[] libs = m_fragments[fragIdx].getNativeLibraries();
+ List<R4Library> libs = m_fragments.get(fragIdx).getNativeLibraries();
for (int reqIdx = 0;
- (libs != null) && (reqIdx < libs.length);
+ (libs != null) && (reqIdx < libs.size());
reqIdx++)
{
- nativeList.add(libs[reqIdx]);
+ nativeList.add(libs.get(reqIdx));
}
}
@@ -434,7 +390,7 @@
// could not be found when resolving the bundle.
result = (nativeList.size() == 0)
? null
- : (R4Library[]) nativeList.toArray(new R4Library[nativeList.size()]);
+ : Collections.unmodifiableList(nativeList);
}
else
{
@@ -465,20 +421,20 @@
// by default, otherwise try to find one match.
boolean included = (m_activationIncludes == null);
for (int i = 0;
- (!included) && (m_activationIncludes != null) && (i < m_activationIncludes.length);
+ (!included) && (m_activationIncludes != null) && (i < m_activationIncludes.size());
i++)
{
- included = m_activationIncludes[i].equals(pkgName);
+ included = m_activationIncludes.get(i).equals(pkgName);
}
// If there are no exclude filters then no classes are excluded
// by default, otherwise try to find one match.
boolean excluded = false;
for (int i = 0;
- (!excluded) && (m_activationExcludes != null) && (i < m_activationExcludes.length);
+ (!excluded) && (m_activationExcludes != null) && (i < m_activationExcludes.size());
i++)
{
- excluded = m_activationExcludes[i].equals(pkgName);
+ excluded = m_activationExcludes.get(i).equals(pkgName);
}
return included && !excluded;
}
@@ -497,40 +453,40 @@
return m_id;
}
- public synchronized IWire[] getWires()
+ public synchronized List<Wire> getWires()
{
return m_wires;
}
- public synchronized void setWires(IWire[] wires)
+ public synchronized void setWires(List<Wire> wires)
{
// Remove module from old wire modules' dependencies,
// since we are no longer dependent on any the moduels
// from the old wires.
- for (int i = 0; (m_wires != null) && (i < m_wires.length); i++)
+ for (int i = 0; (m_wires != null) && (i < m_wires.size()); i++)
{
- if (m_wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ if (m_wires.get(i).getCapability().getNamespace().equals(Capability.MODULE_NAMESPACE))
{
- ((ModuleImpl) m_wires[i].getExporter()).removeDependentRequirer(this);
+ ((ModuleImpl) m_wires.get(i).getExporter()).removeDependentRequirer(this);
}
- else if (m_wires[i].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ else if (m_wires.get(i).getCapability().getNamespace().equals(Capability.PACKAGE_NAMESPACE))
{
- ((ModuleImpl) m_wires[i].getExporter()).removeDependentImporter(this);
+ ((ModuleImpl) m_wires.get(i).getExporter()).removeDependentImporter(this);
}
}
m_wires = wires;
// Add ourself as a dependent to the new wires' modules.
- for (int i = 0; (m_wires != null) && (i < m_wires.length); i++)
+ for (int i = 0; (m_wires != null) && (i < m_wires.size()); i++)
{
- if (m_wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
+ if (m_wires.get(i).getCapability().getNamespace().equals(Capability.MODULE_NAMESPACE))
{
- ((ModuleImpl) m_wires[i].getExporter()).addDependentRequirer(this);
+ ((ModuleImpl) m_wires.get(i).getExporter()).addDependentRequirer(this);
}
- else if (m_wires[i].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+ else if (m_wires.get(i).getCapability().getNamespace().equals(Capability.PACKAGE_NAMESPACE))
{
- ((ModuleImpl) m_wires[i].getExporter()).addDependentImporter(this);
+ ((ModuleImpl) m_wires.get(i).getExporter()).addDependentImporter(this);
}
}
}
@@ -549,12 +505,12 @@
// Content access methods.
//
- public IContent getContent()
+ public Content getContent()
{
return m_content;
}
- private synchronized IContent[] getContentPath()
+ private synchronized Content[] getContentPath()
{
if (m_contentPath == null)
{
@@ -570,19 +526,19 @@
return m_contentPath;
}
- private IContent[] initializeContentPath() throws Exception
+ private Content[] initializeContentPath() throws Exception
{
List contentList = new ArrayList();
calculateContentPath(this, m_content, contentList, true);
for (int i = 0; (m_fragmentContents != null) && (i < m_fragmentContents.length); i++)
{
- calculateContentPath(m_fragments[i], m_fragmentContents[i], contentList, false);
+ calculateContentPath(m_fragments.get(i), m_fragmentContents[i], contentList, false);
}
- return (IContent[]) contentList.toArray(new IContent[contentList.size()]);
+ return (Content[]) contentList.toArray(new Content[contentList.size()]);
}
private List calculateContentPath(
- IModule module, IContent content, List contentList, boolean searchFragments)
+ Module module, Content content, List contentList, boolean searchFragments)
throws Exception
{
// Creating the content path entails examining the bundle's
@@ -596,25 +552,25 @@
// Find class path meta-data.
String classPath = (String) module.getHeaders().get(FelixConstants.BUNDLE_CLASSPATH);
// Parse the class path into strings.
- String[] classPathStrings = ManifestParser.parseDelimitedString(
+ List<String> classPathStrings = ManifestParser.parseDelimitedString(
classPath, FelixConstants.CLASS_PATH_SEPARATOR);
if (classPathStrings == null)
{
- classPathStrings = new String[0];
+ classPathStrings = new ArrayList<String>(0);
}
// Create the bundles class path.
- for (int i = 0; i < classPathStrings.length; i++)
+ for (int i = 0; i < classPathStrings.size(); i++)
{
// Remove any leading slash, since all bundle class path
// entries are relative to the root of the bundle.
- classPathStrings[i] = (classPathStrings[i].startsWith("/"))
- ? classPathStrings[i].substring(1)
- : classPathStrings[i];
+ classPathStrings.set(i, (classPathStrings.get(i).startsWith("/"))
+ ? classPathStrings.get(i).substring(1)
+ : classPathStrings.get(i));
// Check for the bundle itself on the class path.
- if (classPathStrings[i].equals(FelixConstants.CLASS_PATH_DOT))
+ if (classPathStrings.get(i).equals(FelixConstants.CLASS_PATH_DOT))
{
localContentList.add(content);
}
@@ -622,7 +578,7 @@
{
// Try to find the embedded class path entry in the current
// content.
- IContent embeddedContent = content.getEntryAsContent(classPathStrings[i]);
+ Content embeddedContent = content.getEntryAsContent(classPathStrings.get(i));
// If the embedded class path entry was not found, it might be
// in one of the fragments if the current content is the bundle,
// so try to search the fragments if necessary.
@@ -631,7 +587,8 @@
&& (m_fragmentContents != null) && (fragIdx < m_fragmentContents.length);
fragIdx++)
{
- embeddedContent = m_fragmentContents[fragIdx].getEntryAsContent(classPathStrings[i]);
+ embeddedContent =
+ m_fragmentContents[fragIdx].getEntryAsContent(classPathStrings.get(i));
}
// If we found the embedded content, then add it to the
// class path content list.
@@ -645,7 +602,7 @@
// need to create an "Eventer" class like "Logger" perhaps.
m_logger.log(Logger.LOG_INFO,
"Class path entry not found: "
- + classPathStrings[i]);
+ + classPathStrings.get(i));
}
}
}
@@ -840,7 +797,7 @@
}
// Check the module class path.
- IContent[] contentPath = getContentPath();
+ Content[] contentPath = getContentPath();
for (int i = 0;
(url == null) &&
(i < contentPath.length); i++)
@@ -936,15 +893,15 @@
// Note that the search may be aborted if this method throws an
// exception, otherwise it continues if a null is returned.
- IWire[] wires = getWires();
- for (int i = 0; (wires != null) && (i < wires.length); i++)
+ List<Wire> wires = getWires();
+ for (int i = 0; (wires != null) && (i < wires.size()); i++)
{
- if (wires[i] instanceof R4Wire)
+ if (wires.get(i) instanceof WireImpl)
{
try
{
// If we find the class or resource, then return it.
- urls = wires[i].getResources(name);
+ urls = wires.get(i).getResources(name);
}
catch (ResourceNotFoundException ex)
{
@@ -962,14 +919,14 @@
// See whether we can get the resource from the required bundles and
// regardless of whether or not this is the case continue to the next
// step potentially passing on the result of this search (if any).
- for (int i = 0; (wires != null) && (i < wires.length); i++)
+ for (int i = 0; (wires != null) && (i < wires.size()); i++)
{
- if (wires[i] instanceof R4WireModule)
+ if (wires.get(i) instanceof WireModuleImpl)
{
try
{
// If we find the class or resource, then add it.
- urls = wires[i].getResources(name);
+ urls = wires.get(i).getResources(name);
}
catch (ResourceNotFoundException ex)
{
@@ -996,10 +953,10 @@
// At this point, the module's imports were searched and so was the
// the module's content. Now we make an attempt to load the
// class/resource via a dynamic import, if possible.
- IWire wire = null;
+ Wire wire = null;
try
{
- wire = m_resolver.resolveDynamicImport(this, pkgName);
+ wire = m_resolver.resolve(this, pkgName);
}
catch (ResolveException ex)
{
@@ -1033,7 +990,7 @@
// Special case "/" so that it returns a root URLs for
// each bundle class path entry...this isn't very
// clean or meaningful, but the Spring guys want it.
- final IContent[] contentPath = getContentPath();
+ final Content[] contentPath = getContentPath();
if (name.equals("/"))
{
for (int i = 0; i < contentPath.length; i++)
@@ -1139,8 +1096,8 @@
try
{
- return m_secureAction.createURL(null,
- FelixConstants.BUNDLE_URL_PROTOCOL + "://" +
+ return m_secureAction.createURL(null,
+ FelixConstants.BUNDLE_URL_PROTOCOL + "://" +
m_id + ":" + port + path, m_streamHandler);
}
catch (MalformedURLException ex)
@@ -1157,19 +1114,19 @@
// Fragment and dependency management methods.
//
- public synchronized IModule[] getFragments()
+ public synchronized List<Module> getFragments()
{
return m_fragments;
}
- public synchronized void attachFragments(IModule[] fragments) throws Exception
+ public synchronized void attachFragments(List<Module> fragments) throws Exception
{
// Remove module from old fragment dependencies.
// We will generally only remove module fragment
// dependencies when we are uninstalling the module.
- for (int i = 0; (m_fragments != null) && (i < m_fragments.length); i++)
+ for (int i = 0; (m_fragments != null) && (i < m_fragments.size()); i++)
{
- ((ModuleImpl) m_fragments[i]).removeDependentHost(this);
+ ((ModuleImpl) m_fragments.get(i)).removeDependentHost(this);
}
// Remove cached capabilities and requirements.
@@ -1185,12 +1142,12 @@
// to attach to our content loader.
if (m_fragments != null)
{
- IContent[] fragmentContents = new IContent[m_fragments.length];
- for (int i = 0; (m_fragments != null) && (i < m_fragments.length); i++)
+ Content[] fragmentContents = new Content[m_fragments.size()];
+ for (int i = 0; (m_fragments != null) && (i < m_fragments.size()); i++)
{
- ((ModuleImpl) m_fragments[i]).addDependentHost(this);
+ ((ModuleImpl) m_fragments.get(i)).addDependentHost(this);
fragmentContents[i] =
- m_fragments[i].getContent()
+ m_fragments.get(i).getContent()
.getEntryAsContent(FelixConstants.CLASS_PATH_DOT);
}
// Now attach the fragment contents to our content loader.
@@ -1199,7 +1156,7 @@
}
// This must be called holding the object lock.
- private void attachFragmentContents(IContent[] fragmentContents)
+ private void attachFragmentContents(Content[] fragmentContents)
throws Exception
{
// Close existing fragment contents.
@@ -1222,123 +1179,70 @@
m_contentPath = initializeContentPath();
}
- public synchronized IModule[] getDependentHosts()
+ public synchronized List<Module> getDependentHosts()
{
return m_dependentHosts;
}
- public synchronized void addDependentHost(IModule module)
+ public synchronized void addDependentHost(Module module)
{
- m_dependentHosts = addDependent(m_dependentHosts, module);
+ if (!m_dependentHosts.contains(module))
+ {
+ m_dependentHosts.add(module);
+ }
}
- public synchronized void removeDependentHost(IModule module)
+ public synchronized void removeDependentHost(Module module)
{
- m_dependentHosts = removeDependent(m_dependentHosts, module);
+ m_dependentHosts.remove(module);
}
- public synchronized IModule[] getDependentImporters()
+ public synchronized List<Module> getDependentImporters()
{
return m_dependentImporters;
}
- public synchronized void addDependentImporter(IModule module)
+ public synchronized void addDependentImporter(Module module)
{
- m_dependentImporters = addDependent(m_dependentImporters, module);
+ if (!m_dependentImporters.contains(module))
+ {
+ m_dependentImporters.add(module);
+ }
}
- public synchronized void removeDependentImporter(IModule module)
+ public synchronized void removeDependentImporter(Module module)
{
- m_dependentImporters = removeDependent(m_dependentImporters, module);
+ m_dependentImporters.remove(module);
}
- public synchronized IModule[] getDependentRequirers()
+ public synchronized List<Module> getDependentRequirers()
{
return m_dependentRequirers;
}
- public synchronized void addDependentRequirer(IModule module)
+ public synchronized void addDependentRequirer(Module module)
{
- m_dependentRequirers = addDependent(m_dependentRequirers, module);
+ if (!m_dependentRequirers.contains(module))
+ {
+ m_dependentRequirers.add(module);
+ }
}
- public synchronized void removeDependentRequirer(IModule module)
+ public synchronized void removeDependentRequirer(Module module)
{
- m_dependentRequirers = removeDependent(m_dependentRequirers, module);
+ m_dependentRequirers.remove(module);
}
- public synchronized IModule[] getDependents()
+ public synchronized List<Module> getDependents()
{
- IModule[] dependents = new IModule[
- m_dependentHosts.length + m_dependentImporters.length + m_dependentRequirers.length];
- System.arraycopy(
- m_dependentHosts,
- 0,
- dependents,
- 0,
- m_dependentHosts.length);
- System.arraycopy(
- m_dependentImporters,
- 0,
- dependents,
- m_dependentHosts.length,
- m_dependentImporters.length);
- System.arraycopy(
- m_dependentRequirers,
- 0,
- dependents,
- m_dependentHosts.length + m_dependentImporters.length,
- m_dependentRequirers.length);
+ List<Module> dependents = new ArrayList<Module>
+ (m_dependentHosts.size() + m_dependentImporters.size() + m_dependentRequirers.size());
+ dependents.addAll(m_dependentHosts);
+ dependents.addAll(m_dependentImporters);
+ dependents.addAll(m_dependentRequirers);
return dependents;
}
- private static IModule[] addDependent(IModule[] modules, IModule module)
- {
- // Make sure the dependent module is not already present.
- for (int i = 0; i < modules.length; i++)
- {
- if (modules[i].equals(module))
- {
- return modules;
- }
- }
- IModule[] tmp = new IModule[modules.length + 1];
- System.arraycopy(modules, 0, tmp, 0, modules.length);
- tmp[modules.length] = module;
- return tmp;
- }
-
- private static IModule[] removeDependent(IModule[] modules, IModule module)
- {
- IModule[] tmp = modules;
-
- // Make sure the dependent module is present.
- for (int i = 0; i < modules.length; i++)
- {
- if (modules[i].equals(module))
- {
- // If this is the module, then point to empty list.
- if ((modules.length - 1) == 0)
- {
- tmp = new IModule[0];
- }
- // Otherwise, we need to do some array copying.
- else
- {
- tmp = new IModule[modules.length - 1];
- System.arraycopy(modules, 0, tmp, 0, i);
- if (i < tmp.length)
- {
- System.arraycopy(modules, i + 1, tmp, i, tmp.length - i);
- }
- }
- break;
- }
- }
-
- return tmp;
- }
-
public synchronized void close()
{
m_content.close();
@@ -1433,13 +1337,13 @@
throws ClassNotFoundException, ResourceNotFoundException
{
// We delegate to the module's wires to find the class or resource.
- IWire[] wires = getWires();
- for (int i = 0; (wires != null) && (i < wires.length); i++)
+ List<Wire> wires = getWires();
+ for (int i = 0; (wires != null) && (i < wires.size()); i++)
{
// If we find the class or resource, then return it.
Object result = (isClass)
- ? (Object) wires[i].getClass(name)
- : (Object) wires[i].getResource(name);
+ ? (Object) wires.get(i).getClass(name)
+ : (Object) wires.get(i).getResource(name);
if (result != null)
{
return result;
@@ -1456,10 +1360,10 @@
// At this point, the module's imports were searched and so was the
// the module's content. Now we make an attempt to load the
// class/resource via a dynamic import, if possible.
- IWire wire = null;
+ Wire wire = null;
try
{
- wire = m_resolver.resolveDynamicImport(this, pkgName);
+ wire = m_resolver.resolve(this, pkgName);
}
catch (ResolveException ex)
{
@@ -1772,8 +1676,8 @@
byte[] bytes = null;
// Check the module class path.
- IContent[] contentPath = getContentPath();
- IContent content = null;
+ Content[] contentPath = getContentPath();
+ Content content = null;
for (int i = 0;
(bytes == null) &&
(i < contentPath.length); i++)
@@ -1799,7 +1703,7 @@
int activationPolicy =
((BundleImpl) getBundle()).isDeclaredActivationPolicyUsed()
? ((BundleImpl) getBundle()).getCurrentModule().getDeclaredActivationPolicy()
- : IModule.EAGER_ACTIVATION;
+ : Module.EAGER_ACTIVATION;
// If the module is using deferred activation, then if
// we load this class from this module we need to activate
@@ -1810,7 +1714,7 @@
? false : isActivationTrigger(pkgName);
if (!m_isActivationTriggered
&& isTriggerClass
- && (activationPolicy == IModule.LAZY_ACTIVATION)
+ && (activationPolicy == Module.LAZY_ACTIVATION)
&& (getBundle().getState() == Bundle.STARTING))
{
List deferredList = (List) m_deferredActivation.get();
@@ -2027,14 +1931,14 @@
// native library.
if (result == null)
{
- R4Library[] libs = getNativeLibraries();
- for (int libIdx = 0; (libs != null) && (libIdx < libs.length); libIdx++)
+ List<R4Library> libs = getNativeLibraries();
+ for (int libIdx = 0; (libs != null) && (libIdx < libs.size()); libIdx++)
{
- if (libs[libIdx].match(m_configMap, name))
+ if (libs.get(libIdx).match(m_configMap, name))
{
// Search bundle content first for native library.
result = getContent().getEntryAsNativeLibrary(
- libs[libIdx].getEntryName());
+ libs.get(libIdx).getEntryName());
// If not found, then search fragments in order.
for (int i = 0;
(result == null) && (m_fragmentContents != null)
@@ -2042,7 +1946,7 @@
i++)
{
result = m_fragmentContents[i].getEntryAsNativeLibrary(
- libs[libIdx].getEntryName());
+ libs.get(libIdx).getEntryName());
}
}
}
@@ -2084,13 +1988,13 @@
String importer = module.getBundle().toString();
// Next, check to see if the module imports the package.
- IWire[] wires = module.getWires();
- for (int i = 0; (wires != null) && (i < wires.length); i++)
+ List<Wire> wires = module.getWires();
+ for (int i = 0; (wires != null) && (i < wires.size()); i++)
{
- if (wires[i].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
- wires[i].getCapability().getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
+ if (wires.get(i).getCapability().getNamespace().equals(Capability.PACKAGE_NAMESPACE) &&
+ wires.get(i).getCapability().getAttribute(Capability.PACKAGE_ATTR).getValue().equals(pkgName))
{
- String exporter = wires[i].getExporter().getBundle().toString();
+ String exporter = wires.get(i).getExporter().getBundle().toString();
StringBuffer sb = new StringBuffer("*** Package '");
sb.append(pkgName);
@@ -2114,7 +2018,7 @@
// Next, check to see if the package was optionally imported and
// whether or not there is an exporter available.
- IRequirement[] reqs = module.getRequirements();
+ List<Requirement> reqs = module.getRequirements();
/*
* TODO: RB - Fix diagnostic message for optional imports.
for (int i = 0; (reqs != null) && (i < reqs.length); i++)
@@ -2174,7 +2078,9 @@
}
*/
// Next, check to see if the package is dynamically imported by the module.
- IRequirement pkgReq = Resolver.findAllowedDynamicImport(module, pkgName);
+// TODO: FELIX3 - Add Resolver.findAllowedDynamicImport().
+/*
+ Requirement pkgReq = Resolver.findAllowedDynamicImport(module, pkgName);
if (pkgReq != null)
{
// Try to see if there is an exporter available.
@@ -2228,22 +2134,14 @@
return sb.toString();
}
-
+*/
// Next, check to see if there are any exporters for the package at all.
- pkgReq = null;
- try
- {
- pkgReq = new Requirement(ICapability.PACKAGE_NAMESPACE, "(package=" + pkgName + ")");
- }
- catch (InvalidSyntaxException ex)
- {
- // This should never happen.
- }
- List exports =
- resolver.getResolvedCandidates(pkgReq, module);
- exports = (exports.size() == 0)
- ? resolver.getUnresolvedCandidates(pkgReq, module)
- : exports;
+ Requirement pkgReq = null;
+ List<Attribute> attrs = new ArrayList(1);
+ attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
+ pkgReq = new RequirementImpl(
+ Capability.PACKAGE_NAMESPACE, new ArrayList<Directive>(0), attrs);
+ Set<Capability> exports = resolver.getCandidates(module, pkgReq, false);
if (exports.size() > 0)
{
boolean classpath = false;
@@ -2261,7 +2159,7 @@
// Ignore
}
- String exporter = ((ICapability) exports.get(0)).getModule().getBundle().toString();
+ String exporter = exports.iterator().next().getModule().getBundle().toString();
StringBuffer sb = new StringBuffer("*** Class '");
sb.append(name);
@@ -2345,4 +2243,46 @@
return sb.toString();
}
-}
+
+ static class FragmentRequirement implements Requirement
+ {
+ private final Requirement m_req;
+ private final Module m_fragment;
+
+ public FragmentRequirement(Requirement req, Module fragment)
+ {
+ m_req = req;
+ m_fragment = fragment;
+ }
+
+ public Module getFragment()
+ {
+ return m_fragment;
+ }
+
+ public String getNamespace()
+ {
+ return m_req.getNamespace();
+ }
+
+ public SimpleFilter getFilter()
+ {
+ return m_req.getFilter();
+ }
+
+ public boolean isOptional()
+ {
+ return m_req.isOptional();
+ }
+
+ public Directive getDirective(String name)
+ {
+ return m_req.getDirective(name);
+ }
+
+ public List<Directive> getDirectives()
+ {
+ return m_req.getDirectives();
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
new file mode 100644
index 0000000..04edb93
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/capabilityset/CapabilitySet.java
@@ -0,0 +1,499 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.capabilityset;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class CapabilitySet
+{
+ private final Map<String, Map<Object, Set<Capability>>> m_indices =
+ new HashMap<String, Map<Object, Set<Capability>>>();
+ private final Set<Capability> m_capList = new HashSet<Capability>();
+
+ public CapabilitySet(List<String> indexProps)
+ {
+ for (int i = 0; (indexProps != null) && (i < indexProps.size()); i++)
+ {
+ m_indices.put(indexProps.get(i), new HashMap<Object, Set<Capability>>());
+ }
+ }
+
+ public void addCapability(Capability cap)
+ {
+ m_capList.add(cap);
+
+ // Index capability.
+ for (Entry<String, Map<Object, Set<Capability>>> entry : m_indices.entrySet())
+ {
+ Attribute capAttr = cap.getAttribute(entry.getKey());
+ if (capAttr != null)
+ {
+ Object capValue = capAttr.getValue();
+ if (capValue.getClass().isArray())
+ {
+ capValue = convertArrayToList(capValue);
+ }
+
+ Map<Object, Set<Capability>> index = entry.getValue();
+
+ if (capValue instanceof Collection)
+ {
+ Collection c = (Collection) capValue;
+ for (Object o : c)
+ {
+ indexCapability(index, cap, o);
+ }
+ }
+ else
+ {
+ indexCapability(index, cap, capValue);
+ }
+ }
+ }
+
+// System.out.println("+++ INDICES " + m_indices);
+ }
+
+ private void indexCapability(
+ Map<Object, Set<Capability>> index, Capability cap, Object capValue)
+ {
+ Set<Capability> caps = index.get(capValue);
+ if (caps == null)
+ {
+ caps = new HashSet<Capability>();
+ index.put(capValue, caps);
+ }
+ caps.add(cap);
+ }
+
+ public void removeCapability(Capability cap)
+ {
+ if (m_capList.remove(cap))
+ {
+ for (Entry<String, Map<Object, Set<Capability>>> entry : m_indices.entrySet())
+ {
+ Attribute capAttr = cap.getAttribute(entry.getKey());
+ if (capAttr != null)
+ {
+ Object capValue = capAttr.getValue();
+ if (capValue.getClass().isArray())
+ {
+ capValue = convertArrayToList(capValue);
+ }
+
+ Map<Object, Set<Capability>> index = entry.getValue();
+
+ if (capValue instanceof Collection)
+ {
+ Collection c = (Collection) capValue;
+ for (Object o : c)
+ {
+ deindexCapability(index, cap, o);
+ }
+ }
+ else
+ {
+ deindexCapability(index, cap, capValue);
+ }
+ }
+ }
+
+// System.out.println("+++ INDICES " + m_indices);
+ }
+ }
+
+ private void deindexCapability(
+ Map<Object, Set<Capability>> index, Capability cap, Object capValue)
+ {
+ Set<Capability> caps = index.get(capValue);
+ caps.remove(cap);
+ if (caps.size() == 0)
+ {
+ index.remove(capValue);
+ }
+ }
+
+ public Set<Capability> match(SimpleFilter sf, boolean obeyMandatory)
+ {
+ Set<Capability> matches = match(m_capList, sf);
+ return (obeyMandatory)
+ ? matchMandatory(matches, sf)
+ : matches;
+ }
+
+ private Set<Capability> match(Set<Capability> caps, SimpleFilter sf)
+ {
+//System.out.println("+++ SF " + sf);
+ Set<Capability> matches = new HashSet<Capability>();
+
+ if (sf.getOperation() == SimpleFilter.AND)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For AND we calculate the intersection of each subfilter.
+ // We can short-circuit the AND operation if there are no
+ // remaining capabilities.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; (caps.size() > 0) && (i < sfs.size()); i++)
+ {
+//System.out.println("+++ REMAINING " + caps);
+ matches = match(caps, sfs.get(i));
+//System.out.println("+++ CURRENT " + matches);
+ caps = matches;
+ }
+ }
+ else if (sf.getOperation() == SimpleFilter.OR)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; i < sfs.size(); i++)
+ {
+ matches.addAll(match(caps, sfs.get(i)));
+ }
+ }
+ else if (sf.getOperation() == SimpleFilter.NOT)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ matches.addAll(caps);
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; i < sfs.size(); i++)
+ {
+ matches.removeAll(match(caps, sfs.get(i)));
+ }
+ }
+ else
+ {
+ Map<Object, Set<Capability>> index = m_indices.get(sf.getName());
+ if ((sf.getOperation() == SimpleFilter.EQ) && (index != null))
+ {
+ Set<Capability> existingCaps = index.get(sf.getValue());
+ if (existingCaps != null)
+ {
+ matches.addAll(existingCaps);
+//System.out.println("NARROWED " + caps.size() + " TO " + existingCaps.size());
+ matches.retainAll(caps);
+ }
+ }
+ else
+ {
+// System.out.println("+++ SEARCHING " + caps.size() + " CAPABILITIES");
+ for (Iterator<Capability> it = caps.iterator(); it.hasNext(); )
+ {
+ Capability cap = it.next();
+ Attribute attr = cap.getAttribute(sf.getName());
+ if (attr != null)
+ {
+ Object lhs = attr.getValue();
+ if (compare(lhs, sf.getValue(), sf.getOperation()))
+ {
+ matches.add(cap);
+ }
+ }
+ }
+ }
+ }
+
+ return matches;
+ }
+
+ public static boolean matches(Capability cap, SimpleFilter sf)
+ {
+ return matchesInternal(cap, sf) && matchMandatory(cap, sf);
+ }
+
+ private static boolean matchesInternal(Capability cap, SimpleFilter sf)
+ {
+ boolean matched = true;
+
+ if (sf.getOperation() == SimpleFilter.AND)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For AND we calculate the intersection of each subfilter.
+ // We can short-circuit the AND operation if there are no
+ // remaining capabilities.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; matched && (i < sfs.size()); i++)
+ {
+ matched = matchesInternal(cap, sfs.get(i));
+ }
+ }
+ else if (sf.getOperation() == SimpleFilter.OR)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ matched = false;
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; !matched && (i < sfs.size()); i++)
+ {
+ matched = matchesInternal(cap, sfs.get(i));
+ }
+ }
+ else if (sf.getOperation() == SimpleFilter.NOT)
+ {
+ // Evaluate each subfilter against the remaining capabilities.
+ // For OR we calculate the union of each subfilter.
+ List<SimpleFilter> sfs = (List<SimpleFilter>) sf.getValue();
+ for (int i = 0; i < sfs.size(); i++)
+ {
+ matched = !(matchesInternal(cap, sfs.get(i)));
+ }
+ }
+ else
+ {
+ matched = false;
+ Attribute attr = cap.getAttribute(sf.getName());
+ if (attr != null)
+ {
+ Object lhs = attr.getValue();
+ matched = compare(lhs, sf.getValue(), sf.getOperation());
+ }
+ }
+
+ return matched;
+ }
+
+ private static Set<Capability> matchMandatory(Set<Capability> caps, SimpleFilter sf)
+ {
+ for (Iterator<Capability> it = caps.iterator(); it.hasNext(); )
+ {
+ Capability cap = it.next();
+ if (!matchMandatory(cap, sf))
+ {
+ it.remove();
+ }
+ }
+ return caps;
+ }
+
+ private static boolean matchMandatory(Capability cap, SimpleFilter sf)
+ {
+ List<Attribute> attrs = cap.getAttributes();
+ for (int attrIdx = 0; attrIdx < attrs.size(); attrIdx++)
+ {
+ if (attrs.get(attrIdx).isMandatory()
+ && !matchMandatory(attrs.get(attrIdx), sf))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean matchMandatory(Attribute attr, SimpleFilter sf)
+ {
+ if ((sf.getName() != null) && sf.getName().equals(attr.getName()))
+ {
+ return true;
+ }
+ else if (sf.getOperation() == SimpleFilter.AND)
+ {
+ List list = (List) sf.getValue();
+ for (int i = 0; i < list.size(); i++)
+ {
+ SimpleFilter sf2 = (SimpleFilter) list.get(i);
+ if ((sf2.getName() != null)
+ && sf2.getName().equals(attr.getName()))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static final Class[] STRING_CLASS = new Class[] { String.class };
+
+ private static boolean compare(Object lhs, Object rhsUnknown, int op)
+ {
+ // If the type is comparable, then we can just return the
+ // result immediately.
+ if (lhs instanceof Comparable)
+ {
+ // The substring operator only works on string values, so if the
+ // lhs is not a string, then do an equality comparison using the
+ // original string containing wildcards.
+ if ((op == SimpleFilter.SUBSTRING) && !(lhs instanceof String))
+ {
+ op = SimpleFilter.EQ;
+ rhsUnknown = SimpleFilter.unparseSubstring((List<String>) rhsUnknown);
+ }
+
+ Object rhs;
+ if (op == SimpleFilter.SUBSTRING)
+ {
+ rhs = rhsUnknown;
+ }
+ else
+ {
+ try
+ {
+ rhs = coerceType(lhs, (String) rhsUnknown);
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+
+ switch (op)
+ {
+ case SimpleFilter.EQ :
+ return (((Comparable) lhs).compareTo(rhs) == 0);
+ case SimpleFilter.GTE :
+ return (((Comparable) lhs).compareTo(rhs) >= 0);
+ case SimpleFilter.LTE :
+ return (((Comparable) lhs).compareTo(rhs) <= 0);
+// case SimpleFilter.APPROX :
+// return compareToApprox(((Comparable) lhs), rhs);
+ case SimpleFilter.SUBSTRING :
+ return SimpleFilter.compareSubstring((String) lhs, (List<String>) rhs);
+ default:
+ throw new RuntimeException(
+ "Unknown comparison operator: " + op);
+ }
+ }
+ // Booleans do not implement comparable, so special case them.
+ else if (lhs instanceof Boolean)
+ {
+ Object rhs;
+ try
+ {
+ rhs = coerceType(lhs, (String) rhsUnknown);
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+
+ switch (op)
+ {
+ case SimpleFilter.EQ :
+ case SimpleFilter.GTE :
+ case SimpleFilter.LTE :
+// case SimpleFilter.APPROX:
+ return (lhs.equals(rhs));
+ default:
+ throw new RuntimeException(
+ "Unknown comparison operator: " + op);
+ }
+ }
+
+ // If the LHS is not a comparable or boolean, check if it is an
+ // array. If so, convert it to a list so we can treat it as a
+ // collection.
+ if (lhs.getClass().isArray())
+ {
+ lhs = convertArrayToList(lhs);
+ }
+
+ // If LHS is a collection, then call compare() on each element
+ // of the collection until a match is found.
+ if (lhs instanceof Collection)
+ {
+ for (Iterator iter = ((Collection) lhs).iterator(); iter.hasNext(); )
+ {
+ if (compare(iter.next(), rhsUnknown, op))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Since we cannot identify the LHS type, then we can only perform
+ // equality comparison.
+ try
+ {
+ return lhs.equals(coerceType(lhs, (String) rhsUnknown));
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+
+ private static Object coerceType(Object lhs, String rhsString) throws Exception
+ {
+ // If the LHS expects a string, then we can just return
+ // the RHS since it is a string.
+ if (lhs.getClass() == rhsString.getClass())
+ {
+ return rhsString;
+ }
+
+ // Try to convert the RHS type to the LHS type by using
+ // the string constructor of the LHS class, if it has one.
+ Object rhs = null;
+ try
+ {
+ // The Character class is a special case, since its constructor
+ // does not take a string, so handle it separately.
+ if (lhs instanceof Character)
+ {
+ rhs = new Character(rhsString.charAt(0));
+ }
+ else
+ {
+ rhs = lhs.getClass()
+ .getConstructor(STRING_CLASS)
+ .newInstance(new Object[] { rhsString });
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new Exception(
+ "Could not instantiate class "
+ + lhs.getClass().getName()
+ + " from string constructor with argument '"
+ + rhsString + "' because " + ex);
+ }
+
+ return rhs;
+ }
+
+ /**
+ * This is an ugly utility method to convert an array of primitives
+ * to an array of primitive wrapper objects. This method simplifies
+ * processing LDAP filters since the special case of primitive arrays
+ * can be ignored.
+ * @param array An array of primitive types.
+ * @return An corresponding array using pritive wrapper objects.
+ **/
+ private static List convertArrayToList(Object array)
+ {
+ int len = Array.getLength(array);
+ List list = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ list.add(Array.get(array, i));
+ }
+ return list;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java b/framework/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java
new file mode 100644
index 0000000..b72ea49
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.Comparator;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+public class CandidateComparator implements Comparator
+{
+ public int compare(Object arg1, Object arg2)
+ {
+ Capability cap1 = (Capability) arg1;
+ Capability cap2 = (Capability) arg2;
+
+ // First check resolved state, since resolved capabilities have priority
+ // over unresolved ones. Compare in reverse order since we want to sort
+ // in descending order.
+ int c = 0;
+ if (cap1.getModule().isResolved() && !cap2.getModule().isResolved())
+ {
+ c = -1;
+ }
+ else if (!cap1.getModule().isResolved() && cap2.getModule().isResolved())
+ {
+ c = 1;
+ }
+
+ // Next compare version numbers.
+ if ((c == 0) && cap1.getNamespace().equals(Capability.MODULE_NAMESPACE))
+ {
+ c = ((Comparable) cap1.getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)
+ .getValue()).compareTo(cap2.getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)
+ .getValue());
+ if (c == 0)
+ {
+ Version v1 = (cap1.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE) == null)
+ ? Version.emptyVersion
+ : (Version) cap1.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE).getValue();
+ Version v2 = (cap2.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE) == null)
+ ? Version.emptyVersion
+ : (Version) cap2.getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE).getValue();
+ // Compare these in reverse order, since we want
+ // highest version to have priority.
+ c = v2.compareTo(v1);
+ }
+ }
+// TODO: PROTO3 RESOLVER - Need to change this to handle arbitrary capabilities
+// that may not have a natural ordering.
+ // Assume everything else is a package capability.
+ else if (c == 0)
+ {
+ c = ((Comparable) cap1.getAttribute(Capability.PACKAGE_ATTR).getValue())
+ .compareTo(cap2.getAttribute(Capability.PACKAGE_ATTR).getValue());
+ if (c == 0)
+ {
+ Version v1 = (cap1.getAttribute(Capability.VERSION_ATTR) == null)
+ ? Version.emptyVersion
+ : (Version) cap1.getAttribute(Capability.VERSION_ATTR).getValue();
+ Version v2 = (cap2.getAttribute(Capability.VERSION_ATTR) == null)
+ ? Version.emptyVersion
+ : (Version) cap2.getAttribute(Capability.VERSION_ATTR).getValue();
+ // Compare these in reverse order, since we want
+ // highest version to have priority.
+ c = v2.compareTo(v1);
+ }
+ }
+
+ // Finally, compare module identity.
+ if (c == 0)
+ {
+ if (cap1.getModule().getBundle().getBundleId() <
+ cap2.getModule().getBundle().getBundleId())
+ {
+ c = -1;
+ }
+ else if (cap1.getModule().getBundle().getBundleId() >
+ cap2.getModule().getBundle().getBundleId())
+ {
+ c = 1;
+ }
+ }
+
+ return c;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/Content.java b/framework/src/main/java/org/apache/felix/framework/resolver/Content.java
new file mode 100644
index 0000000..3e13a19
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Content.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+
+public interface Content
+{
+ /**
+ * <p>
+ * This method must be called when the content is no longer needed so
+ * that any resourses being used (e.g., open files) can be closed. Once
+ * this method is called, the content is no longer usable. If the content
+ * is already closed, then calls on this method should have no effect.
+ * </p>
+ **/
+ void close();
+
+ /**
+ * <p>
+ * This method determines if the specified named entry is contained in
+ * the associated content. The entry name is a relative path with '/'
+ * separators.
+ * </p>
+ * @param name The name of the entry to find.
+ * @return <tt>true</tt> if a corresponding entry was found, <tt>false</tt>
+ * otherwise.
+ **/
+ boolean hasEntry(String name);
+
+ /**
+ * <p>
+ * Returns an enumeration of entry names as <tt>String</tt> objects.
+ * An entry name is a path constructed with '/' as path element
+ * separators and is relative to the root of the content. Entry names
+ * for entries that represent directories should end with the '/'
+ * character.
+ * </p>
+ * @returns An enumeration of entry names or <tt>null</tt>.
+ **/
+ Enumeration getEntries();
+
+ /**
+ * <p>
+ * This method returns the named entry as an array of bytes.
+ * </p>
+ * @param name The name of the entry to retrieve as a byte array.
+ * @return An array of bytes if the corresponding entry was found, <tt>null</tt>
+ * otherwise.
+ **/
+ byte[] getEntryAsBytes(String name);
+
+ /**
+ * <p>
+ * This method returns the named entry as an input stream.
+ * </p>
+ * @param name The name of the entry to retrieve as an input stream.
+ * @return An input stream if the corresponding entry was found, <tt>null</tt>
+ * otherwise.
+ * @throws <tt>java.io.IOException</tt> if any error occurs.
+ **/
+ InputStream getEntryAsStream(String name) throws IOException;
+
+ /**
+ * <p>
+ * This method returns the named entry as an <tt>IContent</tt> Typically,
+ * this method only makes sense for entries that correspond to some form
+ * of aggregated resource (e.g., an embedded JAR file or directory), but
+ * implementations are free to interpret this however makes sense. This method
+ * should return a new <tt>IContent</tt> instance for every invocation and
+ * the caller is responsible for opening and closing the returned content
+ * object.
+ * </p>
+ * @param name The name of the entry to retrieve as an <tt>IContent</tt>.
+ * @return An <tt>IContent</tt> instance if a corresponding entry was found,
+ * <tt>null</tt> otherwise.
+ **/
+ Content getEntryAsContent(String name);
+
+ /**
+ * <p>
+ * This method returns the named entry as a file in the file system for
+ * use as a native library. It may not be possible for all content
+ * implementations (e.g., memory only) to implement this method, in which
+ * case it is acceptable to return <tt>null</tt>. Since native libraries
+ * can only be associated with a single class loader, this method should
+ * return a unique file per request.
+ * </p>
+ * @param name The name of the entry to retrieve as a file.
+ * @return A string corresponding to the absolute path of the file if a
+ * corresponding entry was found, <tt>null</tt> otherwise.
+ **/
+ String getEntryAsNativeLibrary(String name);
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/Module.java b/framework/src/main/java/org/apache/felix/framework/resolver/Module.java
new file mode 100644
index 0000000..25258e9
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Module.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.Requirement;
+import org.apache.felix.framework.util.manifestparser.R4Library;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Version;
+
+public interface Module
+{
+ final static int EAGER_ACTIVATION = 0;
+ final static int LAZY_ACTIVATION = 1;
+
+ // Metadata access methods.
+ Map getHeaders();
+ boolean isExtension();
+ String getSymbolicName();
+ Version getVersion();
+ List<Capability> getCapabilities();
+ List<Requirement> getRequirements();
+ List<Requirement> getDynamicRequirements();
+ List<R4Library> getNativeLibraries();
+ int getDeclaredActivationPolicy();
+
+ // Run-time data access methods.
+ Bundle getBundle();
+ String getId();
+ List<Wire> getWires();
+ boolean isResolved();
+ // TODO: FELIX3 - Shouldn't have mutable method on Module.
+ void setSecurityContext(Object securityContext);
+ Object getSecurityContext();
+
+ // Content access methods.
+ Content getContent();
+ Class getClassByDelegation(String name) throws ClassNotFoundException;
+ URL getResourceByDelegation(String name);
+ Enumeration getResourcesByDelegation(String name);
+ URL getEntry(String name);
+
+ // TODO: ML - For expediency, the index argument was added to these methods
+ // but it is not clear that this makes sense in the long run. This needs to
+ // be readdressed in the future, perhaps by the spec to clearly indicate
+ // how resources on the bundle class path are searched, which is why we
+ // need the index number in the first place -- to differentiate among
+ // resources with the same name on the bundle class path. This was previously
+ // handled as part of the resource path, but that approach is not spec
+ // compliant.
+ boolean hasInputStream(int index, String urlPath)
+ throws IOException;
+ InputStream getInputStream(int index, String urlPath)
+ throws IOException;
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/ResolveException.java b/framework/src/main/java/org/apache/felix/framework/resolver/ResolveException.java
new file mode 100755
index 0000000..e790856
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResolveException.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import org.apache.felix.framework.capabilityset.Requirement;
+
+public class ResolveException extends RuntimeException
+{
+ private final Module m_module;
+ private final Requirement m_req;
+
+ /**
+ * Constructs an instance of <code>ResolveException</code> with the specified detail message.
+ * @param msg the detail message.
+ */
+ public ResolveException(String msg, Module module, Requirement req)
+ {
+ super(msg);
+ m_module = module;
+ m_req = req;
+ }
+
+ public Module getModule()
+ {
+ return m_module;
+ }
+
+ public Requirement getRequirement()
+ {
+ return m_req;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java b/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
new file mode 100644
index 0000000..c8bd513
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.Requirement;
+
+public interface Resolver
+{
+ Map<Module, List<Wire>> resolve(ResolverState state, Module module);
+ Map<Module, List<Wire>> resolve(ResolverState state, Module module, String pkgName);
+
+ public static interface ResolverState
+ {
+ Set<Capability> getCandidates(Module module, Requirement req, boolean obeyMandatory);
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
new file mode 100644
index 0000000..1a90baa
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
@@ -0,0 +1,1581 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+import org.apache.felix.framework.FelixResolverState;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.capabilityset.Attribute;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.framework.capabilityset.Directive;
+import org.apache.felix.framework.capabilityset.Requirement;
+import org.apache.felix.framework.util.manifestparser.R4Library;
+import org.apache.felix.framework.util.manifestparser.RequirementImpl;
+import org.osgi.framework.Constants;
+
+// 1. Treat hard pkg constraints separately from implied package constraints
+// 2. Map pkg constraints to a set of capabilities, not a single capability.
+// 3. Uses constraints cannot conflict with other uses constraints, only with hard constraints.
+public class ResolverImpl implements Resolver
+{
+ private final Logger m_logger;
+
+ // Execution environment.
+// TODO: FELIX3 - Move EE checking to ResolverState interface.
+ private final String m_fwkExecEnvStr;
+ private final Set m_fwkExecEnvSet;
+
+ private static final Map<String, Long> m_invokeCounts = new HashMap<String, Long>();
+ private static boolean m_isInvokeCount = false;
+
+ // Reusable empty array.
+ private static final List<Wire> m_emptyWires = new ArrayList<Wire>(0);
+
+ public ResolverImpl(Logger logger, String fwkExecEnvStr)
+ {
+System.out.println("+++ PROTO3 RESOLVER");
+ m_logger = logger;
+ m_fwkExecEnvStr = (fwkExecEnvStr != null) ? fwkExecEnvStr.trim() : null;
+ m_fwkExecEnvSet = parseExecutionEnvironments(fwkExecEnvStr);
+
+ String v = System.getProperty("invoke.count");
+ m_isInvokeCount = (v == null) ? false : Boolean.valueOf(v);
+ }
+
+ private final List<Map<Requirement, Set<Capability>>> m_candidatePermutations =
+ new ArrayList<Map<Requirement, Set<Capability>>>();
+
+ public Map<Module, List<Wire>> resolve(ResolverState state, Module module)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ Map<Module, List<Wire>> wireMap = new HashMap<Module, List<Wire>>();
+
+ Map<Module, Packages> modulePkgMap = new HashMap<Module, Packages>();
+
+ if (!module.isResolved())
+ {
+ m_candidatePermutations.clear();
+
+//System.out.println("+++ RESOLVING " + module);
+ Map<Requirement, Set<Capability>> candidateMap =
+ new HashMap<Requirement, Set<Capability>>();
+
+ populateCandidates(state, module, m_fwkExecEnvStr, m_fwkExecEnvSet,
+ candidateMap, new HashMap<Module, Object>());
+ m_candidatePermutations.add(candidateMap);
+
+ ResolveException rethrow = null;
+
+ do
+ {
+ rethrow = null;
+
+ candidateMap = m_candidatePermutations.remove(0);
+//dumpCandidateMap(state, candidateMap);
+
+ try
+ {
+ findConsistentCandidates(
+ module,
+ new ArrayList(),
+ candidateMap,
+ modulePkgMap,
+ new HashMap<Module, Object>());
+ }
+ catch (ResolveException ex)
+ {
+ rethrow = ex;
+ System.out.println("RE: " + ex);
+ }
+ }
+ while ((rethrow != null) && (m_candidatePermutations.size() > 0));
+
+ if (rethrow != null)
+ {
+ throw rethrow;
+ }
+//dumpModulePkgMap(modulePkgMap);
+
+ wireMap =
+ populateWireMap(module, modulePkgMap, wireMap,
+ candidateMap);
+ }
+
+ if (m_isInvokeCount)
+ {
+ System.out.println("INVOKE COUNTS " + m_invokeCounts);
+ }
+
+ return wireMap;
+ }
+
+ public Map<Module, List<Wire>> resolve(ResolverState state, Module module, String pkgName)
+ {
+ Capability candidate = null;
+
+ // We can only create a dynamic import if the following
+ // conditions are met:
+ // 1. The specified module is resolved.
+ // 2. The package in question is not already imported.
+ // 3. The package in question is not accessible via require-bundle.
+ // 4. The package in question is not exported by the bundle.
+ // 5. The package in question matches a dynamic import of the bundle.
+ // The following call checks all of these conditions and returns
+ // a matching dynamic requirement if possible.
+ Map<Requirement, Set<Capability>> candidateMap =
+ new HashMap<Requirement, Set<Capability>>();
+ if (isAllowedDynamicImport(state, module, pkgName, candidateMap))
+ {
+ m_candidatePermutations.clear();
+
+ Map<Module, List<Wire>> wireMap = new HashMap<Module, List<Wire>>();
+
+ Map<Module, Packages> modulePkgMap = new HashMap<Module, Packages>();
+
+//System.out.println("+++ DYNAMICALLY RESOLVING " + module + " - " + pkgName);
+ populateDynamicCandidates(state, module,
+ m_fwkExecEnvStr, m_fwkExecEnvSet, candidateMap);
+ m_candidatePermutations.add(candidateMap);
+ ResolveException rethrow = null;
+
+ do
+ {
+ rethrow = null;
+
+ candidateMap = m_candidatePermutations.remove(0);
+//dumpCandidateMap(state, candidateMap);
+
+ try
+ {
+ findConsistentDynamicCandidate(
+ module,
+ new ArrayList(),
+ candidateMap,
+ modulePkgMap);
+ }
+ catch (ResolveException ex)
+ {
+ rethrow = ex;
+ System.out.println("RE: " + ex);
+ }
+ }
+ while ((rethrow != null) && (m_candidatePermutations.size() > 0));
+
+ if (rethrow != null)
+ {
+ throw rethrow;
+ }
+//dumpModulePkgMap(modulePkgMap);
+ wireMap =
+ populateDynamicWireMap(
+ module, pkgName, modulePkgMap, wireMap, candidateMap);
+
+//System.out.println("+++ DYNAMIC SUCCESS: " + wireMap.get(module));
+ return wireMap;
+ }
+
+//System.out.println("+++ DYNAMIC FAILURE");
+ return null;
+ }
+
+ // TODO: FELIX3 - It would be nice to make this private.
+ // TODO: FELIX3 - At a minimum, figure out a different way than passing in the
+ // candidate map.
+ public static boolean isAllowedDynamicImport(
+ ResolverState state, Module module, String pkgName, Map<Requirement,
+ Set<Capability>> candidateMap)
+ {
+ // Unresolved modules cannot dynamically import, nor can the default
+ // package be dynamically imported.
+ if (!module.isResolved() || pkgName.length() == 0)
+ {
+ return false;
+ }
+
+ // If any of the module exports this package, then we cannot
+ // attempt to dynamically import it.
+ List<Capability> caps = module.getCapabilities();
+ for (int i = 0; (caps != null) && (i < caps.size()); i++)
+ {
+ if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
+ && caps.get(i).getAttribute(Capability.PACKAGE_ATTR).getValue().equals(pkgName))
+ {
+ return false;
+ }
+ }
+ // If any of our wires have this package, then we cannot
+ // attempt to dynamically import it.
+ List<Wire> wires = module.getWires();
+ for (int i = 0; (wires != null) && (i < wires.size()); i++)
+ {
+ if (wires.get(i).hasPackage(pkgName))
+ {
+ return false;
+ }
+ }
+
+ // Loop through the importer's dynamic requirements to determine if
+ // there is a matching one for the package from which we want to
+ // load a class.
+ List<Directive> dirs = new ArrayList(0);
+ List<Attribute> attrs = new ArrayList(1);
+ attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
+ Requirement req = new RequirementImpl(Capability.PACKAGE_NAMESPACE, dirs, attrs);
+ Set<Capability> candidates = state.getCandidates(module, req, false);
+ List<Requirement> dynamics = module.getDynamicRequirements();
+
+ // First find a dynamic requirement that matches the capabilities.
+ Requirement 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(cap, dynamics.get(dynIdx).getFilter()))
+ {
+ dynReq = 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(cap, dynReq.getFilter()))
+ {
+ itCand.remove();
+ }
+ }
+
+ if (candidates.size() > 0)
+ {
+ candidateMap.put(dynReq, candidates);
+ }
+ }
+ else
+ {
+ candidates.clear();
+ }
+
+ return !candidates.isEmpty();
+ }
+
+ private static void dumpCandidateMap(
+ ResolverState state, Map<Requirement, Set<Capability>> candidateMap)
+ {
+ System.out.println("=== BEGIN CANDIDATE MAP ===");
+ for (Module module : ((FelixResolverState) state).getModules())
+ {
+ System.out.println(" " + module
+ + " (" + (module.isResolved() ? "RESOLVED)" : "UNRESOLVED)"));
+ for (Requirement req : module.getRequirements())
+ {
+ Set<Capability> candidates = candidateMap.get(req);
+ if ((candidates != null) && (candidates.size() > 0))
+ {
+ System.out.println(" " + req + ": " + candidates);
+ }
+ }
+ for (Requirement req : module.getDynamicRequirements())
+ {
+ Set<Capability> candidates = candidateMap.get(req);
+ if ((candidates != null) && (candidates.size() > 0))
+ {
+ System.out.println(" " + req + ": " + candidates);
+ }
+ }
+ }
+ System.out.println("=== END CANDIDATE MAP ===");
+ }
+
+ private static void dumpModulePkgMap(Map<Module, Packages> modulePkgMap)
+ {
+ System.out.println("+++MODULE PKG MAP+++");
+ for (Entry<Module, Packages> entry : modulePkgMap.entrySet())
+ {
+ dumpModulePkgs(entry.getKey(), entry.getValue());
+ }
+ }
+
+ private static void dumpModulePkgs(Module module, Packages packages)
+ {
+ System.out.println(module + " (" + (module.isResolved() ? "RESOLVED)" : "UNRESOLVED)"));
+ System.out.println(" EXPORTED");
+ for (Entry<String, Blame> entry : packages.m_exportedPkgs.entrySet())
+ {
+ System.out.println(" " + entry.getKey() + " - " + entry.getValue());
+ }
+ System.out.println(" IMPORTED");
+ for (Entry<String, Blame> entry : packages.m_importedPkgs.entrySet())
+ {
+ System.out.println(" " + entry.getKey() + " - " + entry.getValue());
+ }
+ System.out.println(" REQUIRED");
+ for (Entry<String, List<Blame>> entry : packages.m_requiredPkgs.entrySet())
+ {
+ System.out.println(" " + entry.getKey() + " - " + entry.getValue());
+ }
+ System.out.println(" USED");
+ for (Entry<String, List<Blame>> entry : packages.m_usedPkgs.entrySet())
+ {
+ System.out.println(" " + entry.getKey() + " - " + entry.getValue());
+ }
+ }
+
+ private static void populateCandidates(
+ ResolverState state, Module module, String fwkExecEnvStr, Set fwkExecEnvSet,
+ Map<Requirement, Set<Capability>> candidateMap, Map<Module, Object> resultCache)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ // Determine if we've already calculated this module's candidates.
+ // The result cache will have one of three values:
+ // 1. A resolve exception if we've already attempted to populate the
+ // module's candidates but were unsuccessful.
+ // 2. Boolean.TRUE indicating we've already attempted to populate the
+ // module's candidates and were successful.
+ // 3. An array containing the cycle count, current map of candidates
+ // for already processed requirements, and a list of remaining
+ // requirements whose candidates still need to be calculated.
+ // For case 1, rethrow the exception. For case 3, simply return immediately.
+ // For case 3, this means we have a cycle so we should continue to populate
+ // the candidate where we left off and not record any results globally
+ // until we've popped completely out of the cycle.
+
+ // Keeps track of the number of times we've reentered this method
+ // for the current module.
+ Integer cycleCount = null;
+
+ // Keeps track of the candidates we've already calculated for the
+ // current module's requirements.
+ Map<Requirement, Set<Capability>> localCandidateMap = null;
+
+ // Keeps track of the current module's requirements for which we
+ // haven't yet found candidates.
+ List<Requirement> remainingReqs = null;
+
+ // Get the cache value for the current module.
+ Object cacheValue = resultCache.get(module);
+
+ // This is case 1.
+ if (cacheValue instanceof ResolveException)
+ {
+ throw (ResolveException) cacheValue;
+ }
+ // This is case 2.
+ else if (cacheValue instanceof Boolean)
+ {
+ return;
+ }
+ // This is case 3.
+ else if (cacheValue != null)
+ {
+ cycleCount = (Integer) ((Object[]) cacheValue)[0];
+ ((Object[]) cacheValue)[0] = new Integer(cycleCount.intValue() + 1);
+ cycleCount = (Integer) ((Object[]) cacheValue)[0];
+ localCandidateMap = (Map) ((Object[]) cacheValue)[1];
+ remainingReqs = (List) ((Object[]) cacheValue)[2];
+ }
+
+ // If there is no cache value for the current module, then this is
+ // the first time we are attempting to populate its candidates, so
+ // do some one-time checks and initialization.
+ if ((remainingReqs == null) && (localCandidateMap == null))
+ {
+ // Verify that any required execution environment is satisfied.
+ verifyExecutionEnvironment(fwkExecEnvStr, fwkExecEnvSet, module);
+
+ // Verify that any native libraries match the current platform.
+ verifyNativeLibraries(module);
+
+ // Record cycle count.
+ cycleCount = new Integer(0);
+
+ // Store candidates in a local map first, just in case the module
+ // is not resolvable.
+ localCandidateMap = new HashMap();
+
+ // Create a modifiable list of the module's requirements.
+ remainingReqs = new ArrayList(module.getRequirements());
+
+ // Add these value to the result cache so we know we are
+ // in the middle of populating candidates for the current
+ // module.
+ resultCache.put(module,
+ cacheValue = new Object[] { cycleCount, localCandidateMap, remainingReqs });
+ }
+
+ // If we have requirements remaining, then find candidates for them.
+ while (remainingReqs.size() > 0)
+ {
+ Requirement req = remainingReqs.remove(0);
+
+ // Get satisfying candidates and populate their candidates if necessary.
+ Set<Capability> candidates = state.getCandidates(module, req, true);
+ for (Iterator<Capability> itCandCap = candidates.iterator(); itCandCap.hasNext(); )
+ {
+ Capability candCap = itCandCap.next();
+ if (!candCap.getModule().isResolved())
+ {
+ try
+ {
+ populateCandidates(state, candCap.getModule(),
+ fwkExecEnvStr, fwkExecEnvSet, candidateMap, resultCache);
+ }
+ catch (ResolveException ex)
+ {
+System.out.println("RE: Candidate not resolveable: " + ex);
+ // Remove the candidate since we weren't able to
+ // populate its candidates.
+ itCandCap.remove();
+ }
+ }
+ }
+
+ // If there are no candidates for the current requirement
+ // and it is not optional, then create, cache, and throw
+ // a resolve exception.
+ if ((candidates.size() == 0) && !req.isOptional())
+ {
+ ResolveException ex =
+ new ResolveException("Unable to resolve " + module
+ + ": missing requirement " + req, module, req);
+ resultCache.put(module, ex);
+ throw ex;
+ }
+ // If we actually have candidates for the requirement, then
+ // add them to the local candidate map.
+ else if (candidates.size() > 0)
+ {
+ localCandidateMap.put(req, candidates);
+ }
+ }
+
+ // If we are exiting from a cycle then decrement
+ // cycle counter, otherwise record the result.
+ if (cycleCount.intValue() > 0)
+ {
+ ((Object[]) cacheValue)[0] = new Integer(cycleCount.intValue() - 1);
+ }
+ else if (cycleCount.intValue() == 0)
+ {
+ // Record that the module was successfully populated.
+ resultCache.put(module, Boolean.TRUE);
+
+ // Merge local candidate map into global candidate map.
+ if (localCandidateMap.size() > 0)
+ {
+ candidateMap.putAll(localCandidateMap);
+ }
+ }
+ }
+
+ private static void populateDynamicCandidates(
+ ResolverState state, Module module,
+ String fwkExecEnvStr, Set fwkExecEnvSet,
+ Map<Requirement, Set<Capability>> candidateMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ // There should be one entry in the candidate map, which are the
+ // the candidates for the matching dynamic requirement. Get the
+ // matching candidates and populate their candidates if necessary.
+ Entry<Requirement, Set<Capability>> entry = candidateMap.entrySet().iterator().next();
+ Requirement dynReq = entry.getKey();
+ Set<Capability> candidates = entry.getValue();
+ for (Iterator<Capability> itCandCap = candidates.iterator(); itCandCap.hasNext(); )
+ {
+ Capability candCap = itCandCap.next();
+ if (!candCap.getModule().isResolved())
+ {
+ try
+ {
+ populateCandidates(state, candCap.getModule(),
+ fwkExecEnvStr, fwkExecEnvSet, candidateMap,
+ new HashMap<Module, Object>());
+ }
+ catch (ResolveException ex)
+ {
+System.out.println("RE: Candidate not resolveable: " + ex);
+ itCandCap.remove();
+ }
+ }
+ }
+
+// TODO: FELIX3 - Since we reuse the same dynamic requirement, is it possible
+// that some sort of cycle could cause us to try to match another set
+// of candidates to the same requirement?
+ if (candidates.size() == 0)
+ {
+ candidateMap.remove(dynReq);
+ throw new ResolveException("Dynamic import failed.", module, dynReq);
+ }
+
+ // Add existing wires as candidates.
+ for (Wire wire : module.getWires())
+ {
+ Set<Capability> cs = new TreeSet();
+ cs.add(wire.getCapability());
+ candidateMap.put(wire.getRequirement(), cs);
+ }
+ }
+
+ private void findConsistentCandidates(
+ Module module, List<Requirement> incomingReqs,
+ Map<Requirement, Set<Capability>> candidateMap,
+ Map<Module, Packages> modulePkgMap,
+ Map<Module, Object> cycleMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ Integer cycleCount = null;
+
+ Object o = cycleMap.get(module);
+
+ if (o instanceof Boolean)
+ {
+ return;
+ }
+ else if (o == null)
+ {
+ List list;
+ if (module.isResolved())
+ {
+ list = new ArrayList(module.getWires());
+ }
+ else
+ {
+ list = new ArrayList(module.getRequirements());
+ }
+ cycleMap.put(module, o = new Object[] { cycleCount = new Integer(0), list });
+ calculateExportedPackages(module, incomingReqs, modulePkgMap);
+ }
+ else
+ {
+ cycleCount = (Integer) ((Object[]) o)[0];
+ ((Object[]) o)[0] = new Integer(cycleCount.intValue() + 1);
+ cycleCount = (Integer) ((Object[]) o)[0];
+ }
+
+//System.out.println("+++ RESOLVING " + module);
+
+ if (module.isResolved())
+ {
+ List<Wire> wires = (List<Wire>) ((Object[]) o)[1];
+
+ while (wires.size() > 0)
+ {
+ Wire wire = wires.remove(0);
+
+ // Try to resolve the candidate.
+ findConsistentCandidates(
+ wire.getCapability().getModule(),
+ incomingReqs,
+ candidateMap,
+ modulePkgMap,
+ cycleMap);
+
+ // If we are here, the candidate was consistent. Try to
+ // merge the candidate into the target module's packages.
+ mergeCandidatePackages(
+ module,
+ incomingReqs,
+ wire.getCapability(),
+ modulePkgMap,
+ candidateMap);
+ }
+ }
+ else
+ {
+ List<Requirement> reqs = (List<Requirement>) ((Object[]) o)[1];
+
+ while (reqs.size() > 0)
+ {
+ Requirement req = reqs.remove(0);
+
+ // Get the candidates for the current requirement.
+ Set<Capability> candCaps = candidateMap.get(req);
+ // Optional requirements may not have any candidates.
+ if (candCaps == null)
+ {
+ continue;
+ }
+
+ List<Requirement> outgoingReqs = new ArrayList<Requirement>(incomingReqs);
+ outgoingReqs.add(req);
+
+ for (Iterator<Capability> it = candCaps.iterator(); it.hasNext(); )
+ {
+ Capability candCap = it.next();
+//System.out.println("+++ TRYING CAND " + candCap + " FOR " + req);
+ try
+ {
+ // Try to resolve the candidate.
+ findConsistentCandidates(
+ candCap.getModule(),
+ outgoingReqs,
+ candidateMap,
+ modulePkgMap,
+ cycleMap);
+
+ // If we are here, the candidate was consistent. Try to
+ // merge the candidate into the target module's packages.
+ mergeCandidatePackages(
+ module,
+ outgoingReqs,
+ candCap,
+ modulePkgMap,
+ candidateMap);
+
+ // If we are here, we merged the candidate successfully,
+ // so we can continue with the next requirement
+ break;
+ }
+ catch (ResolveException ex)
+ {
+System.out.println("RE: " + ex);
+ex.printStackTrace();
+
+// TODO: FELIX3 RESOLVER - Is it ok to remove the failed candidate? By removing
+// it we keep the candidateMap up to date with the selected candidate, but
+// theoretically this eliminates some potential combinations. Are those
+// combinations guaranteed to be failures so eliminating them is ok?
+ it.remove();
+ if (!it.hasNext() && !req.isOptional())
+ {
+ throw new ResolveException("Unresolved constraint "
+ + req + " in " + module, module, req);
+ }
+ }
+ }
+ }
+ }
+
+ // If we are exiting from a cycle then decrement
+ // cycle counter, otherwise record the result.
+ if (cycleCount.intValue() > 0)
+ {
+ ((Object[]) o)[0] = new Integer(cycleCount.intValue() - 1);
+ }
+ else if (cycleCount.intValue() == 0)
+ {
+ // Record that the module was successfully populated.
+ cycleMap.put(module, Boolean.TRUE);
+ }
+ }
+
+ private void findConsistentDynamicCandidate(
+ Module module, List<Requirement> incomingReqs,
+ Map<Requirement, Set<Capability>> candidateMap,
+ Map<Module, Packages> modulePkgMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+//System.out.println("+++ RESOLVING " + module);
+ calculateExportedPackages(module, incomingReqs, modulePkgMap);
+
+ List<Requirement> reqs = new ArrayList(module.getRequirements());
+ reqs.addAll(module.getDynamicRequirements());
+ for (Requirement req : reqs)
+ {
+ // Get the candidates for the current requirement.
+ Set<Capability> candCaps = candidateMap.get(req);
+ // Optional requirements may not have any candidates.
+ if (candCaps == null)
+ {
+ continue;
+ }
+
+ List<Requirement> outgoingReqs = new ArrayList<Requirement>(incomingReqs);
+ outgoingReqs.add(req);
+
+ for (Iterator<Capability> it = candCaps.iterator(); it.hasNext(); )
+ {
+ Capability candCap = it.next();
+//System.out.println("+++ TRYING CAND " + candCap + " FOR " + req);
+ try
+ {
+ // Try to resolve the candidate.
+ findConsistentCandidates(
+ candCap.getModule(),
+ outgoingReqs,
+ candidateMap,
+ modulePkgMap,
+ new HashMap());
+
+ // If we are here, the candidate was consistent. Try to
+ // merge the candidate into the target module's packages.
+ mergeCandidatePackages(
+ module,
+ outgoingReqs,
+ candCap,
+ modulePkgMap,
+ candidateMap);
+
+ // If we are here, we merged the candidate successfully,
+ // so we can continue with the next requirement
+ break;
+ }
+ catch (ResolveException ex)
+ {
+System.out.println("RE: " + ex);
+ex.printStackTrace();
+// TODO: FELIX3 RESOLVER - Is it ok to remove the failed candidate? By removing
+// it we keep the candidateMap up to date with the selected candidate, but
+// theoretically this eliminates some potential combinations. Are those
+// combinations guaranteed to be failures so eliminating them is ok?
+ it.remove();
+ if (!it.hasNext() && !req.isOptional())
+ {
+ throw new ResolveException("Unresolved constraint "
+ + req + " in " + module, module, req);
+ }
+ }
+ }
+ }
+ }
+
+ private static void calculateExportedPackages(
+ Module module, List<Requirement> incomingReqs, Map<Module, Packages> modulePkgMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ Packages packages = new Packages();
+
+ List<Capability> caps = module.getCapabilities();
+
+ if (caps.size() > 0)
+ {
+ for (int i = 0; i < caps.size(); i++)
+ {
+// TODO: PROTO3 RESOLVER - Assume if a module imports the same package it
+// exports that the import will overlap the export.
+ if (caps.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
+ && !hasOverlappingImport(module, caps.get(i)))
+ {
+ packages.m_exportedPkgs.put(
+ (String) caps.get(i).getAttribute(Capability.PACKAGE_ATTR).getValue(),
+ new Blame(incomingReqs, caps.get(i)));
+ }
+ }
+ }
+
+ modulePkgMap.put(module, packages);
+ }
+
+ private static boolean hasOverlappingImport(Module module, Capability cap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ List<Requirement> reqs = module.getRequirements();
+ for (int i = 0; i < reqs.size(); i++)
+ {
+ if (reqs.get(i).getNamespace().equals(Capability.PACKAGE_NAMESPACE)
+ && CapabilitySet.matches(cap, reqs.get(i).getFilter()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void mergeCandidatePackages(
+ Module current, List<Requirement> outgoingReqs,
+ Capability candCap, Map<Module, Packages> modulePkgMap,
+ Map<Requirement, Set<Capability>> candidateMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ if (candCap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ mergeCandidatePackage(
+ current, false, new Blame(outgoingReqs, candCap), modulePkgMap, candidateMap);
+ }
+ else if (candCap.getNamespace().equals(Capability.MODULE_NAMESPACE))
+ {
+ // Get the candidate's package space to determine which packages
+ // will be visible to the current module.
+ Packages candPkgs = modulePkgMap.get(candCap.getModule());
+
+// TODO: PROTO3 RESOLVER - For now assume only exports, but eventually we also
+// have to support re-exported packages.
+ for (Entry<String, Blame> entry : candPkgs.m_exportedPkgs.entrySet())
+ {
+ mergeCandidatePackage(
+ current,
+ true,
+ new Blame(outgoingReqs, entry.getValue().m_cap),
+ modulePkgMap,
+ candidateMap);
+ }
+ for (Entry<String, List<Blame>> entry : candPkgs.m_requiredPkgs.entrySet())
+ {
+ List<Blame> blames = entry.getValue();
+ for (Blame blame : blames)
+ {
+// TODO: FELIX3 RESOLVER - Since a single module requirement can include many packages,
+// it is likely we call merge too many times for the same module req. If we knew
+// which candidates were being used to resolve this candidate's module dependencies,
+// then we could just try to merge them directly. This info would also help in
+// in creating wires, since we ultimately want to create wires for the selected
+// candidates, which we are trying to deduce from the package space, but if we
+// knew the selected candidates, we'd be done.
+ if (blame.m_cap.getModule().equals(current))
+ {
+ continue;
+ }
+
+ Directive dir = blame.m_reqs.get(blame.m_reqs.size() - 1)
+ .getDirective(Constants.VISIBILITY_DIRECTIVE);
+ if ((dir != null) && dir.getValue().equals(Constants.VISIBILITY_REEXPORT))
+ {
+ mergeCandidatePackage(
+ current,
+ true,
+ new Blame(outgoingReqs, blame.m_cap),
+ modulePkgMap,
+ candidateMap);
+ }
+ }
+ }
+ }
+ }
+
+ private void mergeCandidatePackage(
+ Module current, boolean requires,
+ Blame candBlame, Map<Module, Packages> modulePkgMap,
+ Map<Requirement, Set<Capability>> candidateMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+// TODO: PROTO3 RESOLVER - Check for merging where module imports from itself,
+// then it should be listed as an export for requiring bundles.
+ if (candBlame.m_cap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+//System.out.println("+++ MERGING " + candBlame.m_cap + " INTO " + current);
+ String pkgName = (String)
+ candBlame.m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue();
+
+ // Since this capability represents a package, it will become
+ // a hard constraint on the module's package space, so we need
+ // to make sure it doesn't conflict with any other hard constraints
+ // or any other uses constraints.
+
+ //
+ // First, check to see if the capability conflicts with
+ // any existing hard constraints.
+ //
+
+ Packages currentPkgs = modulePkgMap.get(current);
+ Blame currentExportedBlame = currentPkgs.m_exportedPkgs.get(pkgName);
+ Blame currentImportedBlame = currentPkgs.m_importedPkgs.get(pkgName);
+ List<Blame> currentRequiredBlames = currentPkgs.m_requiredPkgs.get(pkgName);
+
+ // We don't need to worry about an import conflicting with a required
+ // bundle's export, since imported package wires are terminal the
+ // bundle will never see the exported package from the required bundle.
+// TODO: FELIX3 - See scenario 21, this seems odd.
+ if (!requires &&
+ (currentImportedBlame != null) && !currentImportedBlame.m_cap.equals(candBlame.m_cap))
+// if (!requires &&
+// (((currentExportedBlame != null) && !currentExportedBlame.m_cap.equals(candBlame.m_cap))
+// || ((currentImportedBlame != null) && !currentImportedBlame.m_cap.equals(candBlame.m_cap))))
+// || ((currentRequiredBlames != null) && !currentRequiredBlames.contains(candBlame))))
+ {
+ // Permutate the candidate map and throw a resolve exception.
+ // NOTE: This method ALWAYS throws an exception.
+ permutateCandidates(
+ current,
+ pkgName,
+ currentImportedBlame,
+ candBlame,
+ candidateMap);
+ }
+
+ //
+ // Second, check to see if the capability conflicts with
+ // any existing uses constraints
+ //
+
+ Packages currentPkgsCopy = currentPkgs;
+
+ if (!current.isResolved())
+ {
+ List<Blame> currentUsedBlames = currentPkgs.m_usedPkgs.get(pkgName);
+ checkExistingUsesConstraints(
+ current, pkgName, currentUsedBlames, candBlame, modulePkgMap, candidateMap);
+
+ //
+ // Last, check to see if any uses constraints implied by the
+ // candidate conflict with any of the existing hard constraints.
+ //
+
+ // For now, create a copy of the module's package space and
+ // add the current candidate to the imported packages.
+ currentPkgsCopy = new Packages(currentPkgs);
+ }
+
+ if (requires)
+ {
+ if (currentRequiredBlames == null)
+ {
+ currentRequiredBlames = new ArrayList<Blame>();
+ currentPkgsCopy.m_requiredPkgs.put(pkgName, currentRequiredBlames);
+ }
+// TODO: PROTO3 RESOLVER - This is potentially modifying the original, we need to modify a copy.
+ currentRequiredBlames.add(candBlame);
+ }
+ else
+ {
+ currentPkgsCopy.m_importedPkgs.put(pkgName, candBlame);
+ }
+
+ // Verify and merge the candidate's transitive uses constraints.
+ verifyAndMergeUses(
+ current,
+ currentPkgsCopy,
+ candBlame,
+ modulePkgMap,
+ candidateMap,
+ new HashMap<String, List<Module>>());
+
+ // If we are here, then there were no conflict, so we should update
+ // the module's package space.
+ if (!current.isResolved())
+ {
+ currentPkgs.m_exportedPkgs.putAll(currentPkgsCopy.m_exportedPkgs);
+ currentPkgs.m_importedPkgs.putAll(currentPkgsCopy.m_importedPkgs);
+ currentPkgs.m_requiredPkgs.putAll(currentPkgsCopy.m_requiredPkgs);
+ currentPkgs.m_usedPkgs.putAll(currentPkgsCopy.m_usedPkgs);
+ }
+//dumpModulePkgs(current, currentPkgs);
+ }
+ }
+
+ private void checkExistingUsesConstraints(
+ Module current, String pkgName, List<Blame> currentUsedBlames,
+ Blame candBlame, Map<Module, Packages> modulePkgMap,
+ Map<Requirement, Set<Capability>> candidateMap)
+ {
+ for (int i = 0; (currentUsedBlames != null) && (i < currentUsedBlames.size()); i++)
+ {
+//System.out.println("+++ CHECK " + candBlame + " IN EXISTING " + currentUsedBlames.get(i));
+ if (!isCompatible(currentUsedBlames.get(i).m_cap, candBlame.m_cap, modulePkgMap))
+ {
+ // Permutate the candidate map and throw a resolve exception.
+ // NOTE: This method ALWAYS throws an exception.
+ permutateCandidates(
+ current,
+ pkgName,
+ currentUsedBlames.get(i),
+ candBlame,
+ candidateMap);
+ }
+ }
+ }
+
+// TODO: PROTO3 RESOLVER - We end up with duplicates in uses constraints,
+// see scenario 2 for an example.
+ private void verifyAndMergeUses(
+ Module current, Packages currentPkgs,
+ Blame candBlame, Map<Module, Packages> modulePkgMap,
+ Map<Requirement, Set<Capability>> candidateMap,
+ Map<String, List<Module>> cycleMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ // Check for cycles.
+ String pkgName = (String)
+ candBlame.m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue();
+ List<Module> list = cycleMap.get(pkgName);
+ if ((list != null) && list.contains(current))
+ {
+ return;
+ }
+ list = (list == null) ? new ArrayList<Module>() : list;
+ list.add(current);
+ cycleMap.put(pkgName, list);
+
+//System.out.println("+++ VERIFYING USES " + current + " FOR " + candBlame);
+ for (Capability candSourceCap : getPackageSources(
+ candBlame.m_cap, modulePkgMap, new ArrayList<Capability>(), new HashSet<Capability>()))
+ {
+ for (String usedPkgName : candSourceCap.getUses())
+ {
+ Blame currentExportedBlame = currentPkgs.m_exportedPkgs.get(usedPkgName);
+ Blame currentImportedBlame = currentPkgs.m_importedPkgs.get(usedPkgName);
+// TODO: PROTO3 RESOLVER - What do we do with required packages?
+ List<Blame> currentRequiredBlames = currentPkgs.m_requiredPkgs.get(usedPkgName);
+
+ Packages candSourcePkgs = modulePkgMap.get(candSourceCap.getModule());
+//System.out.println("+++ candSourceCap " + candSourceCap);
+//System.out.println("+++ candSourceCap.getModule() " + candSourceCap.getModule() + " (" + candSourceCap.getModule().isResolved() + ")");
+//System.out.println("+++ candSourcePkgs " + candSourcePkgs);
+//System.out.println("+++ candSourcePkgs.m_exportedPkgs " + candSourcePkgs.m_exportedPkgs);
+ Blame candSourceBlame = candSourcePkgs.m_exportedPkgs.get(usedPkgName);
+ candSourceBlame = (candSourceBlame != null)
+ ? candSourceBlame
+ : candSourcePkgs.m_importedPkgs.get(usedPkgName);
+// sourceCap = (sourceCap != null)
+// ? sourceCap
+// : sourcePkgs.m_requiredPkgs.get(usedPkgName);
+
+ // If the candidate doesn't actually have a constraint for
+ // the used package, then just ignore it since this is likely
+ // an error in its metadata.
+ if (candSourceBlame == null)
+ {
+ return;
+ }
+
+ // If there is no current mapping for this package, then
+ // we can just return.
+ if ((currentExportedBlame == null)
+ && (currentImportedBlame == null)
+ && (currentRequiredBlames == null))
+ {
+ List<Blame> usedCaps = currentPkgs.m_usedPkgs.get(usedPkgName);
+ if (usedCaps == null)
+ {
+ usedCaps = new ArrayList<Blame>();
+ currentPkgs.m_usedPkgs.put(usedPkgName, usedCaps);
+ }
+//System.out.println("+++ MERGING CB " + candBlame + " SB " + candSourceBlame);
+// usedCaps.add(new Blame(candBlame.m_reqs, sourceBlame.m_cap));
+ usedCaps.add(candSourceBlame);
+// return;
+ }
+ else if (!current.isResolved())
+ {
+ if ((currentExportedBlame != null)
+ && !isCompatible(currentExportedBlame.m_cap, candSourceBlame.m_cap, modulePkgMap))
+ {
+ throw new ResolveException(
+ "Constraint violation for package '" + usedPkgName
+ + "' when resolving module " + current
+ + " between existing constraint "
+ + currentExportedBlame
+ + " and candidate constraint "
+ + candSourceBlame, null, null);
+ }
+ else if ((currentImportedBlame != null)
+ && !isCompatible(currentImportedBlame.m_cap, candSourceBlame.m_cap, modulePkgMap))
+ {
+//System.out.println("+++ CIB " + currentImportedBlame + " SB " + sourceBlame);
+ // Try to remove the previously selected candidate associated
+ // with the requirement blamed for adding the constraint. This
+ // Permutate the candidate map.
+ if (currentImportedBlame.m_reqs.size() != 0)
+ {
+ // Permutate the candidate map.
+ for (int reqIdx = 0; reqIdx < currentImportedBlame.m_reqs.size(); reqIdx++)
+ {
+ Map<Requirement, Set<Capability>> copy = copyCandidateMap(candidateMap);
+ Set<Capability> candidates =
+ copy.get(currentImportedBlame.m_reqs.get(reqIdx));
+ Iterator it = candidates.iterator();
+ it.next();
+ it.remove();
+// TODO: PROTO3 RESOLVER - We could check before doing the candidate map copy.
+ if (candidates.size() > 0)
+ {
+ m_candidatePermutations.add(copy);
+ }
+ }
+ }
+
+ throw new ResolveException(
+ "Constraint violation for package '" + usedPkgName
+ + "' when resolving module " + current
+ + " between existing constraint "
+ + currentImportedBlame
+ + " and candidate constraint "
+ + candSourceBlame, null, null);
+ }
+ }
+
+ verifyAndMergeUses(current, currentPkgs, candSourceBlame,
+ modulePkgMap, candidateMap, cycleMap);
+ }
+ }
+ }
+
+ private void permutateCandidates(
+ Module current, String pkgName, Blame currentBlame, Blame candBlame,
+ Map<Requirement, Set<Capability>> candidateMap)
+ throws ResolveException
+ {
+ // Try to remove the previously selected candidate associated
+ // with the requirement blamed for adding the constraint. This
+ // blamed requirement may be null if the bundle itself is
+ // exports the package imposing the uses constraint.
+ if ((currentBlame.m_reqs != null) && (currentBlame.m_reqs.size() != 0))
+ {
+ // Permutate the candidate map.
+ for (int reqIdx = 0; reqIdx < currentBlame.m_reqs.size(); reqIdx++)
+ {
+ Map<Requirement, Set<Capability>> copy = copyCandidateMap(candidateMap);
+ Set<Capability> candidates = copy.get(currentBlame.m_reqs.get(reqIdx));
+ Iterator it = candidates.iterator();
+ it.next();
+ it.remove();
+ // TODO: PROTO3 RESOLVER - We could check before doing the candidate map copy.
+ if (candidates.size() > 0)
+ {
+ m_candidatePermutations.add(copy);
+ }
+ }
+ }
+ throw new ResolveException(
+ "Constraint violation for package '"
+ + pkgName + "' when resolving module "
+ + current + " between existing constraint "
+ + currentBlame + " and candidate constraint "
+ + candBlame, null, null);
+ }
+
+ private static boolean isCompatible(
+ Capability currentCap, Capability candCap, Map<Module, Packages> modulePkgMap)
+ {
+ if ((currentCap != null) && (candCap != null))
+ {
+ List<Capability> currentSources =
+ getPackageSources(
+ currentCap,
+ modulePkgMap,
+ new ArrayList<Capability>(),
+ new HashSet<Capability>());
+ List<Capability> candSources =
+ getPackageSources(
+ candCap,
+ modulePkgMap,
+ new ArrayList<Capability>(),
+ new HashSet<Capability>());
+//System.out.println("+++ currentSources " + currentSources + " - candSources " + candSources);
+ return currentSources.containsAll(candSources) || candSources.containsAll(currentSources);
+ }
+ return true;
+ }
+
+ private static List<Capability> getPackageSources(
+ Capability cap, Map<Module, Packages> modulePkgMap, List<Capability> sources,
+ Set<Capability> cycleMap)
+ {
+ if (cap.getNamespace().equals(Capability.PACKAGE_NAMESPACE))
+ {
+ if (cycleMap.contains(cap))
+ {
+ return sources;
+ }
+ cycleMap.add(cap);
+
+ Packages pkgs = modulePkgMap.get(cap.getModule());
+ sources.add(cap);
+ String pkgName = cap.getAttribute(Capability.PACKAGE_ATTR).getValue().toString();
+ List<Blame> required = pkgs.m_requiredPkgs.get(pkgName);
+ if (required != null)
+ {
+ for (Blame blame : required)
+ {
+ getPackageSources(blame.m_cap, modulePkgMap, sources, cycleMap);
+ }
+ }
+ }
+
+ return sources;
+ }
+
+ private static Map<Requirement, Set<Capability>> copyCandidateMap(
+ Map<Requirement, Set<Capability>> candidateMap)
+ {
+ Map<Requirement, Set<Capability>> copy =
+ new HashMap<Requirement, Set<Capability>>();
+ for (Entry<Requirement, Set<Capability>> entry : candidateMap.entrySet())
+ {
+ Set<Capability> candidates = new TreeSet(new CandidateComparator());
+ candidates.addAll(entry.getValue());
+ copy.put(entry.getKey(), candidates);
+ }
+ return copy;
+ }
+
+ private static Map<Module, List<Wire>> populateWireMap(
+ Module module, Map<Module, Packages> modulePkgMap,
+ Map<Module, List<Wire>> wireMap, Map<Requirement, Set<Capability>> candidateMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ if (!module.isResolved() && !wireMap.containsKey(module))
+ {
+ wireMap.put(module, m_emptyWires);
+
+ List<Wire> packageWires = new ArrayList<Wire>();
+ List<Wire> moduleWires = new ArrayList<Wire>();
+
+ for (Requirement req : module.getRequirements())
+ {
+ Set<Capability> cands = candidateMap.get(req);
+ if ((cands != null) && (cands.size() > 0))
+ {
+ Capability cand = cands.iterator().next();
+ if (!cand.getModule().isResolved())
+ {
+ populateWireMap(cand.getModule(),
+ modulePkgMap, wireMap, candidateMap);
+ }
+ // Ignore modules that import themselves.
+ if (req.getNamespace().equals(Capability.PACKAGE_NAMESPACE)
+ && !module.equals(cand.getModule()))
+ {
+ packageWires.add(
+ new WireImpl(module,
+ req,
+ cand.getModule(),
+ cand));
+ }
+ else if (req.getNamespace().equals(Capability.MODULE_NAMESPACE))
+ {
+ Packages candPkgs = modulePkgMap.get(cand.getModule());
+ moduleWires.add(
+ new WireModuleImpl(module,
+ req,
+ cand.getModule(),
+ cand,
+ candPkgs.getExportedAndReexportedPackages()));
+ }
+ }
+ }
+
+ // Combine wires with module wires last.
+ packageWires.addAll(moduleWires);
+ wireMap.put(module, packageWires);
+ }
+
+ return wireMap;
+ }
+
+ private static Map<Module, List<Wire>> populateDynamicWireMap(
+ Module module, String pkgName, Map<Module, Packages> modulePkgMap,
+ Map<Module, List<Wire>> wireMap, Map<Requirement, Set<Capability>> candidateMap)
+ {
+ if (m_isInvokeCount)
+ {
+ String methodName = new Exception().fillInStackTrace().getStackTrace()[0].getMethodName();
+ Long count = m_invokeCounts.get(methodName);
+ count = (count == null) ? new Long(1) : new Long(count.longValue() + 1);
+ m_invokeCounts.put(methodName, count);
+ }
+
+ wireMap.put(module, m_emptyWires);
+
+ List<Wire> packageWires = new ArrayList<Wire>();
+
+ Packages pkgs = modulePkgMap.get(module);
+ for (Entry<String, Blame> entry : pkgs.m_importedPkgs.entrySet())
+ {
+ if (!entry.getValue().m_cap.getModule().isResolved())
+ {
+ populateWireMap(entry.getValue().m_cap.getModule(), modulePkgMap, wireMap,
+ candidateMap);
+ }
+
+ // Ignore modules that import themselves.
+ if (!module.equals(entry.getValue().m_cap.getModule())
+ && entry.getValue().m_cap.getAttribute(
+ Capability.PACKAGE_ATTR).getValue().equals(pkgName))
+ {
+ List<Attribute> attrs = new ArrayList();
+ attrs.add(new Attribute(Capability.PACKAGE_ATTR, pkgName, false));
+ packageWires.add(
+ new WireImpl(
+ module,
+ // We need an unique requirement here or else subsequent
+ // dynamic imports for the same dynamic requirement will
+ // conflict with previous ones.
+ new RequirementImpl(Capability.PACKAGE_NAMESPACE, new ArrayList(0), attrs),
+ entry.getValue().m_cap.getModule(),
+ entry.getValue().m_cap));
+ }
+ }
+
+ wireMap.put(module, packageWires);
+
+ return wireMap;
+ }
+
+// TODO: FELIX3 - This check should be moved to ResolverState.
+ private static void verifyNativeLibraries(Module module)
+ {
+ // Next, try to resolve any native code, since the module is
+ // not resolvable if its native code cannot be loaded.
+ List<R4Library> libs = module.getNativeLibraries();
+ if (libs != null)
+ {
+ String msg = null;
+ // Verify that all native libraries exist in advance; this will
+ // throw an exception if the native library does not exist.
+ for (int libIdx = 0; (msg == null) && (libIdx < libs.size()); libIdx++)
+ {
+ String entryName = libs.get(libIdx).getEntryName();
+ if (entryName != null)
+ {
+ if (!module.getContent().hasEntry(entryName))
+ {
+ msg = "Native library does not exist: " + entryName;
+ }
+ }
+ }
+ // If we have a zero-length native library array, then
+ // this means no native library class could be selected
+ // so we should fail to resolve.
+ if (libs.size() == 0)
+ {
+ msg = "No matching native libraries found.";
+ }
+ if (msg != null)
+ {
+ throw new ResolveException(msg, module, null);
+ }
+ }
+ }
+
+ /**
+ * Checks to see if the passed in module's required execution environment
+ * is provided by the framework.
+ * @param fwkExecEvnStr The original property value of the framework's
+ * supported execution environments.
+ * @param fwkExecEnvSet Parsed set of framework's supported execution environments.
+ * @param module The module whose required execution environment is to be to verified.
+ * @throws ResolveException if the module's required execution environment does
+ * not match the framework's supported execution environment.
+ **/
+ private static void verifyExecutionEnvironment(
+ String fwkExecEnvStr, Set fwkExecEnvSet, Module module)
+ throws ResolveException
+ {
+ String bundleExecEnvStr = (String)
+ module.getHeaders().get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
+ if (bundleExecEnvStr != null)
+ {
+ bundleExecEnvStr = bundleExecEnvStr.trim();
+
+ // If the bundle has specified an execution environment and the
+ // framework has an execution environment specified, then we must
+ // check for a match.
+ if (!bundleExecEnvStr.equals("")
+ && (fwkExecEnvStr != null)
+ && (fwkExecEnvStr.length() > 0))
+ {
+ StringTokenizer tokens = new StringTokenizer(bundleExecEnvStr, ",");
+ boolean found = false;
+ while (tokens.hasMoreTokens() && !found)
+ {
+ if (fwkExecEnvSet.contains(tokens.nextToken().trim()))
+ {
+ found = true;
+ }
+ }
+ if (!found)
+ {
+ throw new ResolveException(
+ "Execution environment not supported: "
+ + bundleExecEnvStr, module, null);
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates the framework wide execution environment string and a cached Set of
+ * execution environment tokens from the comma delimited list specified by the
+ * system variable 'org.osgi.framework.executionenvironment'.
+ * @param frameworkEnvironment Comma delimited string of provided execution environments
+ **/
+ private static Set parseExecutionEnvironments(String fwkExecEnvStr)
+ {
+ Set newSet = new HashSet();
+ if (fwkExecEnvStr != null)
+ {
+ StringTokenizer tokens = new StringTokenizer(fwkExecEnvStr, ",");
+ while (tokens.hasMoreTokens())
+ {
+ newSet.add(tokens.nextToken().trim());
+ }
+ }
+ return newSet;
+ }
+
+ private static class Packages
+ {
+ public final Map<String, Blame> m_exportedPkgs
+ = new HashMap<String, Blame>();
+ public final Map<String, Blame> m_importedPkgs
+ = new HashMap<String, Blame>();
+ public final Map<String, List<Blame>> m_requiredPkgs
+ = new HashMap<String, List<Blame>>();
+ public final Map<String, List<Blame>> m_usedPkgs
+ = new HashMap<String, List<Blame>>();
+
+ public Packages()
+ {
+ }
+
+ public Packages(Packages packages)
+ {
+ m_exportedPkgs.putAll(packages.m_exportedPkgs);
+ m_importedPkgs.putAll(packages.m_importedPkgs);
+ m_requiredPkgs.putAll(packages.m_requiredPkgs);
+ m_usedPkgs.putAll(packages.m_usedPkgs);
+ }
+
+ public List<String> getExportedAndReexportedPackages()
+ {
+ List<String> pkgs = new ArrayList();
+ for (Entry<String, Blame> entry : m_exportedPkgs.entrySet())
+ {
+ pkgs.add((String)
+ entry.getValue().m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue());
+ }
+ for (Entry<String, List<Blame>> entry : m_requiredPkgs.entrySet())
+ {
+ for (Blame blame : entry.getValue())
+ {
+ Directive dir = blame.m_reqs.get(
+ blame.m_reqs.size() - 1).getDirective(Constants.VISIBILITY_DIRECTIVE);
+ if ((dir != null)
+ && dir.getValue().equals(Constants.VISIBILITY_REEXPORT))
+ {
+ pkgs.add((String)
+ blame.m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue());
+ break;
+ }
+ }
+ }
+ return pkgs;
+ }
+ }
+
+ private static class Blame
+ {
+ public final List<Requirement> m_reqs;
+ public final Capability m_cap;
+
+ public Blame(List<Requirement> reqs, Capability cap)
+ {
+ m_reqs = reqs;
+ m_cap = cap;
+ }
+
+ public String toString()
+ {
+ return m_cap.getModule() + "." + m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue()
+ + " BLAMED ON " + m_reqs;
+ }
+
+ public boolean equals(Object o)
+ {
+ return (o instanceof Blame) && m_reqs.equals(((Blame) o).m_reqs)
+ && m_cap.equals(((Blame) o).m_cap);
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/ResourceNotFoundException.java b/framework/src/main/java/org/apache/felix/framework/resolver/ResourceNotFoundException.java
new file mode 100644
index 0000000..3cb5107
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResourceNotFoundException.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+public class ResourceNotFoundException extends Exception
+{
+ public ResourceNotFoundException(String msg)
+ {
+ super(msg);
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/IWire.java b/framework/src/main/java/org/apache/felix/framework/resolver/Wire.java
similarity index 72%
rename from framework/src/main/java/org/apache/felix/moduleloader/IWire.java
rename to framework/src/main/java/org/apache/felix/framework/resolver/Wire.java
index 65bd320..ba85f50 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/IWire.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Wire.java
@@ -1,57 +1,53 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
*/
-package org.apache.felix.moduleloader;
+package org.apache.felix.framework.resolver;
+import org.apache.felix.framework.resolver.Module;
import java.net.URL;
import java.util.Enumeration;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.Requirement;
-/**
- * This interface represents a directed class/resource loading dependency
- * between two modules, which result when the framework resolves
- * <tt>Import-Package</tt> or <tt>Require-Bundle</tt> declarations. A wire is
- * the means by which a dependent module makes a class/resource request on
- * the providing module.
-**/
-public interface IWire
+public interface Wire
{
/**
* Returns the importing module.
* @return The importing module.
**/
- public IModule getImporter();
+ public Module getImporter();
/**
* Returns the associated requirement from the importing module that
* resulted in the creation of this wire.
* @return
**/
- public IRequirement getRequirement();
+ public Requirement getRequirement();
/**
* Returns the exporting module.
* @return The exporting module.
**/
- public IModule getExporter();
+ public Module getExporter();
/**
* Returns the associated capability from the exporting module that
* satisfies the requirement of the importing module.
* @return
**/
- public ICapability getCapability();
+ public Capability getCapability();
/**
* Returns whether or not the wire has a given package name. For some
* wires, such as ones for Require-Bundle, there may be many packages.
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/WireImpl.java b/framework/src/main/java/org/apache/felix/framework/resolver/WireImpl.java
new file mode 100755
index 0000000..8e36f3b
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/WireImpl.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.net.URL;
+import java.util.Enumeration;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.Requirement;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.CapabilityImpl;
+
+public class WireImpl implements Wire
+{
+ private final Module m_importer;
+ private final Requirement m_req;
+ private final Module m_exporter;
+ private final Capability m_cap;
+
+ public WireImpl(Module importer, Requirement ip, Module exporter, Capability ep)
+ {
+ m_importer = importer;
+ m_req = ip;
+ m_exporter = exporter;
+ m_cap = ep;
+ }
+
+ public Module getImporter()
+ {
+ return m_importer;
+ }
+
+ public Requirement getRequirement()
+ {
+ return m_req;
+ }
+
+ public Module getExporter()
+ {
+ return m_exporter;
+ }
+
+ public Capability getCapability()
+ {
+ return m_cap;
+ }
+
+ public String toString()
+ {
+ return m_req + " (" + m_importer + ") -> " + m_cap + " (" + m_exporter + ")";
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.felix.framework.searchpolicy.IWire#getClass(java.lang.String)
+ */
+ public boolean hasPackage(String pkgName)
+ {
+ return (m_cap.getNamespace().equals(Capability.PACKAGE_NAMESPACE) &&
+ m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue().equals(pkgName));
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.felix.framework.searchpolicy.IWire#getClass(java.lang.String)
+ */
+ public Class getClass(String name) throws ClassNotFoundException
+ {
+ Class clazz = null;
+
+ // Get the package of the target class.
+ String pkgName = Util.getClassPackage(name);
+
+ // Only check when the package of the target class is
+ // the same as the package for the wire.
+ if (m_cap.getNamespace().equals(Capability.PACKAGE_NAMESPACE) &&
+ m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue().equals(pkgName))
+ {
+ // Check the include/exclude filters from the target package
+ // to make sure that the class is actually visible. We delegate
+ // to the exporting module, rather than its content, so it can
+ // it can follow any internal wires it may have (e.g., if the
+ // package has multiple sources).
+// TODO: FELIX3 - Should isIncluded() be part of Capability?
+ if (((CapabilityImpl) m_cap).isIncluded(name))
+ {
+ clazz = m_exporter.getClassByDelegation(name);
+ }
+
+ // If no class was found, then we must throw an exception
+ // since the exporter for this package did not contain the
+ // requested class.
+ if (clazz == null)
+ {
+ throw new ClassNotFoundException(name);
+ }
+ }
+
+ return clazz;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.felix.framework.searchpolicy.IWire#getResource(java.lang.String)
+ */
+ public URL getResource(String name) throws ResourceNotFoundException
+ {
+ URL url = null;
+
+ // Get the package of the target class.
+ String pkgName = Util.getResourcePackage(name);
+
+ // Only check when the package of the target resource is
+ // the same as the package for the wire.
+ if (m_cap.getNamespace().equals(Capability.PACKAGE_NAMESPACE) &&
+ m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue().equals(pkgName))
+ {
+ // Delegate to the exporting module, rather than its
+ // content, so that it can follow any internal wires it may have
+ // (e.g., if the package has multiple sources).
+ url = m_exporter.getResourceByDelegation(name);
+
+ // If no resource was found, then we must throw an exception
+ // since the exporter for this package did not contain the
+ // requested class.
+ if (url == null)
+ {
+ throw new ResourceNotFoundException(name);
+ }
+ }
+
+ return url;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.felix.framework.searchpolicy.IWire#getResources(java.lang.String)
+ */
+ public Enumeration getResources(String name) throws ResourceNotFoundException
+ {
+ Enumeration urls = null;
+
+ // Get the package of the target class.
+ String pkgName = Util.getResourcePackage(name);
+
+ // Only check when the package of the target resource is
+ // the same as the package for the wire.
+ if (m_cap.getNamespace().equals(Capability.PACKAGE_NAMESPACE) &&
+ m_cap.getAttribute(Capability.PACKAGE_ATTR).getValue().equals(pkgName))
+ {
+ urls = m_exporter.getResourcesByDelegation(name);
+
+ // If no resource was found, then we must throw an exception
+ // since the exporter for this package did not contain the
+ // requested class.
+ if (urls == null)
+ {
+ throw new ResourceNotFoundException(name);
+ }
+ }
+
+ return urls;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java b/framework/src/main/java/org/apache/felix/framework/resolver/WireModuleImpl.java
similarity index 78%
rename from framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java
rename to framework/src/main/java/org/apache/felix/framework/resolver/WireModuleImpl.java
index 28671c6..42df1cd 100644
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/WireModuleImpl.java
@@ -16,36 +16,37 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.framework.searchpolicy;
+package org.apache.felix.framework.resolver;
import java.net.URL;
-import java.util.*;
-
+import java.util.Enumeration;
+import java.util.List;
+import org.apache.felix.framework.capabilityset.Capability;
+import org.apache.felix.framework.capabilityset.Requirement;
import org.apache.felix.framework.util.Util;
-import org.apache.felix.moduleloader.*;
-public class R4WireModule implements IWire
+public class WireModuleImpl implements Wire
{
- private final IModule m_importer;
- private final IRequirement m_requirement;
- private final IModule m_exporter;
- private final ICapability m_capability;
- private final Map m_pkgMap;
+ private final Module m_importer;
+ private final Requirement m_requirement;
+ private final Module m_exporter;
+ private final Capability m_capability;
+ private final List<String> m_packages;
- public R4WireModule(IModule importer, IRequirement requirement,
- IModule exporter, ICapability capability, Map pkgMap)
+ public WireModuleImpl(Module importer, Requirement requirement,
+ Module exporter, Capability capability, List<String> packages)
{
m_importer = importer;
m_requirement = requirement;
m_exporter = exporter;
m_capability = capability;
- m_pkgMap = pkgMap;
+ m_packages = packages;
}
/* (non-Javadoc)
* @see org.apache.felix.framework.searchpolicy.IWire#getImporter()
*/
- public IModule getImporter()
+ public Module getImporter()
{
return m_importer;
}
@@ -53,7 +54,7 @@
/* (non-Javadoc)
* @see org.apache.felix.framework.searchpolicy.IWire#getRequirement()
*/
- public IRequirement getRequirement()
+ public Requirement getRequirement()
{
return m_requirement;
}
@@ -61,7 +62,7 @@
/* (non-Javadoc)
* @see org.apache.felix.framework.searchpolicy.IWire#getExporter()
*/
- public IModule getExporter()
+ public Module getExporter()
{
return m_exporter;
}
@@ -69,7 +70,7 @@
/* (non-Javadoc)
* @see org.apache.felix.framework.searchpolicy.IWire#getCapability()
*/
- public ICapability getCapability()
+ public Capability getCapability()
{
return m_capability;
}
@@ -79,7 +80,7 @@
*/
public boolean hasPackage(String pkgName)
{
- return (m_pkgMap.get(pkgName) != null);
+ return m_packages.contains(pkgName);
}
/* (non-Javadoc)
@@ -89,9 +90,7 @@
{
// Get the package of the target class.
String pkgName = Util.getClassPackage(name);
-
- ResolvedPackage rp = (ResolvedPackage) m_pkgMap.get(pkgName);
- if (rp != null)
+ if (m_packages.contains(pkgName))
{
try
{
@@ -119,9 +118,7 @@
{
// Get the package of the target class.
String pkgName = Util.getResourcePackage(name);
-
- ResolvedPackage rp = (ResolvedPackage) m_pkgMap.get(pkgName);
- if (rp != null)
+ if (m_packages.contains(pkgName))
{
URL url = m_exporter.getResourceByDelegation(name);
if (url != null)
@@ -145,10 +142,7 @@
String pkgName = Util.getResourcePackage(name);
// See if we have a resolved package for the resource's package.
- // If so, loop through all package sources and aggregate any
- // matching resource enumerations.
- ResolvedPackage rp = (ResolvedPackage) m_pkgMap.get(pkgName);
- if (rp != null)
+ if (m_packages.contains(pkgName))
{
Enumeration urls = m_exporter.getResourcesByDelegation(name);
if (urls != null)
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/CandidateSet.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/CandidateSet.java
deleted file mode 100644
index f16165c..0000000
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/CandidateSet.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.framework.searchpolicy;
-
-import java.util.List;
-import org.apache.felix.moduleloader.IModule;
-import org.apache.felix.moduleloader.IRequirement;
-
-class CandidateSet
-{
- public static final int NORMAL = 0;
- public final IModule m_module;
- public final IRequirement m_requirement;
- public final List m_candidates;
- public int m_idx = 0;
- public int m_rotated = 0;
-
- public CandidateSet(IModule module, IRequirement requirement, List candidates)
- {
- super();
- m_module = module;
- m_requirement = requirement;
- m_candidates = candidates;
- }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4Wire.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4Wire.java
deleted file mode 100755
index 0eb232b..0000000
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4Wire.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.framework.searchpolicy;
-
-import java.net.URL;
-import java.util.Enumeration;
-
-import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.util.manifestparser.Capability;
-import org.apache.felix.moduleloader.*;
-
-public class R4Wire implements IWire
-{
- private final IModule m_importer;
- private final IRequirement m_requirement;
- private final IModule m_exporter;
- private final ICapability m_capability;
-
- public R4Wire(IModule importer, IRequirement requirement,
- IModule exporter, ICapability capability)
- {
- m_importer = importer;
- m_requirement = requirement;
- m_exporter = exporter;
- m_capability = capability;
- }
-
- /* (non-Javadoc)
- * @see org.apache.felix.framework.searchpolicy.IWire#getImporter()
- */
- public IModule getImporter()
- {
- return m_importer;
- }
-
- /* (non-Javadoc)
- * @see org.apache.felix.framework.searchpolicy.IWire#getRequirement()
- */
- public IRequirement getRequirement()
- {
- return m_requirement;
- }
-
- /* (non-Javadoc)
- * @see org.apache.felix.framework.searchpolicy.IWire#getExporter()
- */
- public IModule getExporter()
- {
- return m_exporter;
- }
-
- /* (non-Javadoc)
- * @see org.apache.felix.framework.searchpolicy.IWire#getCapability()
- */
- public ICapability getCapability()
- {
- return m_capability;
- }
-
- /* (non-Javadoc)
- * @see org.apache.felix.framework.searchpolicy.IWire#getClass(java.lang.String)
- */
- public boolean hasPackage(String pkgName)
- {
- return (m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
- m_capability.getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName));
- }
-
- /* (non-Javadoc)
- * @see org.apache.felix.framework.searchpolicy.IWire#getClass(java.lang.String)
- */
- public Class getClass(String name) throws ClassNotFoundException
- {
- Class clazz = null;
-
- // Get the package of the target class.
- String pkgName = Util.getClassPackage(name);
-
- // Only check when the package of the target class is
- // the same as the package for the wire.
- if (m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
- m_capability.getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
- {
- // Check the include/exclude filters from the target package
- // to make sure that the class is actually visible. We delegate
- // to the exporting module, rather than its content, so it can
- // it can follow any internal wires it may have (e.g., if the
- // package has multiple sources).
- if (m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
- && ((Capability) m_capability).isIncluded(name))
- {
- clazz = m_exporter.getClassByDelegation(name);
- }
-
- // If no class was found, then we must throw an exception
- // since the exporter for this package did not contain the
- // requested class.
- if (clazz == null)
- {
- throw new ClassNotFoundException(name);
- }
- }
-
- return clazz;
- }
-
- /* (non-Javadoc)
- * @see org.apache.felix.framework.searchpolicy.IWire#getResource(java.lang.String)
- */
- public URL getResource(String name) throws ResourceNotFoundException
- {
- URL url = null;
-
- // Get the package of the target class.
- String pkgName = Util.getResourcePackage(name);
-
- // Only check when the package of the target resource is
- // the same as the package for the wire.
- if (m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
- m_capability.getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
- {
- // Delegate to the exporting module, rather than its
- // content, so that it can follow any internal wires it may have
- // (e.g., if the package has multiple sources).
- url = m_exporter.getResourceByDelegation(name);
-
- // If no resource was found, then we must throw an exception
- // since the exporter for this package did not contain the
- // requested class.
- if (url == null)
- {
- throw new ResourceNotFoundException(name);
- }
- }
-
- return url;
- }
-
- /* (non-Javadoc)
- * @see org.apache.felix.framework.searchpolicy.IWire#getResources(java.lang.String)
- */
- public Enumeration getResources(String name) throws ResourceNotFoundException
- {
- Enumeration urls = null;
-
- // Get the package of the target class.
- String pkgName = Util.getResourcePackage(name);
-
- // Only check when the package of the target resource is
- // the same as the package for the wire.
- if (m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
- m_capability.getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
- {
- urls = m_exporter.getResourcesByDelegation(name);
-
- // If no resource was found, then we must throw an exception
- // since the exporter for this package did not contain the
- // requested class.
- if (urls == null)
- {
- throw new ResourceNotFoundException(name);
- }
- }
-
- return urls;
- }
-
- public String toString()
- {
- if (m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- return m_importer + " -> "
- + m_capability.getProperties().get(ICapability.PACKAGE_PROPERTY)
- + " -> " + m_exporter;
- }
- return m_importer + " -> " + m_capability + " -> " + m_exporter;
- }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolveException.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolveException.java
deleted file mode 100755
index e3658e5..0000000
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolveException.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.framework.searchpolicy;
-
-import org.apache.felix.moduleloader.IModule;
-import org.apache.felix.moduleloader.IRequirement;
-
-/**
- * <p>
- * This exception is thrown if a module cannot be resolved. The module
- * that failed to be resolved is recorded, along with the failed import target
- * identifier and version number. If the error was a result of a propagation
- * conflict, then the propagation error flag is set.
- * </p>
- * @see org.apache.felix.moduleloader.search.ImportSearchPolicy#validate(org.apache.felix.moduleloader.Module)
-**/
-public class ResolveException extends Exception
-{
- private IModule m_module = null;
- private IRequirement m_req = null;
-
- /**
- * Constructs an exception with the specified message, module,
- * import identifier, import version number, and propagation flag.
- **/
- public ResolveException(String msg, IModule module, IRequirement req)
- {
- super(msg);
- m_module = module;
- m_req = req;
- }
-
- /**
- * Returns the module that was being resolved.
- * @return the module that was being resolved.
- **/
- public IModule getModule()
- {
- return m_module;
- }
-
- public IRequirement getRequirement()
- {
- return m_req;
- }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java
deleted file mode 100644
index b75ebfa..0000000
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.framework.searchpolicy;
-
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.felix.moduleloader.ICapability;
-
-/**
- * This utility class is a resolved package, which is comprised of a
- * set of <tt>ICapability</tt>s that is calculated by the resolver
- * algorithm. A given resolved package may have a single capability,
- * as is the case with imported packages, or it may have multiple
- * capabilities, as is the case with required bundles.
- */
-class ResolvedPackage
-{
- public final String m_name;
- public final CandidateSet m_cs;
- public final List m_capList = new ArrayList();
-
- public ResolvedPackage(String name, CandidateSet cs)
- {
- super();
- m_name = name;
- m_cs = cs;
- }
-
- public boolean isSubset(ResolvedPackage rp)
- {
- if (m_capList.size() > rp.m_capList.size())
- {
- return false;
- }
- else if (!m_name.equals(rp.m_name))
- {
- return false;
- }
- // Determine if the target set of source modules is a subset.
- return rp.m_capList.containsAll(m_capList);
- }
-
- public Object clone()
- {
- ResolvedPackage rp = new ResolvedPackage(m_name, m_cs);
- rp.m_capList.addAll(m_capList);
- return rp;
- }
-
- public void merge(ResolvedPackage rp)
- {
- // Merge required packages, avoiding duplicate
- // package sources and maintaining ordering.
- for (int capIdx = 0; capIdx < rp.m_capList.size(); capIdx++)
- {
- if (!m_capList.contains(rp.m_capList.get(capIdx)))
- {
- m_capList.add(rp.m_capList.get(capIdx));
- }
- }
- }
-
- public String toString()
- {
- return toString("", new StringBuffer()).toString();
- }
-
- public StringBuffer toString(String padding, StringBuffer sb)
- {
- sb.append(padding);
- sb.append(m_name);
- sb.append(" from [");
- for (int i = 0; i < m_capList.size(); i++)
- {
- ICapability cap = (ICapability) m_capList.get(i);
- sb.append(cap.getModule());
- if ((i + 1) < m_capList.size())
- {
- sb.append(", ");
- }
- }
- sb.append("]");
- return sb;
- }
-}
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java b/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java
deleted file mode 100644
index eb42f0c..0000000
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java
+++ /dev/null
@@ -1,1737 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.framework.searchpolicy;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-import org.apache.felix.framework.Logger;
-import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.util.manifestparser.Capability;
-import org.apache.felix.framework.util.manifestparser.R4Attribute;
-import org.apache.felix.framework.util.manifestparser.R4Directive;
-import org.apache.felix.framework.util.manifestparser.R4Library;
-import org.apache.felix.framework.util.manifestparser.Requirement;
-import org.apache.felix.moduleloader.ICapability;
-import org.apache.felix.moduleloader.IModule;
-import org.apache.felix.moduleloader.IRequirement;
-import org.apache.felix.moduleloader.IWire;
-import org.osgi.framework.Constants;
-
-public class Resolver
-{
- private final Logger m_logger;
-
- // Execution environment.
- private final String m_fwkExecEnvStr;
- private final Set m_fwkExecEnvSet;
-
- // Reusable empty array.
- private static final IWire[] m_emptyWires = new IWire[0];
-
- public Resolver(Logger logger, String fwkExecEnvStr)
- {
- m_logger = logger;
- m_fwkExecEnvStr = (fwkExecEnvStr != null) ? fwkExecEnvStr.trim() : null;
- m_fwkExecEnvSet = parseExecutionEnvironments(fwkExecEnvStr);
- }
-
- // Returns a map of resolved bundles where the key is the module
- // and the value is an array of wires.
- public Map resolve(ResolverState state, IModule rootModule) throws ResolveException
- {
- // If the module is already resolved, then we can just return.
- if (rootModule.isResolved())
- {
- return null;
- }
-
- // This variable maps an unresolved module to a list of candidate
- // sets, where there is one candidate set for each requirement that
- // must be resolved. A candidate set contains the potential canidates
- // available to resolve the requirement and the currently selected
- // candidate index.
- Map candidatesMap = new HashMap();
-
- // The first step is to populate the candidates map. This
- // will use the target module to populate the candidates map
- // with all potential modules that need to be resolved as a
- // result of resolving the target module. The key of the
- // map is a potential module to be resolved and the value is
- // a list of candidate sets, one for each of the module's
- // requirements, where each candidate set contains the potential
- // candidates for resolving the requirement. Not all modules in
- // this map will be resolved, only the target module and
- // any candidates selected to resolve its requirements and the
- // transitive requirements this implies.
- populateCandidatesMap(state, candidatesMap, rootModule);
-
- // The next step is to use the candidates map to determine if
- // the class space for the root module is consistent. This
- // is an iterative process that transitively walks the "uses"
- // relationships of all packages visible from the root module
- // checking for conflicts. If a conflict is found, it "increments"
- // the configuration of currently selected potential candidates
- // and tests them again. If this method returns, then it has found
- // a consistent set of candidates; otherwise, a resolve exception
- // is thrown if it exhausts all possible combinations and could
- // not find a consistent class space.
- findConsistentClassSpace(state, candidatesMap, rootModule);
-
- // The final step is to create the wires for the root module and
- // transitively all modules that are to be resolved from the
- // selected candidates for resolving the root module's imports.
- // When this call returns, each module's wiring and resolved
- // attributes are set. The resulting wiring map is used below
- // to fire resolved events outside of the synchronized block.
- // The resolved module wire map maps a module to its array of
- // wires.
- return populateWireMap(state, candidatesMap, rootModule, new HashMap());
- }
-
- // TODO: RESOLVER - Fix this return type.
- // Return candidate wire in result[0] and wire map in result[1]
- public Object[] resolveDynamicImport(ResolverState state, IModule importer, String pkgName)
- throws ResolveException
- {
- ICapability candidate = null;
- Map resolvedModuleWireMap = null;
-
- // We can only create a dynamic import if the following
- // conditions are met:
- // 1. The package in question is not already imported.
- // 2. The package in question is not accessible via require-bundle.
- // 3. The package in question is not exported by the bundle.
- // 4. The package in question matches a dynamic import of the bundle.
- // The following call checks all of these conditions and returns
- // a matching dynamic requirement if possible.
- IRequirement dynReq = findAllowedDynamicImport(importer, pkgName);
- if (dynReq != null)
- {
- // Create a new requirement based on the dynamic requirement,
- // but substitute the precise package name for which we are
- // looking, because it is not possible to use the potentially
- // wildcarded version in the dynamic requirement.
- R4Directive[] dirs = ((Requirement) dynReq).getDirectives();
- R4Attribute[] attrs = ((Requirement) dynReq).getAttributes();
- R4Attribute[] newAttrs = new R4Attribute[attrs.length];
- System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
- for (int attrIdx = 0; attrIdx < newAttrs.length; attrIdx++)
- {
- if (newAttrs[attrIdx].getName().equals(ICapability.PACKAGE_PROPERTY))
- {
- newAttrs[attrIdx] = new R4Attribute(
- ICapability.PACKAGE_PROPERTY, pkgName, false);
- break;
- }
- }
- IRequirement target = new Requirement(ICapability.PACKAGE_NAMESPACE, dirs, newAttrs);
-
- // See if there is a candidate exporter that satisfies the
- // constrained dynamic requirement.
- try
- {
- // Get "resolved" and "unresolved" candidates and put
- // the "resolved" candidates first.
- List candidates = state.getResolvedCandidates(target, importer);
- candidates.addAll(state.getUnresolvedCandidates(target, importer));
-
- // Take the first candidate that can resolve.
- for (int candIdx = 0;
- (candidate == null) && (candIdx < candidates.size());
- candIdx++)
- {
- try
- {
- // If a map is returned, then the candidate resolved
- // consistently with the importer.
- resolvedModuleWireMap =
- resolveDynamicImportCandidate(
- state, ((ICapability) candidates.get(candIdx)).getModule(),
- importer);
- if (resolvedModuleWireMap != null)
- {
- candidate = (ICapability) candidates.get(candIdx);
- }
- }
- catch (ResolveException ex)
- {
- // Ignore candidates that cannot resolve.
- }
- }
-
- if (candidate != null)
- {
- // Create the wire and add it to the module.
- Object[] result = new Object[2];
- result[0] = new R4Wire(
- importer, dynReq, candidate.getModule(),
- candidate);
- result[1] = resolvedModuleWireMap;
- return result;
- }
- }
- catch (Exception ex)
- {
- m_logger.log(Logger.LOG_ERROR, "Unable to dynamically import package.", ex);
- }
- }
-
- return null;
- }
-
- public static IRequirement findAllowedDynamicImport(IModule importer, String pkgName)
- {
- // We cannot import the default package, so return null in that case.
- if (pkgName.length() == 0)
- {
- return null;
- }
-
- // If any of the module exports this package, then we cannot
- // attempt to dynamically import it.
- ICapability[] caps = importer.getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
- {
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
- && caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
- {
- return null;
- }
- }
- // If any of our wires have this package, then we cannot
- // attempt to dynamically import it.
- IWire[] wires = importer.getWires();
- for (int i = 0; (wires != null) && (i < wires.length); i++)
- {
- if (wires[i].hasPackage(pkgName))
- {
- return null;
- }
- }
-
- // Loop through the importer's dynamic requirements to determine if
- // there is a matching one for the package from which we want to
- // load a class.
- IRequirement[] dynamics = importer.getDynamicRequirements();
- for (int dynIdx = 0;
- (dynamics != null) && (dynIdx < dynamics.length);
- dynIdx++)
- {
- // First check to see if the dynamic requirement matches the
- // package name; this means we have to do wildcard matching.
- String dynPkgName = ((Requirement) dynamics[dynIdx]).getTargetName();
- boolean wildcard = (dynPkgName.lastIndexOf(".*") >= 0);
- // Remove the "*", but keep the "." if wildcarded.
- dynPkgName = (wildcard)
- ? dynPkgName.substring(0, dynPkgName.length() - 1) : dynPkgName;
- // If the dynamic requirement matches the package name, then
- // create a new requirement for the specific package.
- if (dynPkgName.equals("*") ||
- pkgName.equals(dynPkgName) ||
- (wildcard && pkgName.startsWith(dynPkgName)))
- {
- return dynamics[dynIdx];
- }
- }
-
- return null;
- }
-
- private Map resolveDynamicImportCandidate(
- ResolverState state, IModule provider, IModule importer)
- throws ResolveException
- {
- // If the provider of the dynamically imported package is not
- // resolved, then we need to calculate the candidates to resolve
- // it and see if there is a consistent class space for the
- // provider. If there is no consistent class space, then a resolve
- // exception is thrown.
- Map candidatesMap = new HashMap();
- if (!provider.isResolved())
- {
- populateCandidatesMap(state, candidatesMap, provider);
- findConsistentClassSpace(state, candidatesMap, provider);
- }
-
- // If the provider can be successfully resolved, then verify that
- // its class space is consistent with the existing class space of the
- // module that instigated the dynamic import.
- Map moduleMap = new HashMap();
- Map importerPkgMap = getModulePackages(moduleMap, importer, candidatesMap);
-
- // Now we need to calculate the "uses" constraints of every package
- // accessible to the provider module based on its current candidates.
- Map usesMap = calculateUsesConstraints(provider, moduleMap, candidatesMap);
-
- // Verify that none of the provider's implied "uses" constraints
- // in the uses map conflict with anything in the importing module's
- // package map.
- for (Iterator iter = usesMap.entrySet().iterator(); iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
-
- // For the given "used" package, get that package from the
- // importing module's package map, if present.
- ResolvedPackage rp = (ResolvedPackage) importerPkgMap.get(entry.getKey());
-
- // If the "used" package is also visible to the importing
- // module, make sure there is no conflicts in the implied
- // "uses" constraints.
- if (rp != null)
- {
- // Clone the resolve package so we can modify it.
- rp = (ResolvedPackage) rp.clone();
-
- // Loop through all implied "uses" constraints for the current
- // "used" package and verify that all packages are
- // compatible with the packages of the importing module's
- // package map.
- List constraintList = (List) entry.getValue();
- for (int constIdx = 0; constIdx < constraintList.size(); constIdx++)
- {
- // Get a specific "uses" constraint for the current "used"
- // package.
- ResolvedPackage rpUses = (ResolvedPackage) constraintList.get(constIdx);
- // Determine if the implied "uses" constraint is compatible with
- // the improting module's packages for the given "used"
- // package. They are compatible if one is the subset of the other.
- // Retain the union of the two sets if they are compatible.
- if (rpUses.isSubset(rp))
- {
- // Do nothing because we already have the superset.
- }
- else if (rp.isSubset(rpUses))
- {
- // Keep the superset, i.e., the union.
- rp.m_capList.clear();
- rp.m_capList.addAll(rpUses.m_capList);
- }
- else
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "Constraint violation for " + importer
- + " detected; module can see "
- + rp + " and " + rpUses);
- return null;
- }
- }
- }
- }
-
- return populateWireMap(state, candidatesMap, provider, new HashMap());
- }
-
- private void populateCandidatesMap(
- ResolverState state, Map candidatesMap, IModule targetModule)
- throws ResolveException
- {
- // Detect cycles.
- if (candidatesMap.containsKey(targetModule))
- {
- return;
- }
-
- // Verify that any required execution environment is satisfied.
- verifyExecutionEnvironment(m_fwkExecEnvStr, m_fwkExecEnvSet, targetModule);
-
- // Verify that any native libraries match the current platform.
- verifyNativeLibraries(targetModule);
-
- // Finally, resolve any dependencies the module may have.
-
- // Add target module to the candidates map so we can detect cycles.
- candidatesMap.put(targetModule, null);
-
- // Create list to hold the resolving candidate sets for the target
- // module's requirements.
- List candSetList = new ArrayList();
-
- // Loop through each requirement and calculate its resolving
- // set of candidates.
- IRequirement[] reqs = targetModule.getRequirements();
- for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
- {
- // Get the candidates from the "resolved" and "unresolved"
- // package maps. The "resolved" candidates have higher priority
- // than "unresolved" ones, so put the "resolved" candidates
- // at the front of the list of candidates.
- List candidates = state.getResolvedCandidates(reqs[reqIdx], targetModule);
- candidates.addAll(state.getUnresolvedCandidates(reqs[reqIdx], targetModule));
-
- // If we have candidates, then we need to recursively populate
- // the resolver map with each of them.
- ResolveException rethrow = null;
- if (candidates.size() > 0)
- {
- for (Iterator it = candidates.iterator(); it.hasNext(); )
- {
- ICapability candidate = (ICapability) it.next();
-
- try
- {
- // Only populate the resolver map with modules that
- // are not already resolved.
- if (!candidate.getModule().isResolved())
- {
- populateCandidatesMap(
- state, candidatesMap, candidate.getModule());
- }
- }
- catch (ResolveException ex)
- {
- // If we received a resolve exception, then the
- // current candidate is not resolvable for some
- // reason and should be removed from the list of
- // candidates. For now, just null it.
- it.remove();
- rethrow = ex;
- }
- }
- }
-
- // If no candidates exist at this point, then throw a
- // resolve exception unless the import is optional.
- if ((candidates.size() == 0) && !reqs[reqIdx].isOptional())
- {
- // Remove invalid candidate and any cycle byproduct resolved modules.
- removeInvalidCandidate(targetModule, candidatesMap, new ArrayList());
-
- // If we have received an exception while trying to populate
- // the candidates map, rethrow that exception since it might
- // be useful. NOTE: This is not necessarily the "only"
- // correct exception, since it is possible that multiple
- // candidates were not resolvable, but it is better than
- // nothing.
- if (rethrow != null)
- {
- throw rethrow;
- }
- else
- {
- throw new ResolveException(
- "Unable to resolve.", targetModule, reqs[reqIdx]);
- }
- }
- else if (candidates.size() > 0)
- {
- candSetList.add(
- new CandidateSet(targetModule, reqs[reqIdx], candidates));
- }
- }
-
- // Now that the module's candidates have been calculated, add the
- // candidate set list to the candidates map to be used for calculating
- // uses constraints and ultimately wires.
- candidatesMap.put(targetModule, candSetList);
- }
-
- private static void removeInvalidCandidate(
- IModule invalidModule, Map candidatesMap, List invalidList)
- {
-// TODO: PERFORMANCE - This could be quicker if we kept track of who depended on whom,
-// or only those modules used as candidates or those in a cycle.
-
- // Remove the invalid module's candidates set list from the candidates map,
- // since it should only contain entries for validly resolved modules.
- candidatesMap.remove(invalidModule);
-
- // Loop through each candidate set list in the candidates map to try
- // to find references to the invalid module.
- for (Iterator itCandidatesMap = candidatesMap.entrySet().iterator();
- itCandidatesMap.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) itCandidatesMap.next();
- IModule module = (IModule) entry.getKey();
- List candSetList = (List) entry.getValue();
- if (candSetList != null)
- {
- // Loop through each candidate set in the candidate set list
- // to search for the invalid module.
- for (Iterator itCandSetList = candSetList.iterator(); itCandSetList.hasNext(); )
- {
- // Loop through the candidate in the candidate set and remove
- // the invalid module if it is found.
- CandidateSet cs = (CandidateSet) itCandSetList.next();
- for (Iterator itCandidates = cs.m_candidates.iterator();
- itCandidates.hasNext(); )
- {
- // If the invalid module is a candidate, then remove it from
- // the candidate set.
- ICapability candCap = (ICapability) itCandidates.next();
- if (candCap.getModule().equals(invalidModule))
- {
- itCandidates.remove();
-
- // If there are no more candidates in the candidate set, then
- // remove it from the candidate set list.
- if (cs.m_candidates.size() == 0)
- {
- itCandSetList.remove();
-
- // If the requirement is not optional, then add the module
- // to a list which will be removed after removing the current
- // invalid module.
- if (!cs.m_requirement.isOptional() && (module != invalidModule)
- && !invalidList.contains(module))
- {
- invalidList.add(module);
- }
- }
- break;
- }
- }
- }
- }
- }
-
- if (!invalidList.isEmpty())
- {
- while (!invalidList.isEmpty())
- {
- IModule m = (IModule) invalidList.remove(0);
- removeInvalidCandidate(m, candidatesMap, invalidList);
- }
- }
- }
-
- // This flag indicates whether candidates have been rotated due to a
- // "uses" constraint conflict. If so, then it is not necessary to perform
- // a permutation, since rotating the candidates selected a new permutation.
- // This part of an attempt to perform smarter permutations.
- private boolean m_candidatesRotated = false;
-
- private void findConsistentClassSpace(
- ResolverState state, Map candidatesMap, IModule rootModule)
- throws ResolveException
- {
- List candidatesList = null;
-
- // The reusable module map maps a module to a map of
- // resolved packages that are accessible by the given
- // module. The set of resolved packages is calculated
- // from the current candidates of the candidates map
- // and the module's metadata.
- Map moduleMap = new HashMap();
-
- // Reusable map used to test for cycles.
- Map cycleMap = new HashMap();
-
- // Test the current potential candidates to determine if they
- // are consistent. Keep looping until we find a consistent
- // set or an exception is thrown.
- while (!isSingletonConsistent(state, rootModule, moduleMap, candidatesMap) ||
- !isClassSpaceConsistent(rootModule, moduleMap, cycleMap, candidatesMap))
- {
- // The incrementCandidateConfiguration() method requires
- // ordered access to the candidates map, so we will create
- // a reusable list once right here.
- if (candidatesList == null)
- {
- candidatesList = new ArrayList();
- for (Iterator iter = candidatesMap.entrySet().iterator();
- iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
- candidatesList.add(entry.getValue());
- }
-
- // Sort the bundles candidate sets according to a weighting
- // based on how many multi-candidate requirements each has.
- // The idea is to push bundles with more potential candidate
- // permutations to the front so we can permutate over them
- // more quickly, since they are likely to have more issues.
- Collections.sort(candidatesList, new Comparator() {
- public int compare(Object o1, Object o2)
- {
- int w1 = calculateWeight((List) o1);
- int w2 = calculateWeight((List) o2);
- if (w1 < w2)
- {
- return -1;
- }
- else if (w1 > w2)
- {
- return 1;
- }
- return 0;
- }
-
- private int calculateWeight(List candSetList)
- {
- int weight = 0;
- for (int csIdx = 0; csIdx < candSetList.size(); csIdx++)
- {
- CandidateSet cs = (CandidateSet) candSetList.get(csIdx);
- if ((cs.m_candidates != null) && (cs.m_candidates.size() > 1))
- {
- weight += cs.m_candidates.size();
- }
- }
- return -weight;
- }
- });
- }
-
- // Increment the candidate configuration to a new permutation so
- // we can test again, unless some candidates have been rotated.
- // In that case, we re-test the current permutation, since rotating
- // the candidates effectively selects a new permutation.
- if (!m_candidatesRotated)
- {
- incrementCandidateConfiguration(candidatesList);
- }
- else
- {
- m_candidatesRotated = false;
- }
-
- // Clear the module map.
- moduleMap.clear();
-
- // Clear the cycle map.
- cycleMap.clear();
- }
- }
-
- /**
- * This methd checks to see if the target module and any of the candidate
- * modules to resolve its dependencies violate any singleton constraints.
- * Actually, it just creates a map of resolved singleton modules and then
- * delegates all checking to another recursive method.
- *
- * @param targetModule the module that is the root of the tree of modules to check.
- * @param moduleMap a map to cache the package space of each module.
- * @param candidatesMap a map containing the all candidates to resolve all
- * dependencies for all modules.
- * @return <tt>true</tt> if all candidates are consistent with respect to singletons,
- * <tt>false</tt> otherwise.
- **/
- private boolean isSingletonConsistent(
- ResolverState state, IModule targetModule, Map moduleMap, Map candidatesMap)
- {
- // Create a map of all resolved singleton modules.
- Map singletonMap = new HashMap();
- IModule[] modules = state.getModules();
- for (int i = 0; (modules != null) && (i < modules.length); i++)
- {
- if (modules[i].isResolved() && isSingleton(modules[i]))
- {
- String symName = modules[i].getSymbolicName();
- singletonMap.put(symName, symName);
- }
- }
-
- return areCandidatesSingletonConsistent(
- state, targetModule, singletonMap, moduleMap, new HashMap(), candidatesMap);
- }
-
- /**
- * This method recursive checks the target module and all of its transitive
- * dependency modules to verify that they do not violate a singleton constraint.
- * If the target module is a singleton, then it checks that againts existing
- * singletons. Then it checks all current unresolved candidates recursively.
- *
- * @param targetModule the module that is the root of the tree of modules to check.
- * @param singletonMap the current map of singleton symbolic names.
- * @param moduleMap a map to cache the package space of each module.
- * @param cycleMap a map to detect cycles.
- * @param candidatesMap a map containing the all candidates to resolve all
- * dependencies for all modules.
- * @return <tt>true</tt> if all candidates are consistent with respect to singletons,
- * <tt>false</tt> otherwise.
- **/
- private boolean areCandidatesSingletonConsistent(
- ResolverState state, IModule targetModule,
- Map singletonMap, Map moduleMap, Map cycleMap, Map candidatesMap)
- {
- // If we are in a cycle, then assume true for now.
- if (cycleMap.get(targetModule) != null)
- {
- return true;
- }
-
- // Record the target module in the cycle map.
- cycleMap.put(targetModule, targetModule);
-
- // Check to see if the targetModule violates a singleton.
- // If not and it is a singleton, then add it to the singleton
- // map since it will constrain other singletons.
- String symName = targetModule.getSymbolicName();
- boolean isSingleton = isSingleton(targetModule);
- if (isSingleton && singletonMap.containsKey(symName))
- {
- return false;
- }
- else if (isSingleton)
- {
- singletonMap.put(symName, symName);
- }
-
- // Get the package space of the target module.
- Map pkgMap = null;
- try
- {
- pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
- }
- catch (ResolveException ex)
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "Constraint violation for " + targetModule + " detected.",
- ex);
- return false;
- }
-
- // Loop through all of the target module's accessible packages and
- // verify that all packages are consistent.
- for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
- // Get the resolved package, which contains the set of all
- // packages for the given package.
- ResolvedPackage rp = (ResolvedPackage) entry.getValue();
- // Loop through each capability and test if it is consistent.
- for (int capIdx = 0; capIdx < rp.m_capList.size(); capIdx++)
- {
- // If the module for this capability is not resolved, then
- // we have to see if resolving it would violate a singleton
- // constraint.
- ICapability cap = (ICapability) rp.m_capList.get(capIdx);
- if (!cap.getModule().isResolved())
- {
- return areCandidatesSingletonConsistent(
- state, cap.getModule(), singletonMap, moduleMap, cycleMap, candidatesMap);
- }
- }
- }
-
- return true;
- }
-
- /**
- * Returns true if the specified module is a singleton
- * (i.e., directive singleton:=true in Bundle-SymbolicName).
- *
- * @param module the module to check for singleton status.
- * @return true if the module is a singleton, false otherwise.
- **/
- private static boolean isSingleton(IModule module)
- {
- final ICapability[] modCaps = Util.getCapabilityByNamespace(
- module, Capability.MODULE_NAMESPACE);
- if (modCaps == null || modCaps.length == 0)
- {
- // this should never happen?
- return false;
- }
- final R4Directive[] dirs = ((Capability) modCaps[0]).getDirectives();
- for (int dirIdx = 0; (dirs != null) && (dirIdx < dirs.length); dirIdx++)
- {
- if (dirs[dirIdx].getName().equalsIgnoreCase(Constants.SINGLETON_DIRECTIVE)
- && Boolean.valueOf(dirs[dirIdx].getValue()).booleanValue())
- {
- return true;
- }
- }
- return false;
- }
-
- private boolean isClassSpaceConsistent(
- IModule targetModule, Map moduleMap, Map cycleMap, Map candidatesMap)
- {
-//System.out.println("isClassSpaceConsistent("+targetModule+")");
- // If we are in a cycle, then assume true for now.
- if (cycleMap.get(targetModule) != null)
- {
- return true;
- }
-
- // Record the target module in the cycle map.
- cycleMap.put(targetModule, targetModule);
-
- // Get the package map for the target module, which is a
- // map of all packages accessible to the module and their
- // associated capabilities.
- Map pkgMap = null;
- try
- {
- pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
- }
- catch (ResolveException ex)
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "Constraint violation for " + targetModule + " detected.",
- ex);
- return false;
- }
-
- // Loop through all of the target module's accessible packages and
- // verify that all packages are consistent.
- for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
- // Get the resolved package, which contains the set of all
- // capabilities for the given package.
- ResolvedPackage rp = (ResolvedPackage) entry.getValue();
- // Loop through each capability and test if it is consistent.
- for (int capIdx = 0; capIdx < rp.m_capList.size(); capIdx++)
- {
- ICapability cap = (ICapability) rp.m_capList.get(capIdx);
- if (!isClassSpaceConsistent(cap.getModule(), moduleMap, cycleMap, candidatesMap))
- {
- return false;
- }
- }
- }
-
- // Now we need to calculate the "uses" constraints of every package
- // accessible to the target module based on the current candidates.
- Map usesMap = null;
- try
- {
- usesMap = calculateUsesConstraints(targetModule, moduleMap, candidatesMap);
- }
- catch (ResolveException ex)
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "Constraint violation for " + targetModule + " detected.",
- ex);
- return false;
- }
-
- // Verify that none of the implied "uses" constraints in the uses map
- // conflict with anything in the target module's package map.
- for (Iterator iter = usesMap.entrySet().iterator(); iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
-
- // For the given "used" package, get that package from the
- // target module's package map, if present.
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
-
- // If the "used" package is also visible to the target module,
- // make sure there is no conflicts in the implied "uses"
- // constraints.
- if (rp != null)
- {
- // Clone the resolve package so we can modify it.
- rp = (ResolvedPackage) rp.clone();
-
- // Loop through all implied "uses" constraints for the current
- // "used" package and verify that all packages are
- // compatible with the packages of the root module's
- // package map.
- List constraintList = (List) entry.getValue();
- for (int constIdx = 0; constIdx < constraintList.size(); constIdx++)
- {
- // Get a specific "uses" constraint for the current "used"
- // package.
- ResolvedPackage rpUses = (ResolvedPackage) constraintList.get(constIdx);
- // Determine if the implied "uses" constraint is compatible with
- // the target module's packages for the given "used"
- // package. They are compatible if one is the subset of the other.
- // Retain the union of the two sets if they are compatible.
- if (rpUses.isSubset(rp))
- {
- // Do nothing because we already have the superset.
- }
- else if (rp.isSubset(rpUses))
- {
- // Keep the superset, i.e., the union.
- rp.m_capList.clear();
- rp.m_capList.addAll(rpUses.m_capList);
- }
- else
- {
- m_logger.log(
- Logger.LOG_DEBUG,
- "Constraint violation for " + targetModule
- + " detected; module can see "
- + rp + " and " + rpUses);
-
- // If the resolved package has a candidate set, then
- // attempt to directly rotate the candidates to fix the
- // "uses" constraint conflict. The idea is rather than
- // blinding incrementing to the next permutation, we will
- // try to target the permutation to the bundle with a
- // conflict, which in some cases will be smarter. Only
- // rotate the candidates if we have more than one and we
- // haven't already rotated them completely.
- if ((rp.m_cs != null) && (rp.m_cs.m_candidates.size() > 1)
- && (rp.m_cs.m_rotated < rp.m_cs.m_candidates.size()))
- {
- // Rotate candidates.
- ICapability first = (ICapability) rp.m_cs.m_candidates.get(0);
- for (int i = 1; i < rp.m_cs.m_candidates.size(); i++)
- {
- rp.m_cs.m_candidates.set(i - 1, rp.m_cs.m_candidates.get(i));
- }
- rp.m_cs.m_candidates.set(rp.m_cs.m_candidates.size() - 1, first);
- rp.m_cs.m_rotated++;
- m_candidatesRotated = true;
- }
-
- return false;
- }
- }
- }
- }
-
- return true;
- }
-
- private static Map calculateUsesConstraints(
- IModule targetModule, Map moduleMap, Map candidatesMap)
- throws ResolveException
- {
-//System.out.println("calculateUsesConstraints("+targetModule+")");
- // Map to store calculated uses constraints. This maps a
- // package name to a list of resolved packages, where each
- // resolved package represents a constraint on anyone
- // importing the given package name. This map is returned
- // by this method.
- Map usesMap = new HashMap();
-
- // Re-usable map to detect cycles.
- Map cycleMap = new HashMap();
-
- // Get all packages accessible by the target module.
- Map pkgMap = getModulePackages(moduleMap, targetModule, candidatesMap);
-
- // Each package accessible from the target module is potentially
- // comprised of one or more capabilities. The "uses" constraints
- // implied by all capabilities must be calculated and combined to
- // determine the complete set of implied "uses" constraints for
- // each package accessible by the target module.
- for (Iterator iter = pkgMap.entrySet().iterator(); iter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) iter.next();
- ResolvedPackage rp = (ResolvedPackage) entry.getValue();
- for (int capIdx = 0; capIdx < rp.m_capList.size(); capIdx++)
- {
- usesMap = calculateUsesConstraints(
- (ICapability) rp.m_capList.get(capIdx),
- moduleMap, usesMap, cycleMap, candidatesMap);
- }
- }
- return usesMap;
- }
-
- private static Map calculateUsesConstraints(
- ICapability capTarget, Map moduleMap, Map usesMap,
- Map cycleMap, Map candidatesMap)
- throws ResolveException
- {
-//System.out.println("calculateUsesConstraints2("+psTarget.m_module+")");
- // If we are in a cycle, then return for now.
- if (cycleMap.get(capTarget) != null)
- {
- return usesMap;
- }
-
- // Record the target capability in the cycle map.
- cycleMap.put(capTarget, capTarget);
-
- // Get all packages accessible from the module of the
- // target capability.
- Map pkgMap = getModulePackages(moduleMap, capTarget.getModule(), candidatesMap);
-
- // Cast to implementation class to get access to cached data.
- Capability cap = (Capability) capTarget;
-
- // Loop through all "used" packages of the capability.
- for (int i = 0; i < cap.getUses().length; i++)
- {
- // The target capability's module should have a resolved package
- // for the "used" package in its set of accessible packages,
- // since it claims to use it, so get the associated resolved
- // package.
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(cap.getUses()[i]);
-
- // In general, the resolved package should not be null,
- // but check for safety.
- if (rp != null)
- {
- // First, iterate through all capabilities for the resolved
- // package associated with the current "used" package and calculate
- // and combine the "uses" constraints for each package.
- for (int srcIdx = 0; srcIdx < rp.m_capList.size(); srcIdx++)
- {
- usesMap = calculateUsesConstraints(
- (ICapability) rp.m_capList.get(srcIdx),
- moduleMap, usesMap, cycleMap, candidatesMap);
- }
-
- // Then, add the resolved package for the current "used" package
- // as a "uses" constraint too; add it to an existing constraint
- // list if the current "used" package is already in the uses map.
- List constraintList = (List) usesMap.get(cap.getUses()[i]);
- if (constraintList == null)
- {
- constraintList = new ArrayList();
- }
- constraintList.add(rp);
- usesMap.put(cap.getUses()[i], constraintList);
- }
- }
-
- return usesMap;
- }
-
- private static Map getModulePackages(Map moduleMap, IModule module, Map candidatesMap)
- throws ResolveException
- {
- Map map = (Map) moduleMap.get(module);
-
- if (map == null)
- {
- map = calculateModulePackages(module, candidatesMap);
- moduleMap.put(module, map);
- }
- return map;
- }
-
- /**
- * <p>
- * Calculates the module's set of accessible packages and their
- * assocaited package capabilities. This method uses the current candidates
- * for resolving the module's requirements from the candidate map
- * to calculate the module's accessible packages.
- * </p>
- * @param module the module whose package map is to be calculated.
- * @param candidatesMap the map of potential candidates for resolving
- * the module's requirements.
- * @return a map of the packages accessible to the specified module where
- * the key of the map is the package name and the value of the map
- * is a ResolvedPackage.
- **/
- private static Map calculateModulePackages(IModule module, Map candidatesMap)
- throws ResolveException
- {
-//System.out.println("calculateModulePackages("+module+")");
- Map importedPackages = calculateImportedPackages(module, candidatesMap);
- Map exportedPackages = calculateExportedPackages(module);
- Map requiredPackages = calculateRequiredPackages(module, candidatesMap);
-
- // Merge exported packages into required packages. If a package is both
- // exported and required, then append the exported package to the end of
- // the require packages; otherwise just add it to the package map.
- for (Iterator i = exportedPackages.entrySet().iterator(); i.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) i.next();
- ResolvedPackage rpReq = (ResolvedPackage) requiredPackages.get(entry.getKey());
- if (rpReq != null)
- {
- // Merge exported and required packages, avoiding duplicate
- // packages and maintaining ordering.
- ResolvedPackage rpExport = (ResolvedPackage) entry.getValue();
- rpReq.merge(rpExport);
- }
- else
- {
- requiredPackages.put(entry.getKey(), entry.getValue());
- }
- }
-
- // Merge imported packages into required packages. Imports overwrite
- // any required and/or exported package.
- for (Iterator i = importedPackages.entrySet().iterator(); i.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) i.next();
- requiredPackages.put(entry.getKey(), entry.getValue());
- }
-
- return requiredPackages;
- }
-
- private static Map calculateImportedPackages(IModule targetModule, Map candidatesMap)
- throws ResolveException
- {
- return (candidatesMap.get(targetModule) == null)
- ? calculateImportedPackagesResolved(targetModule)
- : calculateImportedPackagesUnresolved(targetModule, candidatesMap);
- }
-
- private static Map calculateImportedPackagesUnresolved(IModule targetModule, Map candidatesMap)
- throws ResolveException
- {
-//System.out.println("calculateImportedPackagesUnresolved("+targetModule+")");
- Map pkgMap = new HashMap();
-
- // Get the candidate set list to get all candidates for
- // all of the target module's requirements.
- List candSetList = (List) candidatesMap.get(targetModule);
-
- // Loop through all candidate sets that represent import dependencies
- // for the target module and add the current candidate's packages
- // to the imported package map.
- for (int candSetIdx = 0;
- (candSetList != null) && (candSetIdx < candSetList.size());
- candSetIdx++)
- {
- CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
- ICapability candCap = (ICapability) cs.m_candidates.get(cs.m_idx);
-
- if (candCap.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- String pkgName = (String)
- candCap.getProperties().get(ICapability.PACKAGE_PROPERTY);
-
- ResolvedPackage rp = new ResolvedPackage(pkgName, cs);
- rp.m_capList.add(candCap);
- pkgMap.put(rp.m_name, rp);
- }
- }
-
- return pkgMap;
- }
-
- private static Map calculateImportedPackagesResolved(IModule targetModule)
- throws ResolveException
- {
-//System.out.println("calculateImportedPackagesResolved("+targetModule+")");
- Map pkgMap = new HashMap();
-
- // Loop through the target module's wires for package
- // dependencies and add the resolved packages to the
- // imported package map.
- IWire[] wires = targetModule.getWires();
- for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
- {
- if (wires[wireIdx].getCapability().getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- String pkgName = (String)
- wires[wireIdx].getCapability().getProperties().get(ICapability.PACKAGE_PROPERTY);
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
- rp = (rp == null) ? new ResolvedPackage(pkgName, null) : rp;
- rp.m_capList.add(wires[wireIdx].getCapability());
- pkgMap.put(rp.m_name, rp);
- }
- }
-
- return pkgMap;
- }
-
- private static Map calculateExportedPackages(IModule targetModule)
- {
-//System.out.println("calculateExportedPackages("+targetModule+")");
- Map pkgMap = new HashMap();
-
- // Loop through the target module's capabilities that represent
- // exported packages and add them to the exported package map.
- ICapability[] caps = targetModule.getCapabilities();
- for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
- {
- if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- String pkgName = (String)
- caps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
- rp = (rp == null) ? new ResolvedPackage(pkgName, null) : rp;
- rp.m_capList.add(caps[capIdx]);
- pkgMap.put(rp.m_name, rp);
- }
- }
-
- return pkgMap;
- }
-
- private static Map calculateRequiredPackages(IModule targetModule, Map candidatesMap)
- {
- return (candidatesMap.get(targetModule) == null)
- ? calculateRequiredPackagesResolved(targetModule)
- : calculateRequiredPackagesUnresolved(targetModule, candidatesMap);
- }
-
- private static Map calculateRequiredPackagesUnresolved(IModule targetModule, Map candidatesMap)
- {
-//System.out.println("calculateRequiredPackagesUnresolved("+targetModule+")");
- Map pkgMap = new HashMap();
-
- // Loop through target module's candidate list for candidates
- // for its module dependencies and merge re-exported packages.
- List candSetList = (List) candidatesMap.get(targetModule);
- for (int candSetIdx = 0;
- (candSetList != null) && (candSetIdx < candSetList.size());
- candSetIdx++)
- {
- CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
- ICapability candCap = (ICapability) cs.m_candidates.get(cs.m_idx);
-
- // If the capabaility is a module dependency, then flatten it to packages.
- if (candCap.getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- // Calculate transitively required packages.
- Map cycleMap = new HashMap();
- cycleMap.put(targetModule, targetModule);
- Map requireMap =
- calculateExportedAndReexportedPackages(
- candCap, candidatesMap, cycleMap);
-
- // Take the flattened required package map for the current
- // module dependency and merge it into the existing map
- // of required packages.
- for (Iterator reqIter = requireMap.entrySet().iterator(); reqIter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) reqIter.next();
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
- if (rp != null)
- {
- // Merge required packages, avoiding duplicate
- // packages and maintaining ordering.
- ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
- rp.merge(rpReq);
- }
- else
- {
- pkgMap.put(entry.getKey(), entry.getValue());
- }
- }
- }
- }
-
- return pkgMap;
- }
-
- private static Map calculateRequiredPackagesResolved(IModule targetModule)
- {
-//System.out.println("calculateRequiredPackagesResolved("+targetModule+")");
- Map pkgMap = new HashMap();
-
- // Loop through target module's wires for module dependencies
- // and merge re-exported packages.
- IWire[] wires = targetModule.getWires();
- for (int i = 0; (wires != null) && (i < wires.length); i++)
- {
- // If the wire is a module dependency, then flatten it to packages.
- if (wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- // Calculate transitively required packages.
- // We can call calculateExportedAndReexportedPackagesResolved()
- // directly, since we know all dependencies have to be resolved
- // because this module itself is resolved.
- Map cycleMap = new HashMap();
- cycleMap.put(targetModule, targetModule);
- Map requireMap =
- calculateExportedAndReexportedPackagesResolved(
- wires[i].getExporter(), cycleMap);
-
- // Take the flattened required package map for the current
- // module dependency and merge it into the existing map
- // of required packages.
- for (Iterator reqIter = requireMap.entrySet().iterator(); reqIter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) reqIter.next();
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(entry.getKey());
- if (rp != null)
- {
- // Merge required packages, avoiding duplicate
- // packages and maintaining ordering.
- ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
- rp.merge(rpReq);
- }
- else
- {
- pkgMap.put(entry.getKey(), entry.getValue());
- }
- }
- }
- }
-
- return pkgMap;
- }
-
- private static Map calculateExportedAndReexportedPackages(
- ICapability capTarget, Map candidatesMap, Map cycleMap)
- {
- return (candidatesMap.get(capTarget.getModule()) == null)
- ? calculateExportedAndReexportedPackagesResolved(capTarget.getModule(), cycleMap)
- : calculateExportedAndReexportedPackagesUnresolved(capTarget, candidatesMap, cycleMap);
- }
-
- private static Map calculateExportedAndReexportedPackagesUnresolved(
- ICapability capTarget, Map candidatesMap, Map cycleMap)
- {
-//System.out.println("calculateExportedAndReexportedPackagesUnresolved("+psTarget.m_module+")");
- Map pkgMap = new HashMap();
-
- if (cycleMap.get(capTarget.getModule()) != null)
- {
- return pkgMap;
- }
-
- cycleMap.put(capTarget.getModule(), capTarget.getModule());
-
- // Loop through all current candidates for target module's dependencies
- // and calculate the module's complete set of required packages (and
- // their associated packages) and the complete set of required
- // packages to be re-exported.
- Map allRequiredMap = new HashMap();
- Map reexportedPkgMap = new HashMap();
- List candSetList = (List) candidatesMap.get(capTarget.getModule());
- for (int candSetIdx = 0; candSetIdx < candSetList.size(); candSetIdx++)
- {
- CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
- ICapability candCap = (ICapability) cs.m_candidates.get(cs.m_idx);
-
- // If the candidate is resolving a module dependency, then
- // flatten the required packages if they are re-exported.
- if (candCap.getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- // Determine if required packages are re-exported.
- boolean reexport = false;
- R4Directive[] dirs = ((Requirement) cs.m_requirement).getDirectives();
- for (int dirIdx = 0;
- !reexport && (dirs != null) && (dirIdx < dirs.length); dirIdx++)
- {
- if (dirs[dirIdx].getName().equals(Constants.VISIBILITY_DIRECTIVE)
- && dirs[dirIdx].getValue().equals(Constants.VISIBILITY_REEXPORT))
- {
- reexport = true;
- }
- }
-
- // Recursively calculate the required packages for the
- // current candidate.
- Map requiredMap =
- calculateExportedAndReexportedPackages(candCap, candidatesMap, cycleMap);
-
- // Merge the candidate's exported and required packages
- // into the complete set of required packages.
- for (Iterator reqIter = requiredMap.entrySet().iterator(); reqIter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) reqIter.next();
- String pkgName = (String) entry.getKey();
-
- // Merge the current set of required packages into
- // the overall complete set of required packages.
- // We calculate all the required packages, because
- // despite the fact that some packages will be required
- // "privately" and some will be required "reexport", any
- // re-exported packages will ultimately need to
- // be combined with privately required packages,
- // if the required packages overlap. This is one of the
- // bad things about require-bundle behavior, it does not
- // necessarily obey the visibility rules declared in the
- // dependency.
- ResolvedPackage rp = (ResolvedPackage) allRequiredMap.get(pkgName);
- if (rp != null)
- {
- // Create the union of all packages.
- ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
- rp.merge(rpReq);
- }
- else
- {
- // Add package to required map.
- allRequiredMap.put(pkgName, entry.getValue());
- }
-
- // Keep track of all required packages to be re-exported.
- // All re-exported packages will need to be merged into the
- // target module's package map and become part of its overall
- // export signature.
- if (reexport)
- {
- reexportedPkgMap.put(pkgName, pkgName);
- }
- }
- }
- }
-
- // For the target module we have now calculated its entire set
- // of required packages and their associated packages in
- // allRequiredMap and have calculated all packages to be re-exported
- // in reexportedPkgMap. Add all re-exported required packages to the
- // target module's package map since they will be part of its export
- // signature.
- for (Iterator iter = reexportedPkgMap.entrySet().iterator(); iter.hasNext(); )
- {
- String pkgName = (String) ((Map.Entry) iter.next()).getKey();
- pkgMap.put(pkgName, allRequiredMap.get(pkgName));
- }
-
- // Now loop through the target module's export package capabilities and add
- // the target module's export capability as a source for any exported packages.
- ICapability[] candCaps = capTarget.getModule().getCapabilities();
- for (int capIdx = 0; (candCaps != null) && (capIdx < candCaps.length); capIdx++)
- {
- if (candCaps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- String pkgName = (String)
- candCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
- rp = (rp == null) ? new ResolvedPackage(pkgName, null) : rp;
- rp.m_capList.add(candCaps[capIdx]);
- pkgMap.put(rp.m_name, rp);
- }
- }
-
- return pkgMap;
- }
-
- private static Map calculateExportedAndReexportedPackagesResolved(
- IModule targetModule, Map cycleMap)
- {
-//System.out.println("calculateExportedAndRequiredPackagesResolved("+targetModule+")");
- Map pkgMap = new HashMap();
-
- if (cycleMap.get(targetModule) != null)
- {
- return pkgMap;
- }
-
- cycleMap.put(targetModule, targetModule);
-
- // Loop through all wires for the target module's module dependencies
- // and calculate the module's complete set of required packages (and
- // their associated sources) and the complete set of required
- // packages to be re-exported.
- Map allRequiredMap = new HashMap();
- Map reexportedPkgMap = new HashMap();
- IWire[] wires = targetModule.getWires();
- for (int i = 0; (wires != null) && (i < wires.length); i++)
- {
- // If the wire is a module dependency, then flatten it to packages.
- if (wires[i].getCapability().getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- // Determine if required packages are re-exported.
- boolean reexport = false;
- R4Directive[] dirs = ((Requirement) wires[i].getRequirement()).getDirectives();
- for (int dirIdx = 0;
- !reexport && (dirs != null) && (dirIdx < dirs.length); dirIdx++)
- {
- if (dirs[dirIdx].getName().equals(Constants.VISIBILITY_DIRECTIVE)
- && dirs[dirIdx].getValue().equals(Constants.VISIBILITY_REEXPORT))
- {
- reexport = true;
- }
- }
-
- // Recursively calculate the required packages for the
- // wire's exporting module.
- Map requiredMap = calculateExportedAndReexportedPackagesResolved(
- wires[i].getExporter(), cycleMap);
-
- // Merge the wires exported and re-exported packages
- // into the complete set of required packages.
- for (Iterator reqIter = requiredMap.entrySet().iterator(); reqIter.hasNext(); )
- {
- Map.Entry entry = (Map.Entry) reqIter.next();
- String pkgName = (String) entry.getKey();
-
- // Merge the current set of required packages into
- // the overall complete set of required packages.
- // We calculate all the required packages, because
- // despite the fact that some packages will be required
- // "privately" and some will be required "reexport", any
- // re-exported packages will ultimately need to
- // be combined with privately required packages,
- // if the required packages overlap. This is one of the
- // bad things about require-bundle behavior, it does not
- // necessarily obey the visibility rules declared in the
- // dependency.
- ResolvedPackage rp = (ResolvedPackage) allRequiredMap.get(pkgName);
- if (rp != null)
- {
- // Create the union of all packages.
- ResolvedPackage rpReq = (ResolvedPackage) entry.getValue();
- rp.merge(rpReq);
- }
- else
- {
- // Add package to required map.
- allRequiredMap.put(pkgName, entry.getValue());
- }
-
- // Keep track of all required packages to be re-exported.
- // All re-exported packages will need to be merged into the
- // target module's package map and become part of its overall
- // export signature.
- if (reexport)
- {
- reexportedPkgMap.put(pkgName, pkgName);
- }
- }
- }
- }
-
- // For the target module we have now calculated its entire set
- // of required packages and their associated source capabilities in
- // allRequiredMap and have calculated all packages to be re-exported
- // in reexportedPkgMap. Add all re-exported required packages to the
- // target module's package map since they will be part of its export
- // signature.
- for (Iterator iter = reexportedPkgMap.entrySet().iterator(); iter.hasNext(); )
- {
- String pkgName = (String) ((Map.Entry) iter.next()).getKey();
- pkgMap.put(pkgName, allRequiredMap.get(pkgName));
- }
-
- // Now loop through the target module's export package capabilities and
- // add the target module as a source for any exported packages.
- ICapability[] caps = targetModule.getCapabilities();
- for (int i = 0; (caps != null) && (i < caps.length); i++)
- {
- if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
- {
- String pkgName = (String)
- caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
- ResolvedPackage rp = (ResolvedPackage) pkgMap.get(pkgName);
- rp = (rp == null) ? new ResolvedPackage(pkgName, null) : rp;
- rp.m_capList.add(caps[i]);
- pkgMap.put(rp.m_name, rp);
- }
- }
-
- return pkgMap;
- }
-
- private static Map calculateCandidateRequiredPackages(
- IModule module, ICapability capTarget, Map candidatesMap)
- {
-//System.out.println("calculateCandidateRequiredPackages("+module+")");
- Map cycleMap = new HashMap();
- cycleMap.put(module, module);
- return calculateExportedAndReexportedPackages(capTarget, candidatesMap, cycleMap);
- }
-
- private static void incrementCandidateConfiguration(List resolverList)
- throws ResolveException
- {
- for (int i = 0; i < resolverList.size(); i++)
- {
- List candSetList = (List) resolverList.get(i);
- for (int j = 0; j < candSetList.size(); j++)
- {
- CandidateSet cs = (CandidateSet) candSetList.get(j);
- // See if we can increment the candidate set, without overflowing
- // the candidate array bounds.
- if ((cs.m_idx + 1) < cs.m_candidates.size())
- {
- cs.m_idx++;
- return;
- }
- // If the index will overflow the candidate array bounds,
- // then set the index back to zero and try to increment
- // the next candidate.
- else
- {
- cs.m_idx = 0;
- }
- }
- }
- throw new ResolveException(
- "Unable to resolve due to constraint violation.", null, null);
- }
-
- private static Map populateWireMap(
- ResolverState state, Map candidatesMap, IModule importer, Map wireMap)
- {
- // If the module is already resolved or it is part of
- // a cycle, then just return the wire map.
- if (importer.isResolved() || (wireMap.get(importer) != null))
- {
- return wireMap;
- }
-
- // Get the candidate set list for the importer.
- List candSetList = (List) candidatesMap.get(importer);
-
- List moduleWires = new ArrayList();
- List packageWires = new ArrayList();
-
- // Put the module in the wireMap with an empty wire array;
- // we do this early so we can use it to detect cycles.
- wireMap.put(importer, m_emptyWires);
-
- // Loop through each candidate Set and create a wire
- // for the selected candidate for the associated import.
- for (int candSetIdx = 0; candSetIdx < candSetList.size(); candSetIdx++)
- {
- // Get the current candidate set.
- CandidateSet cs = (CandidateSet) candSetList.get(candSetIdx);
-
- // Create a module wire for module dependencies.
- if (cs.m_requirement.getNamespace().equals(ICapability.MODULE_NAMESPACE))
- {
- moduleWires.add(new R4WireModule(
- importer,
- cs.m_requirement,
- ((ICapability) cs.m_candidates.get(cs.m_idx)).getModule(),
- ((ICapability) cs.m_candidates.get(cs.m_idx)),
- calculateCandidateRequiredPackages(
- importer, (ICapability) cs.m_candidates.get(cs.m_idx), candidatesMap)));
- }
- // Create a package wire for package dependencies.
- // Filter out the case where a module imports from
- // itself, since the module should simply load from
- // its internal class path in this case.
- else if (importer != ((ICapability) cs.m_candidates.get(cs.m_idx)).getModule())
- {
- // Add wire for imported package.
- packageWires.add(new R4Wire(
- importer,
- cs.m_requirement,
- ((ICapability) cs.m_candidates.get(cs.m_idx)).getModule(),
- ((ICapability) cs.m_candidates.get(cs.m_idx))));
- }
-
- // Create any necessary wires for the selected candidate module.
- wireMap = populateWireMap(
- state, candidatesMap,
- ((ICapability) cs.m_candidates.get(cs.m_idx)).getModule(),
- wireMap);
- }
-
- packageWires.addAll(moduleWires);
- wireMap.put(importer, packageWires.toArray(new IWire[packageWires.size()]));
-
- return wireMap;
- }
-
- //
- // Utility methods.
- //
-
- private static void verifyNativeLibraries(IModule module)
- throws ResolveException
- {
- // Next, try to resolve any native code, since the module is
- // not resolvable if its native code cannot be loaded.
- R4Library[] libs = module.getNativeLibraries();
- if (libs != null)
- {
- String msg = null;
- // Verify that all native libraries exist in advance; this will
- // throw an exception if the native library does not exist.
- for (int libIdx = 0; (msg == null) && (libIdx < libs.length); libIdx++)
- {
- String entryName = libs[libIdx].getEntryName();
- if (entryName != null)
- {
- if (!module.getContent().hasEntry(entryName))
- {
- msg = "Native library does not exist: " + entryName;
- }
- }
- }
- // If we have a zero-length native library array, then
- // this means no native library class could be selected
- // so we should fail to resolve.
- if (libs.length == 0)
- {
- msg = "No matching native libraries found.";
- }
- if (msg != null)
- {
- throw new ResolveException(msg, module, null);
- }
- }
- }
-
- /**
- * Checks to see if the passed in module's required execution environment
- * is provided by the framework.
- * @param fwkExecEvnStr The original property value of the framework's
- * supported execution environments.
- * @param fwkExecEnvSet Parsed set of framework's supported execution environments.
- * @param module The module whose required execution environment is to be to verified.
- * @throws ResolveException if the module's required execution environment does
- * not match the framework's supported execution environment.
- **/
- private static void verifyExecutionEnvironment(
- String fwkExecEnvStr, Set fwkExecEnvSet, IModule module)
- throws ResolveException
- {
- String bundleExecEnvStr = (String)
- module.getHeaders().get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
- if (bundleExecEnvStr != null)
- {
- bundleExecEnvStr = bundleExecEnvStr.trim();
-
- // If the bundle has specified an execution environment and the
- // framework has an execution environment specified, then we must
- // check for a match.
- if (!bundleExecEnvStr.equals("")
- && (fwkExecEnvStr != null)
- && (fwkExecEnvStr.length() > 0))
- {
- StringTokenizer tokens = new StringTokenizer(bundleExecEnvStr, ",");
- boolean found = false;
- while (tokens.hasMoreTokens() && !found)
- {
- if (fwkExecEnvSet.contains(tokens.nextToken().trim()))
- {
- found = true;
- }
- }
- if (!found)
- {
- throw new ResolveException(
- "Execution environment not supported: "
- + bundleExecEnvStr, module, null);
- }
- }
- }
- }
-
- /**
- * Updates the framework wide execution environment string and a cached Set of
- * execution environment tokens from the comma delimited list specified by the
- * system variable 'org.osgi.framework.executionenvironment'.
- * @param frameworkEnvironment Comma delimited string of provided execution environments
- **/
- private static Set parseExecutionEnvironments(String fwkExecEnvStr)
- {
- Set newSet = new HashSet();
- if (fwkExecEnvStr != null)
- {
- StringTokenizer tokens = new StringTokenizer(fwkExecEnvStr, ",");
- while (tokens.hasMoreTokens())
- {
- newSet.add(tokens.nextToken().trim());
- }
- }
- return newSet;
- }
-
- //
- // Inner classes.
- //
-
- public static interface ResolverState
- {
- IModule[] getModules();
- List getResolvedCandidates(IRequirement req, IModule reqModule);
- List getUnresolvedCandidates(IRequirement req, IModule reqModule);
- }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/IModule.java b/framework/src/main/java/org/apache/felix/moduleloader/IModule.java
deleted file mode 100644
index 57a9018..0000000
--- a/framework/src/main/java/org/apache/felix/moduleloader/IModule.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.moduleloader;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.util.Enumeration;
-import java.util.Map;
-import org.apache.felix.framework.util.manifestparser.R4Library;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.Version;
-
-public interface IModule
-{
- final static int EAGER_ACTIVATION = 0;
- final static int LAZY_ACTIVATION = 1;
-
- void setSecurityContext(Object securityContext);
- Object getSecurityContext();
-
- // Metadata access methods.
- Map getHeaders();
- boolean isExtension();
- String getSymbolicName();
- Version getVersion();
- ICapability[] getCapabilities();
- IRequirement[] getRequirements();
- IRequirement[] getDynamicRequirements();
- R4Library[] getNativeLibraries();
- int getDeclaredActivationPolicy();
-
- // Run-time data access methods.
- Bundle getBundle();
- String getId();
- IWire[] getWires();
- boolean isResolved();
-
- // Content access methods.
- IContent getContent();
- Class getClassByDelegation(String name) throws ClassNotFoundException;
- URL getResourceByDelegation(String name);
- Enumeration getResourcesByDelegation(String name);
- URL getEntry(String name);
-
- // TODO: ML - For expediency, the index argument was added to these methods
- // but it is not clear that this makes sense in the long run. This needs to
- // be readdressed in the future, perhaps by the spec to clearly indicate
- // how resources on the bundle class path are searched, which is why we
- // need the index number in the first place -- to differentiate among
- // resources with the same name on the bundle class path. This was previously
- // handled as part of the resource path, but that approach is not spec
- // compliant.
- boolean hasInputStream(int index, String urlPath)
- throws IOException;
- InputStream getInputStream(int index, String urlPath)
- throws IOException;
-}
\ No newline at end of file