Implement most of the functionality for resolver hooks (FELIX-2986) along
with some other changes for R4.3 API (FELIX-2950).


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1136150 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index 964afbc..471ec65 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -162,17 +162,22 @@
         }
         else
         {
-            // Dispose of the current revisions.
+            // Get current revision, since we can reuse it.
+            BundleRevisionImpl current = (BundleRevisionImpl) adapt(BundleRevision.class);
+            // Close all existing revisions.
             closeRevisions();
+            // Clear all revisions.
+            m_revisions.clear();
 
-            // Now we will purge all old revisions, only keeping the newest one.
+            // Purge all old archive revisions, only keeping the newest one.
             m_archive.purge();
 
-            // Lastly, we want to reset our bundle be reinitializing our state
-            // and recreating a revision object for the newest revision.
-            m_revisions.clear();
-            final BundleRevision br = createRevision();
-            addRevision(br);
+            // Reset the content of the current bundle revision.
+            current.resetContent(m_archive.getCurrentRevision().getContent());
+            // Re-add the revision to the bundle.
+            addRevision(current);
+
+            // Reset the bundle state.
             m_state = Bundle.INSTALLED;
             m_stale = false;
             m_cachedHeaders.clear();
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleRevisionDependencies.java b/framework/src/main/java/org/apache/felix/framework/BundleRevisionDependencies.java
index ee50cb4..9b4f83a 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleRevisionDependencies.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleRevisionDependencies.java
@@ -157,7 +157,7 @@
 
         // Get exported package name.
         String pkgName = (String)
-            exportCap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+            exportCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
 
         // Get all importers and requirers for all revisions of the bundle.
         // The spec says that require-bundle should be returned with importers.
@@ -171,7 +171,7 @@
                 {
                     BundleCapability cap = entry.getKey();
                     if ((cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
-                        && cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)
+                        && cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
                             .equals(pkgName))
                         || cap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
                     {
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
index 0341468..2db1452 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleRevisionImpl.java
@@ -51,7 +51,6 @@
     private final Logger m_logger;
     private final Map m_configMap;
     private final String m_id;
-    private final Content m_content;
     private final Map m_headerMap;
     private final URLStreamHandler m_streamHandler;
 
@@ -69,6 +68,7 @@
 
     private final Bundle m_bundle;
 
+    private Content m_content;
     private List<Content> m_contentPath;
     private ProtectionDomain m_protectionDomain = null;
     private final static SecureAction m_secureAction = new SecureAction();
@@ -362,11 +362,16 @@
     // Content access methods.
     //
 
-    public Content getContent()
+    public synchronized Content getContent()
     {
         return m_content;
     }
 
+    synchronized void resetContent(Content content)
+    {
+        m_content = content;
+    }
+
     synchronized List<Content> getContentPath()
     {
         if (m_contentPath == null)
@@ -607,7 +612,7 @@
         }
         if (index == 0)
         {
-            return m_content.hasEntry(urlPath);
+            return getContent().hasEntry(urlPath);
         }
         return getContentPath().get(index - 1).hasEntry(urlPath);
     }
@@ -621,7 +626,7 @@
         }
         if (index == 0)
         {
-            return m_content.getEntryAsStream(urlPath);
+            return getContent().getEntryAsStream(urlPath);
         }
         return getContentPath().get(index - 1).getEntryAsStream(urlPath);
     }
@@ -634,7 +639,7 @@
         }
         if (index == 0)
         {
-            return m_content.getEntryAsURL(urlPath);
+            return getContent().getEntryAsURL(urlPath);
         }
         return getContentPath().get(index - 1).getEntryAsURL(urlPath);
     }
@@ -676,6 +681,7 @@
             m_logger.log(Logger.LOG_ERROR, "Error releasing revision: " + ex.getMessage(), ex);
         }
         m_content.close();
+        m_content = null;
         for (int i = 0; (m_contentPath != null) && (i < m_contentPath.size()); i++)
         {
             m_contentPath.get(i).close();
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
index a1a45cd..bd12416 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -403,7 +403,7 @@
     {
         m_wires.add(wire);
         m_importedPkgs.put(
-            (String) wire.getCapability().getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR),
+            (String) wire.getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
             wire.getProviderWiring().getRevision());
     }
 
@@ -537,7 +537,7 @@
         {
             m_resolver.resolve(m_revision);
         }
-        catch (ResolveException ex)
+        catch (Exception ex)
         {
             // The spec states that if the bundle cannot be resolved, then
             // only the local bundle's resources should be searched. So we
@@ -639,6 +639,10 @@
             {
                 // Ignore this since it is likely normal.
             }
+            catch (BundleException ex)
+            {
+                // Ignore this since it is likely the result of a resolver hook.
+            }
             if (provider != null)
             {
                 // Delegate to the provider revision.
@@ -886,20 +890,11 @@
                     }
                 }
             }
-            catch (ResolveException ex)
+// TODO: OSGi R4.3 - If we eliminate resolving from this method, then we can
+//       simplify this catch, since resolve throws resolve and bundle exceptions.
+            catch (Exception ex)
             {
-                if (isClass)
-                {
-                    // We do not use the resolve exception as the
-                    // cause of the exception, since this would
-                    // potentially leak internal module information.
-                    throw new ClassNotFoundException(
-                        name + " not found because "
-                        + getBundle()
-                        + " cannot resolve: "
-                        + ex.getRequirement());
-                }
-                else
+                if (!isClass && (ex instanceof ResolveException))
                 {
                     // The spec states that if the bundle cannot be resolved, then
                     // only the local bundle's resources should be searched. So we
@@ -909,13 +904,38 @@
                     {
                         return url;
                     }
+                }
 
-                    // We need to throw a resource not found exception.
-                    throw new ResourceNotFoundException(
-                        name + " not found because "
-                        + getBundle()
-                        + " cannot resolve: "
-                        + ex.getRequirement());
+                if (isClass)
+                {
+                    if (!(ex instanceof ClassNotFoundException))
+                    {
+                        ClassNotFoundException cnfe = new ClassNotFoundException(
+                            name
+                            + " not found in "
+                            + getBundle()
+                            + " : "
+                            + ex.getMessage());
+ex.printStackTrace();
+                        cnfe.initCause(ex);
+                        throw cnfe;
+                    }
+                    throw (ClassNotFoundException) ex;
+                }
+                else
+                {
+                    if (!(ex instanceof ResourceNotFoundException))
+                    {
+                        ResourceNotFoundException rnfe = new ResourceNotFoundException(
+                            name
+                            + " not found in "
+                            + getBundle()
+                            + " : "
+                            + ex.getMessage());
+                        rnfe.initCause(ex);
+                        throw rnfe;
+                    }
+                    throw (ResourceNotFoundException) ex;
                 }
             }
             finally
@@ -965,10 +985,14 @@
                 return result;
             }
 
-            // If no class was found, then we must throw an exception
+            // If no class or resource was found, then we must throw an exception
             // since the provider of this package did not contain the
             // requested class and imported packages are atomic.
-            throw new ClassNotFoundException(name);
+            if (isClass)
+            {
+                throw new ClassNotFoundException(name);
+            }
+            throw new ResourceNotFoundException(name);
         }
 
         // Check if the package is required.
@@ -1017,6 +1041,10 @@
         {
             // Ignore this since it is likely normal.
         }
+        catch (BundleException ex)
+        {
+            // Ignore this since it is likely the result of a resolver hook.
+        }
 
         // If the dynamic import was successful, then this initial
         // time we must directly return the result from dynamically
