Avoid creating a more complicated data structure if no fragments
are present in the resolve. (FELIX-2909)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1090415 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java b/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
index e4b3384..b99d541 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Candidates.java
@@ -59,6 +59,9 @@
// Map used when populating candidates to hold intermediate and final results.
private final Map<Module, Object> m_populateResultCache;
+ // Flag to signal if fragments are present in the candidate map.
+ private boolean m_fragmentsPresent = false;
+
/**
* Private copy constructor used by the copy() method.
* @param root the root module for the resolve.
@@ -73,7 +76,8 @@
Map<Capability, Set<Requirement>> dependentMap,
Map<Requirement, SortedSet<Capability>> candidateMap,
Map<Capability, Map<String, Map<Version, List<Requirement>>>> hostFragments,
- Map<Module, HostModule> wrappedHosts, Map<Module, Object> populateResultCache)
+ Map<Module, HostModule> wrappedHosts, Map<Module, Object> populateResultCache,
+ boolean fragmentsPresent)
{
m_root = root;
m_candidateModules = candidateModules;
@@ -82,6 +86,7 @@
m_hostFragments = hostFragments;
m_allWrappedHosts = wrappedHosts;
m_populateResultCache = populateResultCache;
+ m_fragmentsPresent = fragmentsPresent;
}
/**
@@ -305,6 +310,100 @@
}
}
+ public final void populateOptional(ResolverState state, Module module)
+ {
+ try
+ {
+ // If the optional module is a fragment, then we only want to populate
+ // the fragment if it has a candidate host in the set of already populated
+ // modules. We do this to avoid unnecessary work in prepare(). If the
+ // fragment has a host, we'll prepopulate the result cache here to avoid
+ // having to do the host lookup again in populate().
+ boolean isFragment = Util.isFragment(module);
+ if (isFragment)
+ {
+ // Get the current result cache value, to make sure the module
+ // hasn't already been populated.
+ Object cacheValue = m_populateResultCache.get(module);
+ if (cacheValue == null)
+ {
+ // Create a modifiable list of the module's requirements.
+ List<Requirement> remainingReqs = new ArrayList(module.getRequirements());
+
+ // Find the host requirement.
+ Requirement hostReq = null;
+ for (Iterator<Requirement> it = remainingReqs.iterator(); it.hasNext(); )
+ {
+ Requirement r = it.next();
+ if (r.getNamespace().equals(Capability.HOST_NAMESPACE))
+ {
+ hostReq = r;
+ it.remove();
+ }
+ }
+
+ // Get candidates hosts and keep any that have been populated.
+ SortedSet<Capability> hosts = state.getCandidates(hostReq, false);
+ for (Iterator<Capability> it = hosts.iterator(); it.hasNext(); )
+ {
+ Capability host = it.next();
+ if (!isPopulated(host.getModule()))
+ {
+ it.remove();
+ }
+ }
+
+ // If there aren't any populated hosts, then we can just
+ // return since this fragment isn't needed.
+ if (hosts.isEmpty())
+ {
+ return;
+ }
+
+ // If there are populates host candidates, then finish up
+ // some other checks and prepopulate the result cache with
+ // the work we've done so far.
+
+ // Verify that any required execution environment is satisfied.
+ state.checkExecutionEnvironment(module);
+
+ // Verify that any native libraries match the current platform.
+ state.checkNativeLibraries(module);
+
+ // Record cycle count, but start at -1 since it will
+ // be incremented again in populate().
+ Integer cycleCount = new Integer(-1);
+
+ // Create a local map for populating candidates first, just in case
+ // the module is not resolvable.
+ Map<Requirement, SortedSet<Capability>> localCandidateMap = new HashMap();
+
+ // Add the discovered host candidates to the local candidate map.
+ localCandidateMap.put(hostReq, hosts);
+
+ // Add these value to the result cache so we know we are
+ // in the middle of populating candidates for the current
+ // module.
+ m_populateResultCache.put(module,
+ new Object[] { cycleCount, localCandidateMap, remainingReqs });
+ }
+ }
+
+ // Try to populate candidates for the optional module.
+ populate(state, module);
+ }
+ catch (ResolveException ex)
+ {
+ // Ignore since the module is optional.
+ }
+ }
+
+ private boolean isPopulated(Module module)
+ {
+ Object value = m_populateResultCache.get(module);
+ return ((value != null) && (value instanceof Boolean));
+ }
+
private void populateDynamic(ResolverState state, Module module)
{
// There should be one entry in the candidate map, which are the
@@ -356,7 +455,10 @@
**/
private void add(Requirement req, SortedSet<Capability> candidates)
{
- boolean isFragment = req.getNamespace().equals(Capability.HOST_NAMESPACE);
+ if (req.getNamespace().equals(Capability.HOST_NAMESPACE))
+ {
+ m_fragmentsPresent = true;
+ }
// Record the candidates.
m_candidateMap.put(req, candidates);
@@ -369,42 +471,6 @@
// Remember the module for all capabilities so we can
// determine which ones are singletons.
m_candidateModules.add(cap.getModule());
-
- // Record the requirement as dependent on the capability.
- Set<Requirement> dependents = m_dependentMap.get(cap);
- if (dependents == null)
- {
- dependents = new HashSet<Requirement>();
- m_dependentMap.put(cap, dependents);
- }
- dependents.add(req);
-
- // Keep track of hosts and associated fragments.
- if (isFragment)
- {
- Map<String, Map<Version, List<Requirement>>>
- fragments = m_hostFragments.get(cap);
- if (fragments == null)
- {
- fragments = new HashMap<String, Map<Version, List<Requirement>>>();
- m_hostFragments.put(cap, fragments);
- }
- Map<Version, List<Requirement>> fragmentVersions =
- fragments.get(req.getModule().getSymbolicName());
- if (fragmentVersions == null)
- {
- fragmentVersions =
- new TreeMap<Version, List<Requirement>>(Collections.reverseOrder());
- fragments.put(req.getModule().getSymbolicName(), fragmentVersions);
- }
- List<Requirement> actual = fragmentVersions.get(req.getModule().getVersion());
- if (actual == null)
- {
- actual = new ArrayList<Requirement>();
- fragmentVersions.put(req.getModule().getVersion(), actual);
- }
- actual.add(req);
- }
}
}
@@ -463,6 +529,14 @@
**/
public void prepare(List<Module> existingSingletons)
{
+ boolean init = false;
+
+ if (m_fragmentsPresent)
+ {
+ populateDependents();
+ init = true;
+ }
+
final Map<String, Module> singletons = new HashMap<String, Module>();
for (Iterator<Module> it = m_candidateModules.iterator(); it.hasNext(); )
@@ -470,6 +544,12 @@
Module m = it.next();
if (isSingleton(m))
{
+ if (!init)
+ {
+ populateDependents();
+ init = true;
+ }
+
// See if there is an existing singleton for the
// module's symbolic name.
Module singleton = singletons.get(m.getSymbolicName());
@@ -636,6 +716,53 @@
}
}
+ private void populateDependents()
+ {
+ for (Entry<Requirement, SortedSet<Capability>> entry : m_candidateMap.entrySet())
+ {
+ Requirement req = entry.getKey();
+ SortedSet<Capability> caps = entry.getValue();
+ for (Capability cap : caps)
+ {
+ // Record the requirement as dependent on the capability.
+ Set<Requirement> dependents = m_dependentMap.get(cap);
+ if (dependents == null)
+ {
+ dependents = new HashSet<Requirement>();
+ m_dependentMap.put(cap, dependents);
+ }
+ dependents.add(req);
+
+ // Keep track of hosts and associated fragments.
+ if (req.getNamespace().equals(Capability.HOST_NAMESPACE))
+ {
+ Map<String, Map<Version, List<Requirement>>>
+ fragments = m_hostFragments.get(cap);
+ if (fragments == null)
+ {
+ fragments = new HashMap<String, Map<Version, List<Requirement>>>();
+ m_hostFragments.put(cap, fragments);
+ }
+ Map<Version, List<Requirement>> fragmentVersions =
+ fragments.get(req.getModule().getSymbolicName());
+ if (fragmentVersions == null)
+ {
+ fragmentVersions =
+ new TreeMap<Version, List<Requirement>>(Collections.reverseOrder());
+ fragments.put(req.getModule().getSymbolicName(), fragmentVersions);
+ }
+ List<Requirement> actual = fragmentVersions.get(req.getModule().getVersion());
+ if (actual == null)
+ {
+ actual = new ArrayList<Requirement>();
+ fragmentVersions.put(req.getModule().getVersion(), actual);
+ }
+ actual.add(req);
+ }
+ }
+ }
+ }
+
/**
* Removes a module from the internal data structures if it wasn't selected
* as a fragment or a singleton. This process may cause other modules to
@@ -785,8 +912,6 @@
**/
public Candidates copy()
{
- Set<Module> candidateModules = new HashSet<Module>(m_candidateModules);
-
Map<Capability, Set<Requirement>> dependentMap =
new HashMap<Capability, Set<Requirement>>();
for (Entry<Capability, Set<Requirement>> entry : m_dependentMap.entrySet())
@@ -804,8 +929,9 @@
}
return new Candidates(
- m_root, candidateModules, dependentMap, candidateMap,
- m_hostFragments, m_allWrappedHosts, m_populateResultCache);
+ m_root, m_candidateModules, dependentMap, candidateMap,
+ m_hostFragments, m_allWrappedHosts, m_populateResultCache,
+ m_fragmentsPresent);
}
public void dump()
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
index 66a2b91..04dca7d 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/ResolverImpl.java
@@ -75,14 +75,7 @@
// Try to populate optional fragments.
for (Module fragment : fragments)
{
- try
- {
- allCandidates.populate(state, fragment);
- }
- catch (ResolveException ex)
- {
- // Ignore, since fragments are optional.
- }
+ allCandidates.populateOptional(state, fragment);
}
// Merge any fragments into hosts.
@@ -213,14 +206,7 @@
// Try to populate optional fragments.
for (Module fragment : fragments)
{
- try
- {
- allCandidates.populate(state, fragment);
- }
- catch (ResolveException ex)
- {
- // Ignore, since fragments are optional.
- }
+ allCandidates.populateOptional(state, fragment);
}
// Merge any fragments into hosts.