@@ -1858,7 +1886,7 @@
         for (int i = 0; (wires != null) && (i < wires.size()); i++)
         {
             if (wires.get(i).getCapability().getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE) &&
-                wires.get(i).getCapability().getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
+                wires.get(i).getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
             {
                 String exporter = wires.get(i).getProviderWiring().getBundle().toString();
 
@@ -1949,7 +1977,7 @@
             // Try to see if there is an exporter available.
             Map<String, String> dirs = Collections.EMPTY_MAP;
             Map<String, Object> attrs = new HashMap<String, Object>(1);
-            attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+            attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
             BundleRequirementImpl req = new BundleRequirementImpl(
                 revision, BundleRevision.PACKAGE_NAMESPACE, dirs, attrs);
             Set<BundleCapability> exporters = resolver.getCandidates(req, false);
@@ -1988,7 +2016,7 @@
         // Next, check to see if there are any exporters for the package at all.
         Map<String, String> dirs = Collections.EMPTY_MAP;
         Map<String, Object> attrs = new HashMap<String, Object>(1);
-        attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+        attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
         BundleRequirementImpl req = new BundleRequirementImpl(
             revision, BundleRevision.PACKAGE_NAMESPACE, dirs, attrs);
         Set<BundleCapability> exports = resolver.getCandidates(req, false);
diff --git a/framework/src/main/java/org/apache/felix/framework/ExportedPackageImpl.java b/framework/src/main/java/org/apache/felix/framework/ExportedPackageImpl.java
index 01fab7a..be2b1db 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExportedPackageImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExportedPackageImpl.java
@@ -1,4 +1,4 @@
-/* 
+/*
  * 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
@@ -42,7 +42,7 @@
         m_exportingBundle = exporter;
         m_exportingRevision = revision;
         m_export = export;
-        m_pkgName = (String) m_export.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+        m_pkgName = (String) m_export.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
         m_version = (!m_export.getAttributes().containsKey(BundleCapabilityImpl.VERSION_ATTR))
             ? Version.emptyVersion
             : (Version) m_export.getAttributes().get(BundleCapabilityImpl.VERSION_ATTR);
diff --git a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
index 4b6a368..16e6f46 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -447,7 +447,7 @@
 
                 // Append exported package information.
                 exportSB.append(m_capabilities.get(i)
-                    .getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+                    .getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
                 for (Entry<String, String> entry
                     : m_capabilities.get(i).getDirectives().entrySet())
                 {
@@ -460,7 +460,7 @@
                 for (Entry<String, Object> entry
                     : m_capabilities.get(i).getAttributes().entrySet())
                 {
-                    if (!entry.getKey().equals(BundleCapabilityImpl.PACKAGE_ATTR)
+                    if (!entry.getKey().equals(BundleRevision.PACKAGE_NAMESPACE)
                         && !entry.getKey().equals(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE)
                         && !entry.getKey().equals(Constants.BUNDLE_VERSION_ATTRIBUTE))
                     {
@@ -474,7 +474,7 @@
 
                 // Remember exported packages.
                 exportNames.add(m_capabilities.get(i)
-                    .getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+                    .getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
             }
         }
 
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index 35269bc..7dfb4ae 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -1531,11 +1531,23 @@
         {
             return null;
         }
-// TODO: OSGi R4.3 - Currently, we try to resolve resource requests in
+// TODO: OSGi R4.3 - Previously, we try to resolve resource requests in
 //       findClassOrResourceByDelegation() and fall back to local resource
-//       searching if it fails. Perhaps we should attempt the resolve here
-//       and do the local searching here. This means we could get rid of
-//       resolve attempts in findClassOrResourceByDelegation().
+//       searching if it fails. Now we must attempt the resolve here since
+//       we cannot search by delegation until we are resolved and do the local
+//       searching here if we fail. This means we could get rid of resolve
+//       attempts in findClassOrResourceByDelegation().
+        try
+        {
+            resolveBundleRevision(bundle.adapt(BundleRevision.class));
+        }
+        catch (Exception ex)
+        {
+            // Ignore.
+        }
+
+        // If the bundle revision isn't resolved, then just search
+        // locally, otherwise delegate.
         if (bundle.adapt(BundleRevision.class).getWiring() == null)
         {
             return ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
@@ -1561,11 +1573,21 @@
         {
             return null;
         }
-// TODO: OSGi R4.3 - Currently, we try to resolve resource requests in
-//       findResourcesByDelegation() and fall back to local resource
-//       searching if it fails. Perhaps we should attempt the resolve here
-//       and do the local searching here. This means we could get rid of
-//       resolve attempts in findResourcesByDelegation().
+// TODO: OSGi R4.3 - Previously, we try to resolve resource requests in
+//       findClassOrResourceByDelegation() and fall back to local resource
+//       searching if it fails. Now we must attempt the resolve here since
+//       we cannot search by delegation until we are resolved and do the local
+//       searching here if we fail. This means we could get rid of resolve
+//       attempts in findClassOrResourceByDelegation().
+        try
+        {
+            resolveBundleRevision(bundle.adapt(BundleRevision.class));
+        }
+        catch (Exception ex)
+        {
+            // Ignore.
+        }
+
         if (bundle.adapt(BundleRevision.class).getWiring() == null)
         {
             return ((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
@@ -1726,7 +1748,7 @@
         {
             try
             {
-                resolveBundle(bundle);
+                resolveBundleRevision(bundle.adapt(BundleRevision.class));
             }
             catch (BundleException ex)
             {
@@ -1881,7 +1903,7 @@
                 case Bundle.ACTIVE:
                     return;
                 case Bundle.INSTALLED:
-                    resolveBundle(bundle);
+                    resolveBundleRevision(bundle.adapt(BundleRevision.class));
                     // No break.
                 case Bundle.RESOLVED:
                     // Set the bundle's context.
@@ -2037,7 +2059,9 @@
                 }
 
                 // Rethrow all other exceptions as a BundleException.
-                throw new BundleException("Activator start error in bundle " + bundle + ".", th);
+                throw new BundleException(
+                    "Activator start error in bundle " + bundle + ".",
+                    BundleException.ACTIVATOR_ERROR, th);
             }
         }
         finally
@@ -3427,7 +3451,7 @@
     {
         // First, get all exporters of the package.
         Map<String, Object> attrs = new HashMap<String, Object>(1);
-        attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+        attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
         BundleRequirementImpl req = new BundleRequirementImpl(
             null,
             BundleRevision.PACKAGE_NAMESPACE,
@@ -3580,9 +3604,9 @@
 //       BundleWiring.getCapabilities() returns the proper result. We probably
 //       Won't even need this method.
                         String pkgName = (String)
-                            cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+                            cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
                         Map<String, Object> attrs = new HashMap<String, Object>(1);
-                        attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+                        attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
                         BundleRequirementImpl req =
                             new BundleRequirementImpl(
                             null,
@@ -3637,22 +3661,15 @@
 
         try
         {
+            // Remember original targets.
+            Collection<Bundle> originalTargets = targets;
+
             // Determine set of bundles to be resolved, which is either the
             // specified bundles or all bundles if null.
             if (targets == null)
             {
-                targets = new ArrayList<Bundle>();
-
-                // Add all unresolved bundles to the list.
-                Iterator iter = m_installedBundles[LOCATION_MAP_IDX].values().iterator();
-                while (iter.hasNext())
-                {
-                    BundleImpl bundle = (BundleImpl) iter.next();
-                    if (bundle.getState() == Bundle.INSTALLED)
-                    {
-                        targets.add(bundle);
-                    }
-                }
+                // Add all bundles to the list.
+                targets = m_installedBundles[LOCATION_MAP_IDX].values();
             }
 
             // Now resolve each target bundle.
@@ -3661,17 +3678,45 @@
             // If there are targets, then resolve each one.
             if (!targets.isEmpty())
             {
+                // Get bundle revisions for bundles in INSTALLED state.
+                Set<BundleRevision> revisions =
+                    new HashSet<BundleRevision>(targets.size());
                 for (Bundle b : targets)
                 {
-                    try
+                    if (b.getState() != Bundle.UNINSTALLED)
                     {
-                        resolveBundle((BundleImpl) b);
+                        revisions.add(b.adapt(BundleRevision.class));
                     }
-                    catch (BundleException ex)
+                }
+                // If we had to filter any of the original targets, then
+                // the return result will be false regardless.
+                if ((originalTargets != null) && (originalTargets.size() != revisions.size()))
+                {
+                    result = false;
+                }
+                try
+                {
+                    m_resolver.resolve(revisions);
+                    if (result)
                     {
-                        result = false;
+                        for (BundleRevision br : revisions)
+                        {
+                            if (br.getWiring() == null)
+                            {
+                                result = false;
+                                break;
+                            }
+                        }
                     }
                 }
+                catch (ResolveException ex)
+                {
+                    result = false;
+                }
+                catch (BundleException ex)
+                {
+                    result = false;
+                }
             }
 
             return result;
@@ -3683,11 +3728,11 @@
         }
     }
 
-    private void resolveBundle(Bundle bundle) throws BundleException
+    private void resolveBundleRevision(BundleRevision revision) throws BundleException
     {
         try
         {
-            m_resolver.resolve(bundle.adapt(BundleRevision.class));
+            m_resolver.resolve(revision);
         }
         catch (ResolveException ex)
         {
@@ -3696,11 +3741,11 @@
                 Bundle b = ex.getRevision().getBundle();
                 throw new BundleException(
                     "Unresolved constraint in bundle "
-                    + b + ": " + ex.getMessage());
+                    + b + ": " + ex.getMessage(), BundleException.RESOLVE_ERROR);
             }
             else
             {
-                throw new BundleException(ex.getMessage());
+                throw new BundleException(ex.getMessage(), BundleException.RESOLVE_ERROR);
             }
         }
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java b/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java
deleted file mode 100644
index fc4ce43..0000000
--- a/framework/src/main/java/org/apache/felix/framework/ResolverStateImpl.java
+++ /dev/null
@@ -1,344 +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;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.StringTokenizer;
-import java.util.TreeSet;
-import org.apache.felix.framework.capabilityset.CapabilitySet;
-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.manifestparser.R4Library;
-import org.apache.felix.framework.wiring.BundleCapabilityImpl;
-import org.apache.felix.framework.wiring.BundleRequirementImpl;
-import org.osgi.framework.BundlePermission;
-import org.osgi.framework.Constants;
-import org.osgi.framework.PackagePermission;
-import org.osgi.framework.wiring.BundleCapability;
-import org.osgi.framework.wiring.BundleRevision;
-import org.osgi.framework.wiring.BundleWire;
-
-class ResolverStateImpl implements Resolver.ResolverState
-{
-    private final Logger m_logger;
-    // Set of all revisions.
-    private final Set<BundleRevision> m_revisions;
-    // Set of all fragments.
-    private final Set<BundleRevision> m_fragments;
-    // Capability sets.
-    private final Map<String, CapabilitySet> m_capSets;
-    // Execution environment.
-    private final String m_fwkExecEnvStr;
-    // Parsed framework environments
-    private final Set<String> m_fwkExecEnvSet;
-
-//    void dump()
-//    {
-//        for (Entry<String, CapabilitySet> entry : m_capSets.entrySet())
-//        {
-//            System.out.println("+++ START CAPSET " + entry.getKey());
-//            entry.getValue().dump();
-//            System.out.println("+++ END CAPSET " + entry.getKey());
-//        }
-//    }
-
-    ResolverStateImpl(Logger logger, String fwkExecEnvStr)
-    {
-        m_logger = logger;
-        m_revisions = new HashSet<BundleRevision>();
-        m_fragments = new HashSet<BundleRevision>();
-        m_capSets = new HashMap<String, CapabilitySet>();
-
-        m_fwkExecEnvStr = (fwkExecEnvStr != null) ? fwkExecEnvStr.trim() : null;
-        m_fwkExecEnvSet = parseExecutionEnvironments(fwkExecEnvStr);
-
-        List<String> indices = new ArrayList<String>();
-        indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
-        m_capSets.put(BundleRevision.BUNDLE_NAMESPACE, new CapabilitySet(indices, true));
-
-        indices = new ArrayList<String>();
-        indices.add(BundleCapabilityImpl.PACKAGE_ATTR);
-        m_capSets.put(BundleRevision.PACKAGE_NAMESPACE, new CapabilitySet(indices, true));
-
-        indices = new ArrayList<String>();
-        indices.add(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
-        m_capSets.put(BundleRevision.HOST_NAMESPACE,  new CapabilitySet(indices, true));
-    }
-
-    synchronized void addRevision(BundleRevision br)
-    {
-        m_revisions.add(br);
-        List<BundleCapability> caps = (br.getWiring() == null)
-            ? br.getDeclaredCapabilities(null)
-            : br.getWiring().getCapabilities(null);
-        if (caps != null)
-        {
-            for (BundleCapability cap : caps)
-            {
-                CapabilitySet capSet = m_capSets.get(cap.getNamespace());
-                if (capSet == null)
-                {
-                    capSet = new CapabilitySet(null, true);
-                    m_capSets.put(cap.getNamespace(), capSet);
-                }
-                capSet.addCapability(cap);
-            }
-        }
-
-        if (Util.isFragment(br))
-        {
-            m_fragments.add(br);
-        }
-    }
-
-    synchronized void removeRevision(BundleRevision br)
-    {
-        m_revisions.remove(br);
-        List<BundleCapability> caps = (br.getWiring() == null)
-            ? br.getDeclaredCapabilities(null)
-            : br.getWiring().getCapabilities(null);
-        if (caps != null)
-        {
-            for (BundleCapability cap : caps)
-            {
-                CapabilitySet capSet = m_capSets.get(cap.getNamespace());
-                if (capSet != null)
-                {
-                    capSet.removeCapability(cap);
-                }
-            }
-        }
-
-        if (Util.isFragment(br))
-        {
-            m_fragments.remove(br);
-        }
-    }
-
-    synchronized Set<BundleRevision> getFragments()
-    {
-        return new HashSet(m_fragments);
-    }
-
-// TODO: OSGi R4.3 - This will need to be changed once BundleWiring.getCapabilities()
-//       is correctly implemented, since it already has to remove substituted caps.
-    synchronized void removeSubstitutedCapabilities(BundleRevision br)
-    {
-        if (br.getWiring() != null)
-        {
-            // Loop through the revision's package wires and determine if any
-            // of them overlap any of the packages exported by the revision.
-            // If so, then the framework must have chosen to have the revision
-            // import rather than export the package, so we need to remove the
-            // corresponding package capability from the package capability set.
-            for (BundleWire w : br.getWiring().getRequiredWires(null))
-            {
-                if (w.getCapability().getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
-                {
-                    for (BundleCapability cap : br.getWiring().getCapabilities(null))
-                    {
-                        if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
-                            && w.getCapability().getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)
-                                .equals(cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)))
-                        {
-                            m_capSets.get(BundleRevision.PACKAGE_NAMESPACE).removeCapability(cap);
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    //
-    // ResolverState methods.
-    //
-
-    public synchronized SortedSet<BundleCapability> getCandidates(
-        BundleRequirementImpl req, boolean obeyMandatory)
-    {
-        BundleRevisionImpl reqRevision = (BundleRevisionImpl) req.getRevision();
-        SortedSet<BundleCapability> result =
-            new TreeSet<BundleCapability>(new CandidateComparator());
-
-        CapabilitySet capSet = m_capSets.get(req.getNamespace());
-        if (capSet != null)
-        {
-            Set<BundleCapability> matches = capSet.match(req.getFilter(), obeyMandatory);
-            for (BundleCapability cap : matches)
-            {
-                if (System.getSecurityManager() != null)
-                {
-                    if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE) && (
-                        !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
-                            new PackagePermission((String) cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR),
-                            PackagePermission.EXPORTONLY)) ||
-                            !((reqRevision == null) ||
-                                ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
-                                    new PackagePermission((String) cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR),
-                                    cap.getRevision().getBundle(),PackagePermission.IMPORT))
-                            )))
-                    {
-                        if (reqRevision != cap.getRevision())
-                        {
-                            continue;
-                        }
-                    }
-                    else if (req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE) && (
-                        !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
-                            new BundlePermission(cap.getRevision().getSymbolicName(), BundlePermission.PROVIDE)) ||
-                            !((reqRevision == null) ||
-                                ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
-                                    new BundlePermission(reqRevision.getSymbolicName(), BundlePermission.REQUIRE))
-                            )))
-                    {
-                        continue;
-                    }
-                    else if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE) &&
-                        (!((BundleProtectionDomain) reqRevision.getProtectionDomain())
-                            .impliesDirect(new BundlePermission(
-                                reqRevision.getSymbolicName(),
-                                BundlePermission.FRAGMENT))
-                        || !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain())
-                            .impliesDirect(new BundlePermission(
-                                cap.getRevision().getSymbolicName(),
-                                BundlePermission.HOST))))
-                    {
-                        continue;
-                    }
-                }
-
-                if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE)
-                    && (cap.getRevision().getWiring() != null))
-                {
-                    continue;
-                }
-
-                result.add(cap);
-            }
-        }
-
-        return result;
-    }
-
-    public void checkExecutionEnvironment(BundleRevision revision) throws ResolveException
-    {
-        String bundleExecEnvStr = (String)
-            ((BundleRevisionImpl) revision).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("")
-                && (m_fwkExecEnvStr != null)
-                && (m_fwkExecEnvStr.length() > 0))
-            {
-                StringTokenizer tokens = new StringTokenizer(bundleExecEnvStr, ",");
-                boolean found = false;
-                while (tokens.hasMoreTokens() && !found)
-                {
-                    if (m_fwkExecEnvSet.contains(tokens.nextToken().trim()))
-                    {
-                        found = true;
-                    }
-                }
-                if (!found)
-                {
-                    throw new ResolveException(
-                        "Execution environment not supported: "
-                        + bundleExecEnvStr, revision, null);
-                }
-            }
-        }
-    }
-
-    public void checkNativeLibraries(BundleRevision revision) throws ResolveException
-    {
-        // Next, try to resolve any native code, since the revision is
-        // not resolvable if its native code cannot be loaded.
-// TODO: OSGi R4.3 - Is it sufficient to just check declared native libs here?
-//        List<R4Library> libs = ((BundleWiringImpl) revision.getWiring()).getNativeLibraries();
-        List<R4Library> libs = ((BundleRevisionImpl) revision).getDeclaredNativeLibraries();
-        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 (!((BundleRevisionImpl) revision).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.isEmpty())
-            {
-                msg = "No matching native libraries found.";
-            }
-            if (msg != null)
-            {
-                throw new ResolveException(msg, revision, null);
-            }
-        }
-    }
-
-    //
-    // Utility methods.
-    //
-
-    /**
-     * 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 fwkExecEnvStr Comma delimited string of provided execution environments
-     * @return the parsed set of execution environments
-    **/
-    private static Set<String> parseExecutionEnvironments(String fwkExecEnvStr)
-    {
-        Set<String> newSet = new HashSet<String>();
-        if (fwkExecEnvStr != null)
-        {
-            StringTokenizer tokens = new StringTokenizer(fwkExecEnvStr, ",");
-            while (tokens.hasMoreTokens())
-            {
-                newSet.add(tokens.nextToken().trim());
-            }
-        }
-        return newSet;
-    }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java b/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
index 2856581..83955d6 100644
--- a/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
+++ b/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
@@ -19,6 +19,7 @@
 package org.apache.felix.framework;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -27,17 +28,30 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.SortedSet;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+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.resolver.ResolverImpl;
 import org.apache.felix.framework.resolver.ResolverWire;
+import org.apache.felix.framework.util.ShrinkableCollection;
 import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.R4Library;
 import org.apache.felix.framework.wiring.BundleCapabilityImpl;
 import org.apache.felix.framework.wiring.BundleRequirementImpl;
 import org.apache.felix.framework.wiring.BundleWireImpl;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundlePermission;
 import org.osgi.framework.Constants;
+import org.osgi.framework.PackagePermission;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
 import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRequirement;
 import org.osgi.framework.wiring.BundleRevision;
@@ -48,6 +62,9 @@
     private final Felix m_felix;
     private final Resolver m_resolver;
     private final ResolverStateImpl m_resolverState;
+    private final List<ResolverHook> m_hooks = new ArrayList<ResolverHook>();
+    private boolean m_isResolving = false;
+    private Collection<BundleRevision> m_whitelist = null;
 
     StatefulResolver(Felix felix)
     {
@@ -72,15 +89,13 @@
         return m_resolverState.getCandidates(req, obeyMandatory);
     }
 
-    void resolve(BundleRevision rootRevision) throws ResolveException
+    void resolve(BundleRevision rootRevision) throws ResolveException, BundleException
     {
         // Although there is a race condition to check the bundle state
         // then lock it, we do this because we don't want to acquire the
         // a lock just to check if the revision is resolved, which itself
         // is a safe read. If the revision isn't resolved, we end up double
         // check the resolved status later.
-// TODO: OSGi R4.3 - This locking strategy here depends on how we ultimately
-//       implement getWiring(), which may change.
         if (rootRevision.getWiring() == null)
         {
             // Acquire global lock.
@@ -91,26 +106,151 @@
                     "Unable to acquire global lock for resolve.", rootRevision, null);
             }
 
+            // Make sure we are not already resolving, which can be
+            // the case if a resolver hook does something bad.
+            if (m_isResolving)
+            {
+                m_felix.releaseGlobalLock();
+                throw new IllegalStateException("Nested resolve operations not allowed.");
+            }
+            m_isResolving = true;
+
             Map<BundleRevision, List<ResolverWire>> wireMap = null;
             try
             {
-                BundleImpl bundle = (BundleImpl) rootRevision.getBundle();
-
                 // Extensions are resolved differently.
+                BundleImpl bundle = (BundleImpl) rootRevision.getBundle();
                 if (bundle.isExtension())
                 {
                     return;
                 }
 
-                // Resolve the revision.
-                wireMap = m_resolver.resolve(
-                    m_resolverState, rootRevision, m_resolverState.getFragments());
+                // Get resolver hook factories.
+                Set<ServiceReference<ResolverHookFactory>> hookRefs =
+                    m_felix.getHooks(ResolverHookFactory.class);
+                if (!hookRefs.isEmpty())
+                {
+                    // Create triggers list.
+                    List<BundleRevision> triggers = new ArrayList<BundleRevision>(1);
+                    triggers.add(rootRevision);
+                    triggers = Collections.unmodifiableList(triggers);
 
-                // Mark all revisions as resolved.
+                    // Create resolver hook objects by calling begin() on factory.
+                    for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+                    {
+                        try
+                        {
+                            ResolverHook hook = m_felix.getService(m_felix, ref).begin(triggers);
+                            if (hook != null)
+                            {
+                                m_hooks.add(hook);
+                            }
+                        }
+                        catch (Throwable ex)
+                        {
+                            throw new BundleException(
+                                "Resolver hook exception: " + ex.getMessage(),
+                                BundleException.REJECTED_BY_HOOK,
+                                ex);
+                        }
+                    }
+
+                    // Ask hooks to indicate which revisions should not be resolved.
+                    m_whitelist =
+                        new ShrinkableCollection<BundleRevision>(
+                            m_resolverState.getUnresolvedRevisions());
+                    int originalSize = m_whitelist.size();
+                    for (ResolverHook hook : m_hooks)
+                    {
+                        try
+                        {
+                            hook.filterResolvable(m_whitelist);
+                        }
+                        catch (Throwable ex)
+                        {
+                            throw new BundleException(
+                                "Resolver hook exception: " + ex.getMessage(),
+                                BundleException.REJECTED_BY_HOOK,
+                                ex);
+                        }
+                    }
+                    // If nothing was removed, then just null the whitelist
+                    // as an optimization.
+                    if (m_whitelist.size() == originalSize)
+                    {
+                        m_whitelist = null;
+                    }
+
+                    // Check to make sure the target revision is allowed to resolve.
+                    if ((m_whitelist != null) && !m_whitelist.contains(rootRevision))
+                    {
+                        throw new ResolveException(
+                            "Resolver hook prevented resolution.", rootRevision, null);
+                    }
+                }
+
+                // Catch any resolve exception to rethrow later because
+                // we may need to call end() on resolver hooks.
+                ResolveException rethrow = null;
+                try
+                {
+                    // Resolve the revision.
+                    wireMap = m_resolver.resolve(
+                        m_resolverState, rootRevision, m_resolverState.getFragments());
+                }
+                catch (ResolveException ex)
+                {
+                    rethrow = ex;
+                }
+
+                // If we have resolver hooks, we must call end() on them.
+                if (!hookRefs.isEmpty())
+                {
+                    // Verify that all resolver hook service references are still valid
+                    // Call end() on resolver hooks.
+                    for (ResolverHook hook : m_hooks)
+                    {
+// TODO: OSGi R4.3/RESOLVER HOOK - We likely need to put these hooks into a map
+//       to their svc ref since we aren't supposed to call end() on unregistered
+//       but currently we call end() on all.
+                        hook.end();
+                    }
+                    // Verify that all hook service references are still valid
+                    // and unget all resolver hook factories.
+                    boolean invalid = false;
+                    for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+                    {
+                        if (ref.getBundle() == null)
+                        {
+                            invalid = true;
+                        }
+                        m_felix.ungetService(m_felix, ref);
+                    }
+                    if (invalid)
+                    {
+                        throw new BundleException(
+                            "Resolver hook service unregistered during resolve.",
+                            BundleException.REJECTED_BY_HOOK);
+                    }
+                }
+
+                // If the resolve failed, rethrow the exception.
+                if (rethrow != null)
+                {
+                    throw rethrow;
+                }
+
+                // Otherwise, mark all revisions as resolved.
                 markResolvedRevisions(wireMap);
             }
             finally
             {
+                // Clear resolving flag.
+                m_isResolving = false;
+                // Clear whitelist.
+                m_whitelist = null;
+                // Always clear any hooks.
+                m_hooks.clear();
                 // Always release the global lock.
                 m_felix.releaseGlobalLock();
             }
@@ -119,8 +259,186 @@
         }
     }
 
+// TODO: OSGi R4.3 - Isn't this method just a generalization of the above method?
+//       Can't we combine them and perhaps simplify the various resolve() methods
+//       here and in Felix.java too?
+    void resolve(Set<BundleRevision> revisions) throws ResolveException, BundleException
+    {
+        // Acquire global lock.
+        boolean locked = m_felix.acquireGlobalLock();
+        if (!locked)
+        {
+            throw new ResolveException(
+                "Unable to acquire global lock for resolve.", null, null);
+        }
+
+        // Make sure we are not already resolving, which can be
+        // the case if a resolver hook does something bad.
+        if (m_isResolving)
+        {
+            m_felix.releaseGlobalLock();
+            throw new IllegalStateException("Nested resolve operations not allowed.");
+        }
+        m_isResolving = true;
+
+        Map<BundleRevision, List<ResolverWire>> wireMap = null;
+        try
+        {
+            // Make our own copy of revisions.
+            revisions = new HashSet<BundleRevision>(revisions);
+
+            // Extensions are resolved differently.
+            for (Iterator<BundleRevision> it = revisions.iterator(); it.hasNext(); )
+            {
+                BundleImpl bundle = (BundleImpl) it.next().getBundle();
+                if (bundle.isExtension())
+                {
+                    it.remove();
+                }
+            }
+
+            // Get resolver hook factories.
+            Set<ServiceReference<ResolverHookFactory>> hookRefs =
+                m_felix.getHooks(ResolverHookFactory.class);
+            if (!hookRefs.isEmpty())
+            {
+                // Create triggers list.
+                Collection<BundleRevision> triggers = Collections.unmodifiableSet(revisions);
+
+                // Create resolver hook objects by calling begin() on factory.
+                for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+                {
+                    try
+                    {
+                        ResolverHookFactory factory = m_felix.getService(m_felix, ref);
+                        if (factory != null)
+                        {
+                            ResolverHook hook = factory.begin(triggers);
+                            if (hook != null)
+                            {
+                                m_hooks.add(hook);
+                            }
+                        }
+                    }
+                    catch (Throwable ex)
+                    {
+                        throw new BundleException(
+                            "Resolver hook exception: " + ex.getMessage(),
+                            BundleException.REJECTED_BY_HOOK,
+                            ex);
+                    }
+                }
+
+                // Ask hooks to indicate which revisions should not be resolved.
+                m_whitelist =
+                    new ShrinkableCollection<BundleRevision>(
+                        m_resolverState.getUnresolvedRevisions());
+                int originalSize = m_whitelist.size();
+                for (ResolverHook hook : m_hooks)
+                {
+                    try
+                    {
+                        hook.filterResolvable(m_whitelist);
+                    }
+                    catch (Throwable ex)
+                    {
+                        throw new BundleException(
+                            "Resolver hook exception: " + ex.getMessage(),
+                            BundleException.REJECTED_BY_HOOK,
+                            ex);
+                    }
+                }
+                // If nothing was removed, then just null the whitelist
+                // as an optimization.
+                if (m_whitelist.size() == originalSize)
+                {
+                    m_whitelist = null;
+                }
+
+                // Check to make sure the target revision is allowed to resolve.
+                if (m_whitelist != null)
+                {
+                    revisions.retainAll(m_whitelist);
+                    if (revisions.isEmpty())
+                    {
+                        throw new ResolveException(
+                            "Resolver hook prevented resolution.", null, null);
+                    }
+                }
+            }
+
+            // Catch any resolve exception to rethrow later because
+            // we may need to call end() on resolver hooks.
+            ResolveException rethrow = null;
+            try
+            {
+                // Resolve the revision.
+// TODO: OSGi R4.3 - Shouldn't we still be passing in greedy attach fragments here?
+                wireMap = m_resolver.resolve(
+                    m_resolverState, revisions, m_resolverState.getFragments());
+            }
+            catch (ResolveException ex)
+            {
+                rethrow = ex;
+            }
+
+            // If we have resolver hooks, we must call end() on them.
+            if (!hookRefs.isEmpty())
+            {
+                // Verify that all resolver hook service references are still valid
+                // Call end() on resolver hooks.
+                for (ResolverHook hook : m_hooks)
+                {
+// TODO: OSGi R4.3/RESOLVER HOOK - We likely need to put these hooks into a map
+//       to their svc ref since we aren't supposed to call end() on unregistered
+//       but currently we call end() on all.
+                    hook.end();
+                }
+                // Verify that all hook service references are still valid
+                // and unget all resolver hook factories.
+                boolean invalid = false;
+                for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+                {
+                    if (ref.getBundle() == null)
+                    {
+                        invalid = true;
+                    }
+                    m_felix.ungetService(m_felix, ref);
+                }
+                if (invalid)
+                {
+                    throw new BundleException(
+                        "Resolver hook service unregistered during resolve.",
+                        BundleException.REJECTED_BY_HOOK);
+                }
+            }
+
+            // If the resolve failed, rethrow the exception.
+            if (rethrow != null)
+            {
+                throw rethrow;
+            }
+
+            // Otherwise, mark all revisions as resolved.
+            markResolvedRevisions(wireMap);
+        }
+        finally
+        {
+            // Clear resolving flag.
+            m_isResolving = false;
+            // Clear whitelist.
+            m_whitelist = null;
+            // Always clear any hooks.
+            m_hooks.clear();
+            // Always release the global lock.
+            m_felix.releaseGlobalLock();
+        }
+
+        fireResolvedEvents(wireMap);
+    }
+
     BundleRevision resolve(BundleRevision revision, String pkgName)
-        throws ResolveException
+        throws ResolveException, BundleException
     {
         BundleRevision provider = null;
 
@@ -139,6 +457,14 @@
                     "Unable to acquire global lock for resolve.", revision, null);
             }
 
+            // Make sure we are not already resolving, which can be
+            // the case if a resolver hook does something bad.
+            if (m_isResolving)
+            {
+                throw new IllegalStateException("Nested resolve operations not allowed.");
+            }
+            m_isResolving = true;
+
             Map<BundleRevision, List<ResolverWire>> wireMap = null;
             try
             {
@@ -150,9 +476,117 @@
                     .getImportedPackageSource(pkgName);
                 if (provider == null)
                 {
-                    wireMap = m_resolver.resolve(
-                        m_resolverState, revision, pkgName,
-                        m_resolverState.getFragments());
+                    // Get resolver hook factories.
+                    Set<ServiceReference<ResolverHookFactory>> hookRefs =
+                        m_felix.getHooks(ResolverHookFactory.class);
+                    if (!hookRefs.isEmpty())
+                    {
+                        // Create triggers list.
+                        List<BundleRevision> triggers = new ArrayList<BundleRevision>(1);
+                        triggers.add(revision);
+                        triggers = Collections.unmodifiableList(triggers);
+
+                        // Create resolver hook objects by calling begin() on factory.
+                        for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+                        {
+                            try
+                            {
+                                ResolverHook hook = m_felix.getService(m_felix, ref).begin(triggers);
+                                if (hook != null)
+                                {
+                                    m_hooks.add(hook);
+                                }
+                            }
+                            catch (Throwable ex)
+                            {
+                                throw new BundleException(
+                                    "Resolver hook exception: " + ex.getMessage(),
+                                    BundleException.REJECTED_BY_HOOK,
+                                    ex);
+                            }
+                        }
+
+                        // Ask hooks to indicate which revisions should not be resolved.
+                        m_whitelist =
+                            new ShrinkableCollection<BundleRevision>(
+                                m_resolverState.getUnresolvedRevisions());
+                        int originalSize = m_whitelist.size();
+                        for (ResolverHook hook : m_hooks)
+                        {
+                            try
+                            {
+                                hook.filterResolvable(m_whitelist);
+                            }
+                            catch (Throwable ex)
+                            {
+                                throw new BundleException(
+                                    "Resolver hook exception: " + ex.getMessage(),
+                                    BundleException.REJECTED_BY_HOOK,
+                                    ex);
+                            }
+                        }
+                        // If nothing was removed, then just null the whitelist
+                        // as an optimization.
+                        if (m_whitelist.size() == originalSize)
+                        {
+                            m_whitelist = null;
+                        }
+
+                        // Since this is a dynamic import, the root revision is
+                        // already resolved, so we don't need to check it against
+                        // the whitelist as we do in other cases.
+                    }
+
+                    // Catch any resolve exception to rethrow later because
+                    // we may need to call end() on resolver hooks.
+                    ResolveException rethrow = null;
+                    try
+                    {
+                        wireMap = m_resolver.resolve(
+                            m_resolverState, revision, pkgName,
+                            m_resolverState.getFragments());
+                    }
+                    catch (ResolveException ex)
+                    {
+                        rethrow = ex;
+                    }
+
+                    // If we have resolver hooks, we must call end() on them.
+                    if (!hookRefs.isEmpty())
+                    {
+                        // Verify that all resolver hook service references are still valid
+                        // Call end() on resolver hooks.
+                        for (ResolverHook hook : m_hooks)
+                        {
+// TODO: OSGi R4.3/RESOLVER HOOK - We likely need to put these hooks into a map
+//       to their svc ref since we aren't supposed to call end() on unregistered
+//       but currently we call end() on all.
+                            hook.end();
+                        }
+                        // Verify that all hook service references are still valid
+                        // and unget all resolver hook factories.
+                        boolean invalid = false;
+                        for (ServiceReference<ResolverHookFactory> ref : hookRefs)
+                        {
+                            if (ref.getBundle() == null)
+                            {
+                                invalid = true;
+                            }
+                            m_felix.ungetService(m_felix, ref);
+                        }
+                        if (invalid)
+                        {
+                            throw new BundleException(
+                                "Resolver hook service unregistered during resolve.",
+                                BundleException.REJECTED_BY_HOOK);
+                        }
+                    }
+
+                    // If the resolve failed, rethrow the exception.
+                    if (rethrow != null)
+                    {
+                        throw rethrow;
+                    }
 
                     if ((wireMap != null) && wireMap.containsKey(revision))
                     {
@@ -190,6 +624,12 @@
             }
             finally
             {
+                // Clear resolving flag.
+                m_isResolving = false;
+                // Clear whitelist.
+                m_whitelist = null;
+                // Always clear any hooks.
+                m_hooks.clear();
                 // Always release the global lock.
                 m_felix.releaseGlobalLock();
             }
@@ -225,7 +665,7 @@
         for (BundleCapability cap : revision.getWiring().getCapabilities(null))
         {
             if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
-                && cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
+                && cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
             {
                 return false;
             }
@@ -242,7 +682,7 @@
         // there is a matching one for the package from which we want to
         // load a class.
         Map<String, Object> attrs = new HashMap(1);
-        attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+        attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
         BundleRequirementImpl req = new BundleRequirementImpl(
             revision,
             BundleRevision.PACKAGE_NAMESPACE,
@@ -342,7 +782,7 @@
                         {
                             importedPkgs.put(
                                 (String) rw.getCapability().getAttributes()
-                                    .get(BundleCapabilityImpl.PACKAGE_ATTR),
+                                    .get(BundleRevision.PACKAGE_NAMESPACE),
                                 rw.getProvider());
                         }
                         else if (rw.getCapability().getNamespace()
@@ -527,7 +967,7 @@
                 if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
                 {
                     pkgs.add((String)
-                        cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+                        cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
                 }
             }
 
@@ -577,4 +1017,351 @@
 
         return pkgs;
     }
+
+    class ResolverStateImpl implements Resolver.ResolverState
+    {
+        private final Logger m_logger;
+        // Set of all revisions.
+        private final Set<BundleRevision> m_revisions;
+        // Set of all fragments.
+        private final Set<BundleRevision> m_fragments;
+        // Capability sets.
+        private final Map<String, CapabilitySet> m_capSets;
+        // Execution environment.
+        private final String m_fwkExecEnvStr;
+        // Parsed framework environments
+        private final Set<String> m_fwkExecEnvSet;
+
+//    void dump()
+//    {
+//        for (Entry<String, CapabilitySet> entry : m_capSets.entrySet())
+//        {
+//            System.out.println("+++ START CAPSET " + entry.getKey());
+//            entry.getValue().dump();
+//            System.out.println("+++ END CAPSET " + entry.getKey());
+//        }
+//    }
+
+        ResolverStateImpl(Logger logger, String fwkExecEnvStr)
+        {
+            m_logger = logger;
+            m_revisions = new HashSet<BundleRevision>();
+            m_fragments = new HashSet<BundleRevision>();
+            m_capSets = new HashMap<String, CapabilitySet>();
+
+            m_fwkExecEnvStr = (fwkExecEnvStr != null) ? fwkExecEnvStr.trim() : null;
+            m_fwkExecEnvSet = parseExecutionEnvironments(fwkExecEnvStr);
+
+            List<String> indices = new ArrayList<String>();
+            indices.add(BundleRevision.BUNDLE_NAMESPACE);
+            m_capSets.put(BundleRevision.BUNDLE_NAMESPACE, new CapabilitySet(indices, true));
+
+            indices = new ArrayList<String>();
+            indices.add(BundleRevision.PACKAGE_NAMESPACE);
+            m_capSets.put(BundleRevision.PACKAGE_NAMESPACE, new CapabilitySet(indices, true));
+
+            indices = new ArrayList<String>();
+            indices.add(BundleRevision.HOST_NAMESPACE);
+            m_capSets.put(BundleRevision.HOST_NAMESPACE,  new CapabilitySet(indices, true));
+        }
+
+// TODO: OSGi R4.3/RESOLVER HOOK - We could maintain a separate list to optimize this.
+        synchronized Set<BundleRevision> getUnresolvedRevisions()
+        {
+            Set<BundleRevision> unresolved = new HashSet<BundleRevision>();
+            for (BundleRevision revision : m_revisions)
+            {
+                if (revision.getWiring() == null)
+                {
+                    unresolved.add(revision);
+                }
+            }
+            return unresolved;
+        }
+
+        synchronized void addRevision(BundleRevision br)
+        {
+            m_revisions.add(br);
+            List<BundleCapability> caps = (br.getWiring() == null)
+                ? br.getDeclaredCapabilities(null)
+                : br.getWiring().getCapabilities(null);
+            if (caps != null)
+            {
+                for (BundleCapability cap : caps)
+                {
+                    CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+                    if (capSet == null)
+                    {
+                        capSet = new CapabilitySet(null, true);
+                        m_capSets.put(cap.getNamespace(), capSet);
+                    }
+                    capSet.addCapability(cap);
+                }
+            }
+
+            if (Util.isFragment(br))
+            {
+                m_fragments.add(br);
+            }
+        }
+
+        synchronized void removeRevision(BundleRevision br)
+        {
+            m_revisions.remove(br);
+            List<BundleCapability> caps = (br.getWiring() == null)
+                ? br.getDeclaredCapabilities(null)
+                : br.getWiring().getCapabilities(null);
+            if (caps != null)
+            {
+                for (BundleCapability cap : caps)
+                {
+                    CapabilitySet capSet = m_capSets.get(cap.getNamespace());
+                    if (capSet != null)
+                    {
+                        capSet.removeCapability(cap);
+                    }
+                }
+            }
+
+            if (Util.isFragment(br))
+            {
+                m_fragments.remove(br);
+            }
+        }
+
+        synchronized Set<BundleRevision> getFragments()
+        {
+            return new HashSet(m_fragments);
+        }
+
+// TODO: OSGi R4.3 - This will need to be changed once BundleWiring.getCapabilities()
+//       is correctly implemented, since it already has to remove substituted caps.
+        synchronized void removeSubstitutedCapabilities(BundleRevision br)
+        {
+            if (br.getWiring() != null)
+            {
+                // Loop through the revision's package wires and determine if any
+                // of them overlap any of the packages exported by the revision.
+                // If so, then the framework must have chosen to have the revision
+                // import rather than export the package, so we need to remove the
+                // corresponding package capability from the package capability set.
+                for (BundleWire w : br.getWiring().getRequiredWires(null))
+                {
+                    if (w.getCapability().getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                    {
+                        for (BundleCapability cap : br.getWiring().getCapabilities(null))
+                        {
+                            if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                                && w.getCapability().getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
+                                    .equals(cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)))
+                            {
+                                m_capSets.get(BundleRevision.PACKAGE_NAMESPACE).removeCapability(cap);
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        //
+        // ResolverState methods.
+        //
+
+        public synchronized SortedSet<BundleCapability> getCandidates(
+            BundleRequirementImpl req, boolean obeyMandatory)
+        {
+            BundleRevisionImpl reqRevision = (BundleRevisionImpl) req.getRevision();
+            SortedSet<BundleCapability> result =
+                new TreeSet<BundleCapability>(new CandidateComparator());
+
+            CapabilitySet capSet = m_capSets.get(req.getNamespace());
+            if (capSet != null)
+            {
+                Set<BundleCapability> matches = capSet.match(req.getFilter(), obeyMandatory);
+                for (BundleCapability cap : matches)
+                {
+                    if (System.getSecurityManager() != null)
+                    {
+                        if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE) && (
+                            !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
+                                new PackagePermission((String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
+                                PackagePermission.EXPORTONLY)) ||
+                                !((reqRevision == null) ||
+                                    ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
+                                        new PackagePermission((String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
+                                        cap.getRevision().getBundle(),PackagePermission.IMPORT))
+                                )))
+                        {
+                            if (reqRevision != cap.getRevision())
+                            {
+                                continue;
+                            }
+                        }
+                        else if (req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE) && (
+                            !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain()).impliesDirect(
+                                new BundlePermission(cap.getRevision().getSymbolicName(), BundlePermission.PROVIDE)) ||
+                                !((reqRevision == null) ||
+                                    ((BundleProtectionDomain) reqRevision.getProtectionDomain()).impliesDirect(
+                                        new BundlePermission(reqRevision.getSymbolicName(), BundlePermission.REQUIRE))
+                                )))
+                        {
+                            continue;
+                        }
+                        else if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE) &&
+                            (!((BundleProtectionDomain) reqRevision.getProtectionDomain())
+                                .impliesDirect(new BundlePermission(
+                                    reqRevision.getSymbolicName(),
+                                    BundlePermission.FRAGMENT))
+                            || !((BundleProtectionDomain) ((BundleRevisionImpl) cap.getRevision()).getProtectionDomain())
+                                .impliesDirect(new BundlePermission(
+                                    cap.getRevision().getSymbolicName(),
+                                    BundlePermission.HOST))))
+                        {
+                            continue;
+                        }
+                    }
+
+                    if (req.getNamespace().equals(BundleRevision.HOST_NAMESPACE)
+                        && (cap.getRevision().getWiring() != null))
+                    {
+                        continue;
+                    }
+
+                    result.add(cap);
+                }
+            }
+
+            // If we have resolver hooks, then we may need to filter our results
+            // based on a whitelist and/or fine-grained candidate filtering.
+            if (!result.isEmpty() && !m_hooks.isEmpty())
+            {
+                // It we have a whitelist, then first filter out candidates
+                // from disallowed revisions.
+// TODO: OSGi R4.3 - It would be better if we could think of a way to do this
+//       filtering that was less costly. One possibility it to do the check in
+//       ResolverState.checkExecutionEnvironment(), since it will only need to
+//       be done once for any black listed revision. However, as we move toward
+//       OBR-like API, this is a non-standard call, so doing it here is the only
+//       standard way of achieving it.
+                if (m_whitelist != null)
+                {
+                    for (Iterator<BundleCapability> it = result.iterator(); it.hasNext(); )
+                    {
+                        if (!m_whitelist.contains(it.next().getRevision()))
+                        {
+                            it.remove();
+                        }
+                    }
+                }
+
+                // Now give the hooks a chance to do fine-grained filtering.
+                ShrinkableCollection<BundleCapability> shrinkable =
+                    new ShrinkableCollection<BundleCapability>(result);
+                for (ResolverHook hook : m_hooks)
+                {
+                    hook.filterMatches(req, shrinkable);
+                }
+            }
+
+            return result;
+        }
+
+        public void checkExecutionEnvironment(BundleRevision revision) throws ResolveException
+        {
+            String bundleExecEnvStr = (String)
+                ((BundleRevisionImpl) revision).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("")
+                    && (m_fwkExecEnvStr != null)
+                    && (m_fwkExecEnvStr.length() > 0))
+                {
+                    StringTokenizer tokens = new StringTokenizer(bundleExecEnvStr, ",");
+                    boolean found = false;
+                    while (tokens.hasMoreTokens() && !found)
+                    {
+                        if (m_fwkExecEnvSet.contains(tokens.nextToken().trim()))
+                        {
+                            found = true;
+                        }
+                    }
+                    if (!found)
+                    {
+                        throw new ResolveException(
+                            "Execution environment not supported: "
+                            + bundleExecEnvStr, revision, null);
+                    }
+                }
+            }
+        }
+
+        public void checkNativeLibraries(BundleRevision revision) throws ResolveException
+        {
+            // Next, try to resolve any native code, since the revision is
+            // not resolvable if its native code cannot be loaded.
+// TODO: OSGi R4.3 - Is it sufficient to just check declared native libs here?
+//        List<R4Library> libs = ((BundleWiringImpl) revision.getWiring()).getNativeLibraries();
+            List<R4Library> libs = ((BundleRevisionImpl) revision).getDeclaredNativeLibraries();
+            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 (!((BundleRevisionImpl) revision).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.isEmpty())
+                {
+                    msg = "No matching native libraries found.";
+                }
+                if (msg != null)
+                {
+                    throw new ResolveException(msg, revision, null);
+                }
+            }
+        }
+    }
+
+    //
+    // Utility methods.
+    //
+
+    /**
+     * 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 fwkExecEnvStr Comma delimited string of provided execution environments
+     * @return the parsed set of execution environments
+    **/
+    private static Set<String> parseExecutionEnvironments(String fwkExecEnvStr)
+    {
+        Set<String> newSet = new HashSet<String>();
+        if (fwkExecEnvStr != null)
+        {
+            StringTokenizer tokens = new StringTokenizer(fwkExecEnvStr, ",");
+            while (tokens.hasMoreTokens())
+            {
+                newSet.add(tokens.nextToken().trim());
+            }
+        }
+        return newSet;
+    }
 }
\ 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
index 3140a1c..dccd7ca 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/CandidateComparator.java
@@ -47,8 +47,8 @@
         // Compare revision capabilities.
         if ((c == 0) && cap1.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
         {
-            c = ((Comparable) cap1.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE))
-                .compareTo(cap2.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE));
+            c = ((Comparable) cap1.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE))
+                .compareTo(cap2.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE));
             if (c == 0)
             {
                 Version v1 = (!cap1.getAttributes().containsKey(Constants.BUNDLE_VERSION_ATTRIBUTE))
@@ -65,8 +65,8 @@
         // Compare package capabilities.
         else if ((c == 0) && cap1.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
         {
-            c = ((Comparable) cap1.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR))
-                .compareTo(cap2.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+            c = ((Comparable) cap1.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE))
+                .compareTo(cap2.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
             if (c == 0)
             {
                 Version v1 = (!cap1.getAttributes().containsKey(BundleCapabilityImpl.VERSION_ATTR))
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 b5c09cf..bdcf0b3 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
@@ -31,10 +31,8 @@
 import java.util.TreeMap;
 import java.util.TreeSet;
 import org.apache.felix.framework.BundleRevisionImpl;
-import org.apache.felix.framework.BundleWiringImpl;
 import org.apache.felix.framework.resolver.Resolver.ResolverState;
 import org.apache.felix.framework.util.Util;
-import org.apache.felix.framework.wiring.BundleCapabilityImpl;
 import org.apache.felix.framework.wiring.BundleRequirementImpl;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
@@ -44,10 +42,8 @@
 
 class Candidates
 {
-    private final BundleRevision m_root;
-
-    // Set of all candidate bundle revisions.
-    private final Set<BundleRevision> m_candidateRevisions;
+    // Set of all involved bundle revisions.
+    private final Set<BundleRevision> m_involvedRevisions;
     // Maps a capability to requirements that match it.
     private final Map<BundleCapability, Set<BundleRequirement>> m_dependentMap;
     // Maps a requirement to the capability it matches.
@@ -69,23 +65,20 @@
 
     /**
      * Private copy constructor used by the copy() method.
-     * @param root the root module for the resolve.
      * @param dependentMap the capability dependency map.
      * @param candidateMap the requirement candidate map.
      * @param hostFragments the fragment map.
      * @param wrappedHosts the wrapped hosts map.
     **/
     private Candidates(
-        BundleRevision root,
-        Set<BundleRevision> candidateRevisions,
+        Set<BundleRevision> involvedRevisions,
         Map<BundleCapability, Set<BundleRequirement>> dependentMap,
         Map<BundleRequirement, SortedSet<BundleCapability>> candidateMap,
         Map<BundleCapability, Map<String, Map<Version, List<BundleRequirement>>>> hostFragments,
         Map<BundleRevision, HostBundleRevision> wrappedHosts, Map<BundleRevision, Object> populateResultCache,
         boolean fragmentsPresent)
     {
-        m_root = root;
-        m_candidateRevisions = candidateRevisions;
+        m_involvedRevisions = involvedRevisions;
         m_dependentMap = dependentMap;
         m_candidateMap = candidateMap;
         m_hostFragments = hostFragments;
@@ -95,49 +88,17 @@
     }
 
     /**
-     * Constructs a new populated Candidates object for the specified root module.
-     * @param state the resolver state used for populating the candidates.
-     * @param root the root module for the resolve.
+     * Constructs an empty Candidates object.
     **/
-    public Candidates(ResolverState state, BundleRevision root)
+    public Candidates()
     {
-        m_root = root;
-        m_candidateRevisions = new HashSet<BundleRevision>();
+        m_involvedRevisions = new HashSet<BundleRevision>();
         m_dependentMap = new HashMap<BundleCapability, Set<BundleRequirement>>();
         m_candidateMap = new HashMap<BundleRequirement, SortedSet<BundleCapability>>();
         m_hostFragments =
             new HashMap<BundleCapability, Map<String, Map<Version, List<BundleRequirement>>>>();
         m_allWrappedHosts = new HashMap<BundleRevision, HostBundleRevision>();
         m_populateResultCache = new HashMap<BundleRevision, Object>();
-
-        populate(state, m_root);
-    }
-
-    /**
-     * Constructs a new populated Candidates object with the specified root module and
-     * starting requirement and matching candidates. This constructor is used
-     * when the root module is performing a dynamic import for the given
-     * requirement and the given potential candidates.
-     * @param state the resolver state used for populating the candidates.
-     * @param root the module with a dynamic import to resolve.
-     * @param req the requirement being resolved.
-     * @param candidates the potential candidates matching the requirement.
-    **/
-    public Candidates(ResolverState state, BundleRevision root,
-        BundleRequirement req, SortedSet<BundleCapability> candidates)
-    {
-        m_root = root;
-        m_candidateRevisions = new HashSet<BundleRevision>();
-        m_dependentMap = new HashMap<BundleCapability, Set<BundleRequirement>>();
-        m_candidateMap = new HashMap<BundleRequirement, SortedSet<BundleCapability>>();
-        m_hostFragments =
-            new HashMap<BundleCapability, Map<String, Map<Version, List<BundleRequirement>>>>();
-        m_allWrappedHosts = new HashMap<BundleRevision, HostBundleRevision>();
-        m_populateResultCache = new HashMap<BundleRevision, Object>();
-
-        add(req, candidates);
-
-        populateDynamic(state, m_root);
     }
 
     /**
@@ -314,6 +275,9 @@
         }
         else if (cycleCount.intValue() == 0)
         {
+            // Record invoved revision.
+            m_involvedRevisions.add(revision);
+
             // Record that the revision was successfully populated.
             m_populateResultCache.put(revision, Boolean.TRUE);
 
@@ -325,26 +289,31 @@
         }
     }
 
-    public final void populateOptional(ResolverState state, BundleRevision revision)
+// TODO: OSGi R4.3 - Related to resolve() method clean up, can this just
+//       become the normal case? Currently, it just swallows the resolve
+//       exception, which would have to change.
+    public final boolean populate(
+        ResolverState state, BundleRevision revision, boolean isGreedyAttach)
     {
-        // We will always attempt to populate optional fragments, since this
-        // is necessary for greedy resolving of fragment. Howevere, we'll only
-        // attempt to populate optional non-fragment revisions if they aren't
-        // already resolved.
+        // We will always attempt to populate fragments, since this is necessary
+        // for greedy attaching of fragment. However, we'll only attempt to
+        // populate optional non-fragment revisions if they aren't already
+        // resolved.
         boolean isFragment = Util.isFragment(revision);
         if (!isFragment && (revision.getWiring() != null))
         {
-            return;
+            return false;
         }
 
         try
         {
-            // If the optional revision is a fragment, then we only want to populate
-            // the fragment if it has a candidate host in the set of already populated
-            // revisions. 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().
-            if (isFragment)
+            // If the optional revision is a fragment and this is a greedy attach,
+            // then only populate the fragment if it has a candidate host in the
+            // set of already populated revisions. We do this to avoid resolving
+            // unneeded fragments and hosts. If the fragment has a host, we'll
+            // prepopulate the result cache here to avoid having to do the host
+            // lookup again in populate().
+            if (isGreedyAttach && isFragment)
             {
                 // Get the current result cache value, to make sure the revision
                 // hasn't already been populated.
@@ -385,7 +354,7 @@
                     // return since this fragment isn't needed.
                     if (hosts.isEmpty())
                     {
-                        return;
+                        return false;
                     }
 
                     // If there are populates host candidates, then finish up
@@ -423,26 +392,37 @@
         }
         catch (ResolveException ex)
         {
-            // Ignore since the revision is optional.
+            return false;
         }
+
+        return true;
     }
 
-    private boolean isPopulated(BundleRevision revision)
+    public boolean isPopulated(BundleRevision revision)
     {
         Object value = m_populateResultCache.get(revision);
         return ((value != null) && (value instanceof Boolean));
     }
 
-    private void populateDynamic(ResolverState state, BundleRevision revision)
+    public ResolveException getResolveException(BundleRevision revision)
     {
-        // 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.
+        Object value = m_populateResultCache.get(revision);
+        return ((value != null) && (value instanceof ResolveException))
+            ? (ResolveException) value : null;
+    }
+
+    public void populateDynamic(
+        ResolverState state, BundleRevision revision,
+        BundleRequirement req, SortedSet<BundleCapability> candidates)
+    {
+        // Add the dynamic imports candidates.
+// TODO: OSGi R4.3 - Can we just calculate the candidates inside here too?
+//       I think we don't because of performance reasons since we have to
+//       look them up already. If so, maybe it is not worth doing it here.
+        add(req, candidates);
+
+        // Populate the candidates for the dynamic import.
         ResolveException rethrow = null;
-        Entry<BundleRequirement, SortedSet<BundleCapability>> entry =
-            m_candidateMap.entrySet().iterator().next();
-        BundleRequirement dynReq = entry.getKey();
-        SortedSet<BundleCapability> candidates = entry.getValue();
         for (Iterator<BundleCapability> itCandCap = candidates.iterator();
             itCandCap.hasNext(); )
         {
@@ -468,10 +448,12 @@
         {
             if (rethrow == null)
             {
-                rethrow = new ResolveException("Dynamic import failed.", revision, dynReq);
+                rethrow = new ResolveException("Dynamic import failed.", revision, req);
             }
             throw rethrow;
         }
+
+        m_populateResultCache.put(revision, Boolean.TRUE);
     }
 
     /**
@@ -492,16 +474,6 @@
 
         // Record the candidates.
         m_candidateMap.put(req, candidates);
-
-        // Make a list of all candidate revisions for determining singetons.
-        // Add the requirement as a dependent on the candidates. Keep track
-        // of fragments for hosts.
-        for (BundleCapability cap : candidates)
-        {
-            // Remember the revision for all capabilities so we can
-            // determine which ones are singletons.
-            m_candidateRevisions.add(cap.getRevision());
-        }
     }
 
     /**
@@ -569,7 +541,7 @@
 
         final Map<String, BundleRevision> singletons = new HashMap<String, BundleRevision>();
 
-        for (Iterator<BundleRevision> it = m_candidateRevisions.iterator(); it.hasNext(); )
+        for (Iterator<BundleRevision> it = m_involvedRevisions.iterator(); it.hasNext(); )
         {
             BundleRevision br = it.next();
             if (isSingleton(br))
@@ -597,18 +569,25 @@
                     // if it wasn't selected.
                     if (singleton != null)
                     {
-                        removeRevision(singleton);
+                        removeRevision(
+                            singleton,
+                            new ResolveException(
+                                "Conflict with another singleton.", singleton, null));
                     }
                 }
                 else
                 {
-                    removeRevision(br);
+                    removeRevision(br,
+                        new ResolveException(
+                            "Conflict with another singleton.", br, null));
                 }
             }
         }
 
         // If the root is a singleton, then prefer it over any other singleton.
-        if (isSingleton(m_root))
+// TODO: OSGi R4.3/SINGLETON - How do we prefer the root as a singleton?
+/*
+        if ((m_root != null) && isSingleton(m_root))
         {
             BundleRevision singleton = singletons.get(m_root.getSymbolicName());
             singletons.put(m_root.getSymbolicName(), m_root);
@@ -627,6 +606,7 @@
                 removeRevision(singleton);
             }
         }
+*/
 
         // Make sure selected singletons do not conflict with existing
         // singletons passed into this method.
@@ -637,7 +617,9 @@
             if ((singleton != null) && (singleton != existing))
             {
                 singletons.remove(singleton.getSymbolicName());
-                removeRevision(singleton);
+                removeRevision(singleton,
+                    new ResolveException(
+                        "Conflict with another singleton.", singleton, null));
             }
         }
 
@@ -712,7 +694,9 @@
         // Step 3
         for (BundleRevision br : unselectedFragments)
         {
-            removeRevision(br);
+            removeRevision(br,
+                new ResolveException(
+                    "Fragment was not selected for attachment.", br, null));
         }
 
         // Step 4
@@ -722,15 +706,20 @@
             // from the merged host.
             for (BundleCapability c : hostRevision.getDeclaredCapabilities(null))
             {
-                Set<BundleRequirement> dependents =
-                    m_dependentMap.get(((HostedCapability) c).getDeclaredCapability());
-                if (dependents != null)
+                // Don't replace the host capability, since the fragment will
+                // really be attached to the original host, not the wrapper.
+                if (!c.getNamespace().equals(BundleRevision.HOST_NAMESPACE))
                 {
-                    for (BundleRequirement r : dependents)
+                    Set<BundleRequirement> dependents =
+                        m_dependentMap.get(((HostedCapability) c).getDeclaredCapability());
+                    if (dependents != null)
                     {
-                        Set<BundleCapability> cands = m_candidateMap.get(r);
-                        cands.remove(((HostedCapability) c).getDeclaredCapability());
-                        cands.add(c);
+                        for (BundleRequirement r : dependents)
+                        {
+                            Set<BundleCapability> cands = m_candidateMap.get(r);
+                            cands.remove(((HostedCapability) c).getDeclaredCapability());
+                            cands.add(c);
+                        }
                     }
                 }
             }
@@ -806,17 +795,14 @@
      * @param revision the module to remove.
      * @throws ResolveException if removing the module caused the resolve to fail.
     **/
-    private void removeRevision(BundleRevision revision) throws ResolveException
+    private void removeRevision(BundleRevision revision, ResolveException ex)
     {
-        if (m_root.equals(revision))
-        {
-// TODO: SINGLETON RESOLVER - Improve this message.
-            String msg = "Unable to resolve " + m_root;
-            ResolveException ex = new ResolveException(msg, m_root, null);
-            throw ex;
-        }
+        // Add removal reason to result cache.
+        m_populateResultCache.put(revision, ex);
+        // Remove from dependents.
         Set<BundleRevision> unresolvedRevisions = new HashSet<BundleRevision>();
         remove(revision, unresolvedRevisions);
+        // Remove dependents that failed as a result of removing revision.
         while (!unresolvedRevisions.isEmpty())
         {
             Iterator<BundleRevision> it = unresolvedRevisions.iterator();
@@ -928,13 +914,10 @@
                     m_candidateMap.remove(r);
                     if (!((BundleRequirementImpl) r).isOptional())
                     {
-                        if (m_root.equals(r.getRevision()))
-                        {
-                            String msg = "Unable to resolve " + m_root
-                                + ": missing requirement " + r;
-                            ResolveException ex = new ResolveException(msg, m_root, r);
-                            throw ex;
-                        }
+                        String msg = "Unable to resolve " + r.getRevision()
+                            + ": missing requirement " + r;
+                        m_populateResultCache.put(
+                            r.getRevision(), new ResolveException(msg, r.getRevision(), r));
                         unresolvedRevisions.add(r.getRevision());
                     }
                 }
@@ -968,7 +951,7 @@
         }
 
         return new Candidates(
-            m_root, m_candidateRevisions, dependentMap, candidateMap,
+            m_involvedRevisions, dependentMap, candidateMap,
             m_hostFragments, m_allWrappedHosts, m_populateResultCache,
             m_fragmentsPresent);
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java b/framework/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java
index abe24e4..f24ada3 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/HostedCapability.java
@@ -113,7 +113,9 @@
         if (getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
         {
             return "[" + m_host + "] "
-                + getNamespace() + "; " + getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+                + getNamespace()
+                + "; "
+                + getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
         }
         return "[" + m_host + "] " + getNamespace() + "; " + getAttributes();
     }
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
index 25e0ad0..c2f12b7 100644
--- a/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
+++ b/framework/src/main/java/org/apache/felix/framework/resolver/Resolver.java
@@ -31,6 +31,8 @@
     Map<BundleRevision, List<ResolverWire>> resolve(
         ResolverState state, BundleRevision revision, Set<BundleRevision> optional);
     Map<BundleRevision, List<ResolverWire>> resolve(
+        ResolverState state, Set<BundleRevision> revisions, Set<BundleRevision> optional);
+    Map<BundleRevision, List<ResolverWire>> resolve(
         ResolverState state, BundleRevision revision, String pkgName,
         Set<BundleRevision> fragments);
 
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 c0ea759..9e90ea7 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
@@ -71,17 +71,25 @@
 
                 try
                 {
-                    // Populate all candidates.
-                    Candidates allCandidates = new Candidates(state, revision);
+                    // Populate revision's candidates.
+                    Candidates allCandidates = new Candidates();
+                    allCandidates.populate(state, revision);
 
                     // Try to populate optional fragments.
                     for (BundleRevision br : optional)
                     {
-                        allCandidates.populateOptional(state, br);
+                        allCandidates.populate(state, br, true);
                     }
 
                     // Merge any fragments into hosts.
                     allCandidates.prepare(getResolvedSingletons(state));
+                    // Make sure revision is still valid, since it could
+                    // fail due to fragment and/or singleton selection.
+// TODO: OSGi R4.3 - Could this be merged back into Candidates?
+                    if (!allCandidates.isPopulated(revision))
+                    {
+                        throw allCandidates.getResolveException(revision);
+                    }
 
                     // Record the initial candidate permutation.
                     m_usesPermutations.add(allCandidates);
@@ -183,6 +191,182 @@
     }
 
     public Map<BundleRevision, List<ResolverWire>> resolve(
+        ResolverState state, Set<BundleRevision> revisions, Set<BundleRevision> optional)
+    {
+        Map<BundleRevision, List<ResolverWire>> wireMap = new HashMap<BundleRevision, List<ResolverWire>>();
+        Map<BundleRevision, Packages> revisionPkgMap = new HashMap<BundleRevision, Packages>();
+
+        boolean retry;
+        do
+        {
+            retry = false;
+
+            try
+            {
+                // Create object to hold all candidates.
+                Candidates allCandidates = new Candidates();
+
+                // Populate revisions.
+                for (Iterator<BundleRevision> it = revisions.iterator(); it.hasNext(); )
+                {
+                    BundleRevision br = it.next();
+// TODO: OSGi R4.3 - This is not correct for fragments, since they may have wires already
+//       but we still need to resolve them.
+                    if ((br.getWiring() != null) || !allCandidates.populate(state, br, false))
+                    {
+                        it.remove();
+                    }
+                }
+
+                // Try to populate optional fragments.
+                for (BundleRevision br : optional)
+                {
+                    allCandidates.populate(state, br, true);
+                }
+
+                // Merge any fragments into hosts.
+                allCandidates.prepare(getResolvedSingletons(state));
+
+                // Prune failed revisions.
+// TODO: OSGi R4.3 - Again, can we merge this stuff into Candidates?
+//                for (Iterator<BundleRevision> it = revisions.iterator(); it.hasNext(); )
+//                {
+//                    if (!allCandidates.isPopulated(it.next()))
+//                    {
+//                        it.remove();
+//                    }
+//                }
+
+                // Record the initial candidate permutation.
+                m_usesPermutations.add(allCandidates);
+
+                ResolveException rethrow = null;
+
+                // If the requested revision is a fragment, then
+                // ultimately we will verify the host., so store
+                // any host requirements
+                Map<BundleRevision, List<BundleRequirement>> hostReqs =
+                    new HashMap<BundleRevision, List<BundleRequirement>>();
+                for (BundleRevision br : revisions)
+                {
+                    hostReqs.put(
+                        br, br.getDeclaredRequirements(BundleRevision.HOST_NAMESPACE));
+                }
+
+                do
+                {
+                    rethrow = null;
+
+                    revisionPkgMap.clear();
+                    m_packageSourcesCache.clear();
+
+                    allCandidates = (m_usesPermutations.size() > 0)
+                        ? m_usesPermutations.remove(0)
+                        : m_importPermutations.remove(0);
+//allCandidates.dump();
+
+                    for (BundleRevision br : revisions)
+                    {
+                        BundleRevision target = br;
+
+                        // If we are resolving a fragment, then we
+                        // actually want to verify its host.
+                        List<BundleRequirement> hostReq = hostReqs.get(br);
+                        if (!hostReq.isEmpty())
+                        {
+                            target = allCandidates.getCandidates(hostReq.get(0))
+                                .iterator().next().getRevision();
+                        }
+
+                        calculatePackageSpaces(
+                            allCandidates.getWrappedHost(target), allCandidates, revisionPkgMap,
+                            new HashMap(), new HashSet());
+//System.out.println("+++ PACKAGE SPACES START +++");
+//dumpRevisionPkgMap(revisionPkgMap);
+//System.out.println("+++ PACKAGE SPACES END +++");
+
+                        try
+                        {
+                            checkPackageSpaceConsistency(
+                                false, allCandidates.getWrappedHost(target),
+                                allCandidates, revisionPkgMap, new HashMap());
+                        }
+                        catch (ResolveException ex)
+                        {
+                            rethrow = ex;
+                        }
+                    }
+                }
+                while ((rethrow != null)
+                    && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
+
+                // If there is a resolve exception, then determine if an
+                // optionally resolved revision is to blame (typically a fragment).
+                // If so, then remove the optionally resolved resolved and try
+                // again; otherwise, rethrow the resolve exception.
+                if (rethrow != null)
+                {
+                    BundleRevision faultyRevision =
+                        getActualBundleRevision(rethrow.getRevision());
+                    if (rethrow.getRequirement() instanceof HostedRequirement)
+                    {
+                        faultyRevision =
+                            ((HostedRequirement) rethrow.getRequirement())
+                                .getDeclaredRequirement().getRevision();
+                    }
+                    if (revisions.remove(faultyRevision))
+                    {
+                        retry = true;
+                    }
+                    else if (optional.remove(faultyRevision))
+                    {
+                        retry = true;
+                    }
+                    else
+                    {
+                        throw rethrow;
+                    }
+                }
+                // If there is no exception to rethrow, then this was a clean
+                // resolve, so populate the wire map.
+                else
+                {
+                    for (BundleRevision br : revisions)
+                    {
+                        BundleRevision target = br;
+
+                        // If we are resolving a fragment, then we
+                        // actually want to populate its host's wires.
+                        List<BundleRequirement> hostReq = hostReqs.get(br);
+                        if (!hostReq.isEmpty())
+                        {
+                            target = allCandidates.getCandidates(hostReq.get(0))
+                                .iterator().next().getRevision();
+                        }
+
+                        if (allCandidates.isPopulated(target))
+                        {
+                            wireMap =
+                                populateWireMap(
+                                    allCandidates.getWrappedHost(target),
+                                    revisionPkgMap, wireMap, allCandidates);
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                // Always clear the state.
+                m_usesPermutations.clear();
+                m_importPermutations.clear();
+            }
+        }
+        while (retry);
+
+        return wireMap;
+    }
+
+    public Map<BundleRevision, List<ResolverWire>> resolve(
         ResolverState state, BundleRevision revision, String pkgName,
         Set<BundleRevision> optional)
     {
@@ -212,11 +396,18 @@
                     // Try to populate optional fragments.
                     for (BundleRevision br : optional)
                     {
-                        allCandidates.populateOptional(state, br);
+                        allCandidates.populate(state, br, true);
                     }
 
                     // Merge any fragments into hosts.
                     allCandidates.prepare(getResolvedSingletons(state));
+                    // Make sure revision is still valid, since it could
+                    // fail due to fragment and/or singleton selection.
+// TODO: OSGi R4.3 - Could this be merged back into Candidates?
+                    if (!allCandidates.isPopulated(revision))
+                    {
+                        throw allCandidates.getResolveException(revision);
+                    }
 
                     // Record the initial candidate permutation.
                     m_usesPermutations.add(allCandidates);
@@ -349,7 +540,7 @@
         for (BundleCapability cap : revision.getWiring().getCapabilities(null))
         {
             if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
-                && cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
+                && cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
             {
                 return null;
             }
@@ -366,7 +557,7 @@
         // there is a matching one for the package from which we want to
         // load a class.
         Map<String, Object> attrs = new HashMap(1);
-        attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+        attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
         BundleRequirementImpl req = new BundleRequirementImpl(
             revision,
             BundleRevision.PACKAGE_NAMESPACE,
@@ -417,7 +608,8 @@
 
         if (candidates.size() > 0)
         {
-            allCandidates = new Candidates(state, revision, dynReq, candidates);
+            allCandidates = new Candidates();
+            allCandidates.populateDynamic(state, revision, dynReq, candidates);
         }
 
         return allCandidates;
@@ -682,7 +874,7 @@
             // for imported or required packages, appropriately.
 
             String pkgName = (String)
-                candCap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+                candCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
 
             List blameReqs = new ArrayList();
             blameReqs.add(currentReq);
@@ -1147,7 +1339,7 @@
             if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
             {
                 exports.put(
-                    (String) cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR),
+                    (String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
                     cap);
             }
         }
@@ -1165,7 +1357,7 @@
                         BundleRevision.PACKAGE_NAMESPACE))
                     {
                         String pkgName = (String) wire.getCapability()
-                            .getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+                            .getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
                         exports.remove(pkgName);
                     }
                 }
@@ -1181,7 +1373,7 @@
                         if ((cands != null) && !cands.isEmpty())
                         {
                             String pkgName = (String) cands.iterator().next()
-                                .getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR);
+                                .getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
                             exports.remove(pkgName);
                         }
                     }
@@ -1267,7 +1459,7 @@
 
             // Get the package name associated with the capability.
             String pkgName = cap.getAttributes()
-                .get(BundleCapabilityImpl.PACKAGE_ATTR).toString();
+                .get(BundleRevision.PACKAGE_NAMESPACE).toString();
 
             // Since a revision can export the same package more than once, get
             // all package capabilities for the specified package name.
@@ -1277,7 +1469,7 @@
             for (int capIdx = 0; capIdx < caps.size(); capIdx++)
             {
                 if (caps.get(capIdx).getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
-                    && caps.get(capIdx).getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).equals(pkgName))
+                    && caps.get(capIdx).getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
                 {
                     sources.add(caps.get(capIdx));
                 }
@@ -1423,7 +1615,7 @@
             {
                 // Ignore revisions that import themselves.
                 if (!revision.equals(blame.m_cap.getRevision())
-                    && blame.m_cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)
+                    && blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
                         .equals(pkgName))
                 {
                     if (blame.m_cap.getRevision().getWiring() == null)
@@ -1434,7 +1626,7 @@
 
                     Packages candPkgs = revisionPkgMap.get(blame.m_cap.getRevision());
                     Map<String, Object> attrs = new HashMap(1);
-                    attrs.put(BundleCapabilityImpl.PACKAGE_ATTR, pkgName);
+                    attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
                     packageWires.add(
                         new ResolverWireImpl(
                             revision,
@@ -1530,9 +1722,9 @@
                         (BundleRequirementImpl) blame.m_reqs.get(i));
                     if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
                     {
-                        sb.append(BundleCapabilityImpl.PACKAGE_ATTR);
+                        sb.append(BundleRevision.PACKAGE_NAMESPACE);
                         sb.append("=");
-                        sb.append(cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).toString());
+                        sb.append(cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).toString());
                         BundleCapability usedCap;
                         if ((i + 2) < blame.m_reqs.size())
                         {
@@ -1547,7 +1739,7 @@
                                 (BundleRequirementImpl) blame.m_reqs.get(i + 1));
                         }
                         sb.append("; uses:=");
-                        sb.append(usedCap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+                        sb.append(usedCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
                     }
                     else
                     {
@@ -1560,18 +1752,18 @@
                     BundleCapability export = Util.getSatisfyingCapability(
                         blame.m_cap.getRevision(),
                         (BundleRequirementImpl) blame.m_reqs.get(i));
-                    sb.append(BundleCapabilityImpl.PACKAGE_ATTR);
+                    sb.append(BundleRevision.PACKAGE_NAMESPACE);
                     sb.append("=");
-                    sb.append(export.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).toString());
-                    if (!export.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)
-                        .equals(blame.m_cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)))
+                    sb.append(export.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).toString());
+                    if (!export.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
+                        .equals(blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)))
                     {
                         sb.append("; uses:=");
-                        sb.append(blame.m_cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+                        sb.append(blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
                         sb.append("\n    export: ");
-                        sb.append(BundleCapabilityImpl.PACKAGE_ATTR);
+                        sb.append(BundleRevision.PACKAGE_NAMESPACE);
                         sb.append("=");
-                        sb.append(blame.m_cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR).toString());
+                        sb.append(blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).toString());
                     }
                     sb.append("\n  ");
                     sb.append(blame.m_cap.getRevision().getSymbolicName());
@@ -1617,7 +1809,7 @@
         public String toString()
         {
             return m_cap.getRevision()
-                + "." + m_cap.getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR)
+                + "." + m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
                 + (((m_reqs == null) || m_reqs.isEmpty())
                     ? " NO BLAME"
                     : " BLAMED ON " + m_reqs);
diff --git a/framework/src/main/java/org/apache/felix/framework/util/ShrinkableCollection.java b/framework/src/main/java/org/apache/felix/framework/util/ShrinkableCollection.java
index 5719e17..8313d70 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/ShrinkableCollection.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/ShrinkableCollection.java
@@ -24,21 +24,21 @@
 /** This collection wraps any other collection but prohibits calls to add
  *  elements to the collection.
  */
-public class ShrinkableCollection implements Collection
+public class ShrinkableCollection<T> implements Collection<T>
 {
-    private final Collection m_delegate;
+    private final Collection<T> m_delegate;
 
-    public ShrinkableCollection(Collection delegate)
+    public ShrinkableCollection(Collection<T> delegate)
     {
         m_delegate = delegate;
     }
 
-    public boolean add(Object o)
+    public boolean add(T o)
     {
         throw new UnsupportedOperationException();
     }
 
-    public boolean addAll(Collection c)
+    public boolean addAll(Collection<? extends T> c)
     {
         throw new UnsupportedOperationException();
     }
@@ -53,16 +53,18 @@
         return m_delegate.contains(o);
     }
 
-    public boolean containsAll(Collection c)
+    public boolean containsAll(Collection<?> c)
     {
         return m_delegate.containsAll(c);
     }
 
+    @Override
     public boolean equals(Object o)
     {
         return m_delegate.equals(o);
     }
 
+    @Override
     public int hashCode()
     {
         return m_delegate.hashCode();
@@ -83,12 +85,12 @@
         return m_delegate.remove(o);
     }
 
-    public boolean removeAll(Collection c)
+    public boolean removeAll(Collection<?> c)
     {
         return m_delegate.removeAll(c);
     }
 
-    public boolean retainAll(Collection c)
+    public boolean retainAll(Collection<?> c)
     {
         return m_delegate.retainAll(c);
     }
@@ -103,7 +105,7 @@
         return m_delegate.toArray();
     }
 
-    public Object[] toArray(Object[] a)
+    public <A> A[] toArray(A[] a)
     {
         return m_delegate.toArray(a);
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/util/Util.java b/framework/src/main/java/org/apache/felix/framework/util/Util.java
index 3bdf0c3..d2b9151 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/Util.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/Util.java
@@ -358,7 +358,7 @@
                     if (w.getCapability().getNamespace()
                             .equals(BundleRevision.PACKAGE_NAMESPACE) &&
                         w.getCapability().getAttributes()
-                            .get(BundleCapabilityImpl.PACKAGE_ATTR).equals(name))
+                            .get(BundleRevision.PACKAGE_NAMESPACE).equals(name))
                     {
                         return w;
                     }
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index f862ced..58ed7ec 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -95,11 +95,11 @@
         // Parse bundle symbolic name.
         //
 
-        BundleCapabilityImpl requireCap = parseBundleSymbolicName(owner, m_headerMap);
-        if (requireCap != null)
+        BundleCapabilityImpl bundleCap = parseBundleSymbolicName(owner, m_headerMap);
+        if (bundleCap != null)
         {
             m_bundleSymbolicName = (String)
-                requireCap.getAttributes().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+                bundleCap.getAttributes().get(BundleRevision.BUNDLE_NAMESPACE);
 
             // Add a bundle capability and a host capability to all
             // non-fragment bundles. A host capability is the same
@@ -109,12 +109,15 @@
             // dependencies.
             if (headerMap.get(Constants.FRAGMENT_HOST) == null)
             {
-                capList.add(requireCap);
+                capList.add(bundleCap);
+                Map<String, Object> hostAttrs =
+                    new HashMap<String, Object>(bundleCap.getAttributes());
+                Object value = hostAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
+                hostAttrs.put(BundleRevision.HOST_NAMESPACE, value);
                 capList.add(new BundleCapabilityImpl(
                     owner, BundleRevision.HOST_NAMESPACE,
                     Collections.EMPTY_MAP,
-// TODO: OSGi R4.3 - Wraps map as unmodifiable twice.
-                    requireCap.getAttributes()));
+                    hostAttrs));
             }
 
             // Add a singleton capability if the bundle is a singleton.
@@ -123,13 +126,16 @@
             // attach this information to the bundle or host capabilities
             // because fragments don't have those capabilities, but fragments
             // can be singletons too.
-            if (isSingleton(requireCap))
+            if (isSingleton(bundleCap))
             {
+                Map<String, Object> singletonAttrs =
+                    new HashMap<String, Object>(bundleCap.getAttributes());
+                Object value = singletonAttrs.remove(BundleRevision.BUNDLE_NAMESPACE);
+                singletonAttrs.put(BundleCapabilityImpl.SINGLETON_NAMESPACE, value);
                 capList.add(new BundleCapabilityImpl(
                     owner, BundleCapabilityImpl.SINGLETON_NAMESPACE,
                     Collections.EMPTY_MAP,
-// TODO: OSGi R4.3 - Wraps map as unmodifiable twice.
-                    requireCap.getAttributes()));
+                    singletonAttrs));
             }
         }
 
@@ -418,7 +424,7 @@
 //       notion where namespace is also the name of the key attribute.
                 Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
                 newAttrs.put(
-                    BundleCapabilityImpl.PACKAGE_ATTR,
+                    BundleRevision.PACKAGE_NAMESPACE,
                     path);
                 newAttrs.putAll(attrs);
 
@@ -812,7 +818,7 @@
                 Map<String, Object> attrs = clause.m_attrs;
                 Map<String, Object> newAttrs = new HashMap<String, Object>(attrs.size() + 1);
                 newAttrs.put(
-                    BundleCapabilityImpl.PACKAGE_ATTR,
+                    BundleRevision.PACKAGE_NAMESPACE,
                     pkgName);
                 newAttrs.putAll(attrs);
 
@@ -1124,7 +1130,7 @@
         for (int i = 0; i < exports.size(); i++)
         {
             if (map.get(exports.get(i).getAttributes()
-                .get(BundleCapabilityImpl.PACKAGE_ATTR)) == null)
+                .get(BundleRevision.PACKAGE_NAMESPACE)) == null)
             {
                 // Convert Version to VersionRange.
                 Map<String, Object> attrs = new HashMap<String, Object>();
@@ -1138,7 +1144,7 @@
 
                 List<String> paths = new ArrayList();
                 paths.add((String)
-                    exports.get(i).getAttributes().get(BundleCapabilityImpl.PACKAGE_ATTR));
+                    exports.get(i).getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
                 clauseList.add(
                     new ParsedHeaderClause(
                         paths, Collections.EMPTY_MAP, attrs, Collections.EMPTY_MAP));
@@ -1252,7 +1258,7 @@
             // Create a require capability and return it.
             String symName = (String) clauses.get(0).m_paths.get(0);
             Map<String, Object> attrs = new HashMap<String, Object>(2);
-            attrs.put(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE, symName);
+            attrs.put(BundleRevision.BUNDLE_NAMESPACE, symName);
             attrs.put(Constants.BUNDLE_VERSION_ATTRIBUTE, bundleVersion);
             return new BundleCapabilityImpl(
                 owner,
@@ -1317,7 +1323,7 @@
                 Map<String, Object> attrs = clauses.get(0).m_attrs;
                 Map<String, Object> newAttrs = new HashMap<String, Object>(attrs.size() + 1);
                 newAttrs.put(
-                    Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE,
+                    BundleRevision.HOST_NAMESPACE,
                     clauses.get(0).m_paths.get(0));
                 newAttrs.putAll(attrs);
 
@@ -1403,7 +1409,7 @@
                 // Prepend the symbolic name to the array of attributes.
                 Map<String, Object> newAttrs = new LinkedHashMap<String, Object>(attrs.size() + 1);
                 newAttrs.put(
-                    Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE,
+                    BundleRevision.BUNDLE_NAMESPACE,
                     path);
                 newAttrs.putAll(attrs);
 
diff --git a/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
index 77c24ae..72529cc 100644
--- a/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/wiring/BundleCapabilityImpl.java
@@ -36,7 +36,6 @@
 {
     public static final String SINGLETON_NAMESPACE = "singleton";
 
-    public static final String PACKAGE_ATTR = "package";
     public static final String VERSION_ATTR = "version";
 
     private final BundleRevision m_revision;
@@ -199,7 +198,7 @@
         if (m_namespace.equals(BundleRevision.PACKAGE_NAMESPACE))
         {
             return "[" + m_revision + "] "
-                + m_namespace + "; " + m_attrs.get(PACKAGE_ATTR);
+                + m_namespace + "; " + m_attrs.get(BundleRevision.PACKAGE_NAMESPACE);
         }
         return "[" + m_revision + "] " + m_namespace + "; " + m_attrs;
     }