Copied from sandbox.


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1311259 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/resolver/pom.xml b/resolver/pom.xml
new file mode 100644
index 0000000..f1b2f49
--- /dev/null
+++ b/resolver/pom.xml
@@ -0,0 +1,112 @@
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.apache.felix</groupId>
+    <artifactId>felix-parent</artifactId>
+    <version>2.1</version>
+    <relativePath>../pom/pom.xml</relativePath>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <packaging>bundle</packaging>
+  <name>Apache Felix Resolver</name>
+  <description>
+    Provide OSGi resolver service.
+  </description>
+  <version>0.1.0-SNAPSHOT</version>
+  <artifactId>org.apache.felix.resolver</artifactId>
+  <dependencies>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <version>5.0.0</version>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <target>jsr14</target>
+          <source>1.5</source>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>buildnumber-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>validate</phase>
+            <goals>
+              <goal>create</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <doCheck>false</doCheck>
+          <doUpdate>true</doUpdate>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.3.7</version>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <_sources>true</_sources>
+            <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+            <Bundle-Version>0.1.0.r${buildNumber}</Bundle-Version>
+            <Bundle-Activator>
+              org.apache.felix.resolver.Activator
+            </Bundle-Activator>
+            <Private-Package>org.apache.*</Private-Package>
+            <Export-Package>
+              org.osgi.service.resolver.*;provide:=true
+            </Export-Package>
+            <Import-Package>
+              org.osgi.resource.*;provide:=true, *
+            </Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>verify</phase>
+            <goals>
+              <goal>check</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <includes>
+            <include>src/**</include>
+          </includes>
+          <excludes>
+            <exclude>src/**/packageinfo</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/resolver/src/main/java/org/apache/felix/resolver/Activator.java b/resolver/src/main/java/org/apache/felix/resolver/Activator.java
new file mode 100644
index 0000000..0d1c262
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/Activator.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.resolver;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.resolver.Resolver;
+
+public class Activator implements BundleActivator
+{
+    public static final String LOG_LEVEL = "felix.resolver.log.level";
+
+    public void start(BundleContext bc) throws Exception
+    {
+        int logLevel = 4;
+        if (bc.getProperty(LOG_LEVEL) != null)
+        {
+            try
+            {
+                logLevel = Integer.parseInt(bc.getProperty(LOG_LEVEL));
+            }
+            catch (NumberFormatException ex)
+            {
+                // Use default log level.
+            }
+        }
+        bc.registerService(
+            Resolver.class,
+            new ResolverImpl(new Logger(logLevel)),
+            null);
+    }
+
+    public void stop(BundleContext bc) throws Exception
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/Candidates.java b/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
new file mode 100644
index 0000000..b3dc3e2
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/Candidates.java
@@ -0,0 +1,1049 @@
+/*
+ * 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.resolver;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.HostNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+import org.osgi.resource.Wiring;
+import org.osgi.service.resolver.HostedCapability;
+import org.osgi.service.resolver.ResolutionException;
+import org.osgi.service.resolver.ResolveContext;
+
+class Candidates
+{
+    public static final int MANDATORY = 0;
+    public static final int OPTIONAL = 1;
+    public static final int ON_DEMAND = 2;
+
+    private final Set<Resource> m_mandatoryResources;
+    // Maps a capability to requirements that match it.
+    private final Map<Capability, Set<Requirement>> m_dependentMap;
+    // Maps a requirement to the capability it matches.
+    private final Map<Requirement, List<Capability>> m_candidateMap;
+    // Maps a bundle revision to its associated wrapped revision; this only happens
+    // when a revision being resolved has fragments to attach to it.
+    private final Map<Resource, WrappedResource> m_allWrappedHosts;
+    // Map used when populating candidates to hold intermediate and final results.
+    private final Map<Resource, Object> m_populateResultCache;
+
+    // Flag to signal if fragments are present in the candidate map.
+    private boolean m_fragmentsPresent = false;
+
+    /**
+     * Private copy constructor used by the copy() method.
+     * @param dependentMap the capability dependency map.
+     * @param candidateMap the requirement candidate map.
+     * @param hostFragments the fragment map.
+     * @param wrappedHosts the wrapped hosts map.
+    **/
+    private Candidates(
+        Set<Resource> mandatoryResources,
+        Map<Capability, Set<Requirement>> dependentMap,
+        Map<Requirement, List<Capability>> candidateMap,
+        Map<Resource, WrappedResource> wrappedHosts, Map<Resource, Object> populateResultCache,
+        boolean fragmentsPresent)
+    {
+        m_mandatoryResources = mandatoryResources;
+        m_dependentMap = dependentMap;
+        m_candidateMap = candidateMap;
+        m_allWrappedHosts = wrappedHosts;
+        m_populateResultCache = populateResultCache;
+        m_fragmentsPresent = fragmentsPresent;
+    }
+
+    /**
+     * Constructs an empty Candidates object.
+    **/
+    public Candidates()
+    {
+        m_mandatoryResources = new HashSet<Resource>();
+        m_dependentMap = new HashMap<Capability, Set<Requirement>>();
+        m_candidateMap = new HashMap<Requirement, List<Capability>>();
+        m_allWrappedHosts = new HashMap<Resource, WrappedResource>();
+        m_populateResultCache = new HashMap<Resource, Object>();
+    }
+
+    /**
+     * Populates candidates for the specified revision. How a revision is
+     * resolved depends on its resolution type as follows:
+     * <ul>
+     *   <li><tt>MANDATORY</tt> - must resolve and failure to do so throws
+     *       an exception.</li>
+     *   <li><tt>OPTIONAL</tt> - attempt to resolve, but no exception is thrown
+     *       if the resolve fails.</li>
+     *   <li><tt>ON_DEMAND</tt> - only resolve on demand; this only applies to
+     *       fragments and will only resolve a fragment if its host is already
+     *       selected as a candidate.</li>
+     * </ul>
+     * @param state the resolver state used for populating the candidates.
+     * @param revision the revision whose candidates should be populated.
+     * @param resolution indicates the resolution type.
+     */
+    public final void populate(
+        ResolveContext rc, Resource resource, int resolution) throws ResolutionException
+    {
+        // Get the current result cache value, to make sure the revision
+        // hasn't already been populated.
+        Object cacheValue = m_populateResultCache.get(resource);
+        // Has been unsuccessfully populated.
+        if (cacheValue instanceof ResolutionException)
+        {
+            return;
+        }
+        // Has been successfully populated.
+        else if (cacheValue instanceof Boolean)
+        {
+            return;
+        }
+
+        // We will always attempt to populate fragments, since this is necessary
+        // for ondemand attaching of fragment. However, we'll only attempt to
+        // populate optional non-fragment revisions if they aren't already
+        // resolved.
+        boolean isFragment = Util.isFragment(resource);
+        if (!isFragment && rc.getWirings().containsKey(resource))
+        {
+            return;
+        }
+
+        // Always attempt to populate mandatory or optional revisions.
+        // However, for on-demand fragments only populate if their host
+        // is already populated.
+        if ((resolution != ON_DEMAND)
+            || (isFragment && populateFragmentOndemand(rc, resource)))
+        {
+            if (resolution == MANDATORY)
+            {
+                m_mandatoryResources.add(resource);
+            }
+            try
+            {
+                // Try to populate candidates for the optional revision.
+                populateResource(rc, resource);
+            }
+            catch (ResolutionException ex)
+            {
+                // Only throw an exception if resolution is mandatory.
+                if (resolution == MANDATORY)
+                {
+                    throw ex;
+                }
+            }
+        }
+    }
+
+    /**
+     * Populates candidates for the specified revision.
+     * @param state the resolver state used for populating the candidates.
+     * @param revision the revision whose candidates should be populated.
+     */
+// TODO: FELIX3 - Modify to not be recursive.
+    private void populateResource(ResolveContext rc, Resource resource) throws ResolutionException
+    {
+        // Determine if we've already calculated this revision's candidates.
+        // The result cache will have one of three values:
+        //   1. A resolve exception if we've already attempted to populate the
+        //      revision's candidates but were unsuccessful.
+        //   2. Boolean.TRUE indicating we've already attempted to populate the
+        //      revision's candidates and were successful.
+        //   3. An array containing the cycle count, current map of candidates
+        //      for already processed requirements, and a list of remaining
+        //      requirements whose candidates still need to be calculated.
+        // For case 1, rethrow the exception. For case 2, simply return immediately.
+        // For case 3, this means we have a cycle so we should continue to populate
+        // the candidates where we left off and not record any results globally
+        // until we've popped completely out of the cycle.
+
+        // Keeps track of the number of times we've reentered this method
+        // for the current revision.
+        Integer cycleCount = null;
+
+        // Keeps track of the candidates we've already calculated for the
+        // current revision's requirements.
+        Map<Requirement, List<Capability>> localCandidateMap = null;
+
+        // Keeps track of the current revision's requirements for which we
+        // haven't yet found candidates.
+        List<Requirement> remainingReqs = null;
+
+        // Get the cache value for the current revision.
+        Object cacheValue = m_populateResultCache.get(resource);
+
+        // This is case 1.
+        if (cacheValue instanceof ResolutionException)
+        {
+            throw (ResolutionException) cacheValue;
+        }
+        // This is case 2.
+        else if (cacheValue instanceof Boolean)
+        {
+            return;
+        }
+        // This is case 3.
+        else if (cacheValue != null)
+        {
+            // Increment and get the cycle count.
+            cycleCount = (Integer)
+                (((Object[]) cacheValue)[0]
+                    = new Integer(((Integer) ((Object[]) cacheValue)[0]).intValue() + 1));
+            // Get the already populated candidates.
+            localCandidateMap = (Map) ((Object[]) cacheValue)[1];
+            // Get the remaining requirements.
+            remainingReqs = (List) ((Object[]) cacheValue)[2];
+        }
+
+        // If there is no cache value for the current revision, then this is
+        // the first time we are attempting to populate its candidates, so
+        // do some one-time checks and initialization.
+        if ((remainingReqs == null) && (localCandidateMap == null))
+        {
+            // Record cycle count.
+            cycleCount = new Integer(0);
+
+            // Create a local map for populating candidates first, just in case
+            // the revision is not resolvable.
+            localCandidateMap = new HashMap();
+
+            // Create a modifiable list of the revision's requirements.
+            remainingReqs = new ArrayList(resource.getRequirements(null));
+
+            // Add these value to the result cache so we know we are
+            // in the middle of populating candidates for the current
+            // revision.
+            m_populateResultCache.put(resource,
+                cacheValue = new Object[] { cycleCount, localCandidateMap, remainingReqs });
+        }
+
+        // If we have requirements remaining, then find candidates for them.
+        while (!remainingReqs.isEmpty())
+        {
+            Requirement req = remainingReqs.remove(0);
+
+            // Ignore non-effective and dynamic requirements.
+            String resolution = req.getDirectives()
+                .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE);
+            if (!rc.isEffective(req)
+                || ((resolution != null)
+                    && resolution.equals(PackageNamespace.RESOLUTION_DYNAMIC)))
+            {
+                continue;
+            }
+
+            // Process the candidates, removing any candidates that
+            // cannot resolve.
+            List<Capability> candidates = rc.findProviders(req);
+            ResolutionException rethrow = processCandidates(rc, resource, candidates);
+
+            // First, due to cycles, makes sure we haven't already failed in
+            // a deeper recursion.
+            Object result = m_populateResultCache.get(resource);
+            if (result instanceof ResolutionException)
+            {
+                throw (ResolutionException) result;
+            }
+            // Next, if are no candidates remaining and the requirement is not
+            // not optional, then record and throw a resolve exception.
+            else if (candidates.isEmpty() && !Util.isOptional(req))
+            {
+                String msg = "Unable to resolve " + resource
+                    + ": missing requirement " + req;
+                if (rethrow != null)
+                {
+                    msg = msg + " [caused by: " + rethrow.getMessage() + "]";
+                }
+                rethrow = new ResolutionException(msg, null, Collections.singleton(req));
+                m_populateResultCache.put(resource, rethrow);
+                throw rethrow;
+            }
+            // Otherwise, if we actually have candidates for the requirement, then
+            // add them to the local candidate map.
+            else if (candidates.size() > 0)
+            {
+                localCandidateMap.put(req, candidates);
+            }
+        }
+
+        // If we are exiting from a cycle then decrement
+        // cycle counter, otherwise record the result.
+        if (cycleCount.intValue() > 0)
+        {
+            ((Object[]) cacheValue)[0] = new Integer(cycleCount.intValue() - 1);
+        }
+        else if (cycleCount.intValue() == 0)
+        {
+            // Record that the revision was successfully populated.
+            m_populateResultCache.put(resource, Boolean.TRUE);
+
+            // Merge local candidate map into global candidate map.
+            if (localCandidateMap.size() > 0)
+            {
+                add(localCandidateMap);
+            }
+        }
+    }
+
+    private boolean populateFragmentOndemand(ResolveContext rc, Resource resource)
+        throws ResolutionException
+    {
+        // Create a modifiable list of the revision's requirements.
+        List<Requirement> remainingReqs =
+            new ArrayList(resource.getRequirements(null));
+        // Find the host requirement.
+        Requirement hostReq = null;
+        for (Iterator<Requirement> it = remainingReqs.iterator();
+            it.hasNext(); )
+        {
+            Requirement r = it.next();
+            if (r.getNamespace().equals(HostNamespace.HOST_NAMESPACE))
+            {
+                hostReq = r;
+                it.remove();
+                break;
+            }
+        }
+        // Get candidates hosts and keep any that have been populated.
+        List<Capability> hosts = rc.findProviders(hostReq);
+        for (Iterator<Capability> it = hosts.iterator(); it.hasNext(); )
+        {
+            Capability host = it.next();
+            if (!isPopulated(host.getResource()))
+            {
+                it.remove();
+            }
+        }
+        // If there aren't any populated hosts, then we can just
+        // return since this fragment isn't needed.
+        if (hosts.isEmpty())
+        {
+            return false;
+        }
+
+        // If there are populated host candidates, then prepopulate
+        // the result cache with the work we've done so far.
+        // Record cycle count, but start at -1 since it will
+        // be incremented again in populate().
+        Integer cycleCount = new Integer(-1);
+        // Create a local map for populating candidates first, just in case
+        // the revision is not resolvable.
+        Map<Requirement, List<Capability>> localCandidateMap =
+            new HashMap<Requirement, List<Capability>>();
+        // Add the discovered host candidates to the local candidate map.
+        localCandidateMap.put(hostReq, hosts);
+        // Add these value to the result cache so we know we are
+        // in the middle of populating candidates for the current
+        // revision.
+        m_populateResultCache.put(resource,
+            new Object[] { cycleCount, localCandidateMap, remainingReqs });
+        return true;
+    }
+
+    public void populateDynamic(
+        ResolveContext rc, Resource resource,
+        Requirement req, List<Capability> candidates) throws ResolutionException
+    {
+        // Record the revision associated with the dynamic require
+        // as a mandatory revision.
+        m_mandatoryResources.add(resource);
+
+        // Add the dynamic imports candidates.
+        add(req, candidates);
+
+        // Process the candidates, removing any candidates that
+        // cannot resolve.
+        ResolutionException rethrow = processCandidates(rc, resource, candidates);
+
+        if (candidates.isEmpty())
+        {
+            if (rethrow == null)
+            {
+                rethrow = new ResolutionException(
+                    "Dynamic import failed.", null, Collections.singleton(req));
+            }
+            throw rethrow;
+        }
+
+        m_populateResultCache.put(resource, Boolean.TRUE);
+    }
+
+    /**
+     * This method performs common processing on the given set of candidates.
+     * Specifically, it removes any candidates which cannot resolve and it
+     * synthesizes candidates for any candidates coming from any attached
+     * fragments, since fragment capabilities only appear once, but technically
+     * each host represents a unique capability.
+     * @param state the resolver state.
+     * @param revision the revision being resolved.
+     * @param candidates the candidates to process.
+     * @return a resolve exception to be re-thrown, if any, or null.
+     */
+    private ResolutionException processCandidates(
+        ResolveContext rc,
+        Resource resource,
+        List<Capability> candidates)
+    {
+        // Get satisfying candidates and populate their candidates if necessary.
+        ResolutionException rethrow = null;
+        Set<Capability> fragmentCands = null;
+        for (Iterator<Capability> itCandCap = candidates.iterator();
+            itCandCap.hasNext(); )
+        {
+            Capability candCap = itCandCap.next();
+
+            boolean isFragment = Util.isFragment(candCap.getResource());
+
+            // If the capability is from a fragment, then record it
+            // because we have to insert associated host capabilities
+            // if the fragment is already attached to any hosts.
+            if (isFragment)
+            {
+                if (fragmentCands == null)
+                {
+                    fragmentCands = new HashSet<Capability>();
+                }
+                fragmentCands.add(candCap);
+            }
+
+            // If the candidate revision is a fragment, then always attempt
+            // to populate candidates for its dependency, since it must be
+            // attached to a host to be used. Otherwise, if the candidate
+            // revision is not already resolved and is not the current version
+            // we are trying to populate, then populate the candidates for
+            // its dependencies as well.
+            // NOTE: Technically, we don't have to check to see if the
+            // candidate revision is equal to the current revision, but this
+            // saves us from recursing and also simplifies exceptions messages
+            // since we effectively chain exception messages for each level
+            // of recursion; thus, any avoided recursion results in fewer
+            // exceptions to chain when an error does occur.
+            if ((isFragment || !rc.getWirings().containsKey(candCap.getResource()))
+                && !candCap.getResource().equals(resource))
+            {
+                try
+                {
+                    populateResource(rc, candCap.getResource());
+                }
+                catch (ResolutionException ex)
+                {
+                    if (rethrow == null)
+                    {
+                        rethrow = ex;
+                    }
+                    // Remove the candidate since we weren't able to
+                    // populate its candidates.
+                    itCandCap.remove();
+                }
+            }
+        }
+
+        // If any of the candidates for the requirement were from a fragment,
+        // then also insert synthesized hosted capabilities for any other host
+        // to which the fragment is attached since they are all effectively
+        // unique capabilities.
+        if (fragmentCands != null)
+        {
+            for (Capability fragCand : fragmentCands)
+            {
+                // Only necessary for resolved fragments.
+                Wiring wiring = rc.getWirings().get(fragCand.getResource());
+                if (wiring != null)
+                {
+                    // Fragments only have host wire, so each wire represents
+                    // an attached host.
+                    for (Wire wire : wiring.getRequiredResourceWires(null))
+                    {
+                        // If the capability is a package, then make sure the
+                        // host actually provides it in its resolved capabilities,
+                        // since it may be a substitutable export.
+                        if (!fragCand.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE)
+                            || rc.getWirings().get(wire.getProvider())
+                                .getResourceCapabilities(null).contains(fragCand))
+                        {
+                            // Note that we can just add this as a candidate
+                            // directly, since we know it is already resolved.
+                            // NOTE: We are synthesizing a hosted capability here,
+                            // but we are not using a ShadowList like we do when
+                            // we synthesizing capabilities for unresolved hosts.
+                            // It is not necessary to use the ShadowList here since
+                            // the host is resolved, because in that case we can
+                            // calculate the proper package space by traversing
+                            // the wiring. In the unresolved case, this isn't possible
+                            // so we need to use the ShadowList so we can keep
+                            // a reference to a synthesized resource with attached
+                            // fragments so we can correctly calculate its package
+                            // space.
+                            rc.insertHostedCapability(
+                                candidates,
+                                new WrappedCapability(
+                                    wire.getCapability().getResource(),
+                                    fragCand));
+                        }
+                    }
+                }
+            }
+        }
+
+        return rethrow;
+    }
+
+    public boolean isPopulated(Resource resource)
+    {
+        Object value = m_populateResultCache.get(resource);
+        return ((value != null) && (value instanceof Boolean));
+    }
+
+    public ResolutionException getResolveException(Resource resource)
+    {
+        Object value = m_populateResultCache.get(resource);
+        return ((value != null) && (value instanceof ResolutionException))
+            ? (ResolutionException) value : null;
+    }
+
+    /**
+     * Adds a requirement and its matching candidates to the internal data
+     * structure. This method assumes it owns the data being passed in and
+     * does not make a copy. It takes the data and processes, such as calculating
+     * which requirements depend on which capabilities and recording any fragments
+     * it finds for future merging.
+     * @param req the requirement to add.
+     * @param candidates the candidates matching the requirement.
+    **/
+    private void add(Requirement req, List<Capability> candidates)
+    {
+        if (req.getNamespace().equals(HostNamespace.HOST_NAMESPACE))
+        {
+            m_fragmentsPresent = true;
+        }
+
+        // Record the candidates.
+        m_candidateMap.put(req, candidates);
+    }
+
+    /**
+     * Adds requirements and candidates in bulk. The outer map is not retained
+     * by this method, but the inner data structures are, so they should not
+     * be further modified by the caller.
+     * @param candidates the bulk requirements and candidates to add.
+    **/
+    private void add(Map<Requirement, List<Capability>> candidates)
+    {
+        for (Entry<Requirement, List<Capability>> entry : candidates.entrySet())
+        {
+            add(entry.getKey(), entry.getValue());
+        }
+    }
+
+    /**
+     * Returns the wrapped module associated with the given module. If the module
+     * was not wrapped, then the module itself is returned. This is really only
+     * needed to determine if the root modules of the resolve have been wrapped.
+     * @param m the module whose wrapper is desired.
+     * @return the wrapper module or the module itself if it was not wrapped.
+    **/
+    public Resource getWrappedHost(Resource r)
+    {
+        Resource wrapped = m_allWrappedHosts.get(r);
+        return (wrapped == null) ? r : wrapped;
+    }
+
+    /**
+     * Gets the candidates associated with a given requirement.
+     * @param req the requirement whose candidates are desired.
+     * @return the matching candidates or null.
+    **/
+    public List<Capability> getCandidates(Requirement req)
+    {
+        return m_candidateMap.get(req);
+    }
+
+    /**
+     * Merges fragments into their hosts. It does this by wrapping all host
+     * modules and attaching their selected fragments, removing all unselected
+     * fragment modules, and replacing all occurrences of the original fragments
+     * in the internal data structures with the wrapped host modules instead.
+     * Thus, fragment capabilities and requirements are merged into the appropriate
+     * host and the candidates for the fragment now become candidates for the host.
+     * Likewise, any module depending on a fragment now depend on the host. Note
+     * that this process is sort of like multiplication, since one fragment that
+     * can attach to two hosts effectively gets multiplied across the two hosts.
+     * So, any modules being satisfied by the fragment will end up having the
+     * two hosts as potential candidates, rather than the single fragment.
+     * @param existingSingletons existing resolved singletons.
+     * @throws ResolveException if the removal of any unselected fragments result
+     *         in the root module being unable to resolve.
+    **/
+    public void prepare(ResolveContext rc) throws ResolutionException
+    {
+        // Maps a host capability to a map containing its potential fragments;
+        // the fragment map maps a fragment symbolic name to a map that maps
+        // a version to a list of fragments requirements matching that symbolic
+        // name and version.
+        Map<Capability, Map<String, Map<Version, List<Requirement>>>>
+            hostFragments = Collections.EMPTY_MAP;
+        if (m_fragmentsPresent)
+        {
+            hostFragments = populateDependents();
+        }
+
+        // This method performs the following steps:
+        // 1. Select the fragments to attach to a given host.
+        // 2. Wrap hosts and attach fragments.
+        // 3. Remove any unselected fragments. This is necessary because
+        //    other revisions may depend on the capabilities of unselected
+        //    fragments, so we need to remove the unselected fragments and
+        //    any revisions that depends on them, which could ultimately cause
+        //    the entire resolve to fail.
+        // 4. Replace all fragments with any host it was merged into
+        //    (effectively multiplying it).
+        //    * This includes setting candidates for attached fragment
+        //      requirements as well as replacing fragment capabilities
+        //      with host's attached fragment capabilities.
+
+        // Steps 1 and 2
+        List<WrappedResource> hostResources = new ArrayList<WrappedResource>();
+        List<Resource> unselectedFragments = new ArrayList<Resource>();
+        for (Entry<Capability, Map<String, Map<Version, List<Requirement>>>>
+            hostEntry : hostFragments.entrySet())
+        {
+            // Step 1
+            Capability hostCap = hostEntry.getKey();
+            Map<String, Map<Version, List<Requirement>>> fragments
+                = hostEntry.getValue();
+            List<Resource> selectedFragments = new ArrayList<Resource>();
+            for (Entry<String, Map<Version, List<Requirement>>> fragEntry
+                : fragments.entrySet())
+            {
+                boolean isFirst = true;
+                for (Entry<Version, List<Requirement>> versionEntry
+                    : fragEntry.getValue().entrySet())
+                {
+                    for (Requirement hostReq : versionEntry.getValue())
+                    {
+                        // Selecting the first fragment in each entry, which
+                        // is equivalent to selecting the highest version of
+                        // each fragment with a given symbolic name.
+                        if (isFirst)
+                        {
+                            selectedFragments.add(hostReq.getResource());
+                            isFirst = false;
+                        }
+                        // For any fragment that wasn't selected, remove the
+                        // current host as a potential host for it and remove it
+                        // as a dependent on the host. If there are no more
+                        // potential hosts for the fragment, then mark it as
+                        // unselected for later removal.
+                        else
+                        {
+                            m_dependentMap.get(hostCap).remove(hostReq);
+                            List<Capability> hosts = m_candidateMap.get(hostReq);
+                            hosts.remove(hostCap);
+                            if (hosts.isEmpty())
+                            {
+                                unselectedFragments.add(hostReq.getResource());
+                            }
+                        }
+                    }
+                }
+            }
+
+            // Step 2
+            WrappedResource wrappedHost =
+                new WrappedResource(hostCap.getResource(), selectedFragments);
+            hostResources.add(wrappedHost);
+            m_allWrappedHosts.put(hostCap.getResource(), wrappedHost);
+        }
+
+        // Step 3
+        for (Resource fragment : unselectedFragments)
+        {
+            removeResource(fragment,
+                new ResolutionException(
+                    "Fragment was not selected for attachment: " + fragment));
+        }
+
+        // Step 4
+        for (WrappedResource hostResource : hostResources)
+        {
+            // Replaces capabilities from fragments with the capabilities
+            // from the merged host.
+            for (Capability c : hostResource.getCapabilities(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(HostNamespace.HOST_NAMESPACE))
+                {
+                    Capability origCap = ((HostedCapability) c).getDeclaredCapability();
+                    // Note that you might think we could remove the original cap
+                    // from the dependent map, but you can't since it may come from
+                    // a fragment that is attached to multiple hosts, so each host
+                    // will need to make their own copy.
+                    Set<Requirement> dependents = m_dependentMap.get(origCap);
+                    if (dependents != null)
+                    {
+                        dependents = new HashSet<Requirement>(dependents);
+                        m_dependentMap.put(c, dependents);
+                        for (Requirement r : dependents)
+                        {
+                            // We have synthesized hosted capabilities for all
+                            // fragments that have been attached to hosts by
+                            // wrapping the host bundle and their attached
+                            // fragments. We need to use the ResolveContext to
+                            // determine the proper priority order for hosted
+                            // capabilities since the order may depend on the
+                            // declaring host/fragment combination. However,
+                            // internally we completely wrap the host revision
+                            // and make all capabilities/requirements point back
+                            // to the wrapped host not the declaring host. The
+                            // ResolveContext expects HostedCapabilities to point
+                            // to the declaring revision, so we need two separate
+                            // candidate lists: one for the ResolveContext with
+                            // HostedCapabilities pointing back to the declaring
+                            // host and one for the resolver with HostedCapabilities
+                            // pointing back to the wrapped host. We ask the
+                            // ResolveContext to insert its appropriate HostedCapability
+                            // into its list, then we mirror the insert into a
+                            // shadow list with the resolver's HostedCapability.
+                            // We only need to ask the ResolveContext to find
+                            // the insert position for fragment caps since these
+                            // were synthesized and we don't know their priority.
+                            // However, in the resolver's candidate list we need
+                            // to replace all caps with the wrapped caps, no
+                            // matter if they come from the host or fragment,
+                            // since we are completing replacing the declaring
+                            // host and fragments with the wrapped host.
+                            List<Capability> cands = m_candidateMap.get(r);
+                            if (!(cands instanceof ShadowList))
+                            {
+                                ShadowList<Capability> shadow =
+                                    new ShadowList<Capability>(cands);
+                                m_candidateMap.put(r, shadow);
+                                cands = shadow;
+                            }
+
+                            // If the original capability is from a fragment, then
+                            // ask the ResolveContext to insert it and update the
+                            // shadow copy of the list accordingly.
+                            if (!origCap.getResource().equals(hostResource.getDeclaredResource()))
+                            {
+                                List<Capability> original = ((ShadowList) cands).getOriginal();
+                                int removeIdx = original.indexOf(origCap);
+                                original.remove(removeIdx);
+                                int insertIdx = rc.insertHostedCapability(
+                                    original,
+                                    new SimpleHostedCapability(
+                                        hostResource.getDeclaredResource(),
+                                        origCap));
+                                cands.remove(removeIdx);
+                                cands.add(insertIdx, c);
+                            }
+                            // If the original capability is from the host, then
+                            // we just need to replace it in the shadow list.
+                            else
+                            {
+                                int idx = cands.indexOf(origCap);
+                                cands.set(idx, c);
+                            }
+                        }
+                    }
+                }
+            }
+
+            // Copy candidates for fragment requirements to the host.
+            for (Requirement r : hostResource.getRequirements(null))
+            {
+                Requirement origReq = ((WrappedRequirement) r).getDeclaredRequirement();
+                List<Capability> cands = m_candidateMap.get(origReq);
+                if (cands != null)
+                {
+                    m_candidateMap.put(r, new ArrayList<Capability>(cands));
+                    for (Capability cand : cands)
+                    {
+                        Set<Requirement> dependents = m_dependentMap.get(cand);
+                        dependents.remove(origReq);
+                        dependents.add(r);
+                    }
+                }
+            }
+        }
+
+        // Lastly, verify that all mandatory revisions are still
+        // populated, since some might have become unresolved after
+        // selecting fragments/singletons.
+        for (Resource resource : m_mandatoryResources)
+        {
+            if (!isPopulated(resource))
+            {
+                throw getResolveException(resource);
+            }
+        }
+    }
+
+    // Maps a host capability to a map containing its potential fragments;
+    // the fragment map maps a fragment symbolic name to a map that maps
+    // a version to a list of fragments requirements matching that symbolic
+    // name and version.
+    private Map<Capability,
+        Map<String, Map<Version, List<Requirement>>>> populateDependents()
+    {
+        Map<Capability, Map<String, Map<Version, List<Requirement>>>>
+            hostFragments = new HashMap<Capability,
+                Map<String, Map<Version, List<Requirement>>>>();
+        for (Entry<Requirement, List<Capability>> entry : m_candidateMap.entrySet())
+        {
+            Requirement req = entry.getKey();
+            List<Capability> caps = entry.getValue();
+            for (Capability cap : caps)
+            {
+                // Record the requirement as dependent on the capability.
+                Set<Requirement> dependents = m_dependentMap.get(cap);
+                if (dependents == null)
+                {
+                    dependents = new HashSet<Requirement>();
+                    m_dependentMap.put(cap, dependents);
+                }
+                dependents.add(req);
+
+                // Keep track of hosts and associated fragments.
+                if (req.getNamespace().equals(HostNamespace.HOST_NAMESPACE))
+                {
+                    String resSymName = Util.getSymbolicName(req.getResource());
+                    Version resVersion = Util.getVersion(req.getResource());
+
+                    Map<String, Map<Version, List<Requirement>>>
+                        fragments = hostFragments.get(cap);
+                    if (fragments == null)
+                    {
+                        fragments = new HashMap<String, Map<Version, List<Requirement>>>();
+                        hostFragments.put(cap, fragments);
+                    }
+                    Map<Version, List<Requirement>> fragmentVersions = fragments.get(resSymName);
+                    if (fragmentVersions == null)
+                    {
+                        fragmentVersions =
+                            new TreeMap<Version, List<Requirement>>(Collections.reverseOrder());
+                        fragments.put(resSymName, fragmentVersions);
+                    }
+                    List<Requirement> actual = fragmentVersions.get(resVersion);
+                    if (actual == null)
+                    {
+                        actual = new ArrayList<Requirement>();
+                        fragmentVersions.put(resVersion, actual);
+                    }
+                    actual.add(req);
+                }
+            }
+        }
+
+        return hostFragments;
+    }
+
+    /**
+     * Removes a module from the internal data structures if it wasn't selected
+     * as a fragment or a singleton. This process may cause other modules to
+     * become unresolved if they depended on the module's capabilities and there
+     * is no other candidate.
+     * @param revision the module to remove.
+     * @throws ResolveException if removing the module caused the resolve to fail.
+    **/
+    private void removeResource(Resource resource, ResolutionException ex)
+        throws ResolutionException
+    {
+        // Add removal reason to result cache.
+        m_populateResultCache.put(resource, ex);
+        // Remove from dependents.
+        Set<Resource> unresolvedResources = new HashSet<Resource>();
+        remove(resource, unresolvedResources);
+        // Remove dependents that failed as a result of removing revision.
+        while (!unresolvedResources.isEmpty())
+        {
+            Iterator<Resource> it = unresolvedResources.iterator();
+            resource = it.next();
+            it.remove();
+            remove(resource, unresolvedResources);
+        }
+    }
+
+    /**
+     * Removes the specified module from the internal data structures, which
+     * involves removing its requirements and its capabilities. This may cause
+     * other modules to become unresolved as a result.
+     * @param br the module to remove.
+     * @param unresolvedRevisions a list to containing any additional modules that
+     *        that became unresolved as a result of removing this module and will
+     *        also need to be removed.
+     * @throws ResolveException if removing the module caused the resolve to fail.
+    **/
+    private void remove(Resource resource, Set<Resource> unresolvedResources)
+        throws ResolutionException
+    {
+        for (Requirement r : resource.getRequirements(null))
+        {
+            remove(r);
+        }
+
+        for (Capability c : resource.getCapabilities(null))
+        {
+            remove(c, unresolvedResources);
+        }
+    }
+
+    /**
+     * Removes a requirement from the internal data structures.
+     * @param req the requirement to remove.
+    **/
+    private void remove(Requirement req)
+    {
+        boolean isFragment = req.getNamespace().equals(HostNamespace.HOST_NAMESPACE);
+
+        List<Capability> candidates = m_candidateMap.remove(req);
+        if (candidates != null)
+        {
+            for (Capability cap : candidates)
+            {
+                Set<Requirement> dependents = m_dependentMap.get(cap);
+                if (dependents != null)
+                {
+                    dependents.remove(req);
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes a capability from the internal data structures. This may cause
+     * other modules to become unresolved as a result.
+     * @param c the capability to remove.
+     * @param unresolvedRevisions a list to containing any additional modules that
+     *        that became unresolved as a result of removing this module and will
+     *        also need to be removed.
+     * @throws ResolveException if removing the module caused the resolve to fail.
+    **/
+    private void remove(Capability c, Set<Resource> unresolvedResources)
+        throws ResolutionException
+    {
+        Set<Requirement> dependents = m_dependentMap.remove(c);
+        if (dependents != null)
+        {
+            for (Requirement r : dependents)
+            {
+                List<Capability> candidates = m_candidateMap.get(r);
+                candidates.remove(c);
+                if (candidates.isEmpty())
+                {
+                    m_candidateMap.remove(r);
+                    if (!Util.isOptional(r))
+                    {
+                        String msg = "Unable to resolve " + r.getResource()
+                            + ": missing requirement " + r;
+                        m_populateResultCache.put(
+                            r.getResource(),
+                            new ResolutionException(msg, null, Collections.singleton(r)));
+                        unresolvedResources.add(r.getResource());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates a copy of the Candidates object. This is used for creating
+     * permutations when package space conflicts are discovered.
+     * @return copy of this Candidates object.
+    **/
+    public Candidates copy()
+    {
+        Map<Capability, Set<Requirement>> dependentMap =
+            new HashMap<Capability, Set<Requirement>>();
+        for (Entry<Capability, Set<Requirement>> entry : m_dependentMap.entrySet())
+        {
+            Set<Requirement> dependents = new HashSet<Requirement>(entry.getValue());
+            dependentMap.put(entry.getKey(), dependents);
+        }
+
+        Map<Requirement, List<Capability>> candidateMap =
+            new HashMap<Requirement, List<Capability>>();
+        for (Entry<Requirement, List<Capability>> entry
+            : m_candidateMap.entrySet())
+        {
+            List<Capability> candidates =
+                new ArrayList<Capability>(entry.getValue());
+            candidateMap.put(entry.getKey(), candidates);
+        }
+
+        return new Candidates(
+            m_mandatoryResources, dependentMap, candidateMap,
+            m_allWrappedHosts, m_populateResultCache, m_fragmentsPresent);
+    }
+
+    public void dump(ResolveContext rc)
+    {
+        // Create set of all revisions from requirements.
+        Set<Resource> resources = new HashSet<Resource>();
+        for (Entry<Requirement, List<Capability>> entry
+            : m_candidateMap.entrySet())
+        {
+            resources.add(entry.getKey().getResource());
+        }
+        // Now dump the revisions.
+        System.out.println("=== BEGIN CANDIDATE MAP ===");
+        for (Resource resource : resources)
+        {
+            Wiring wiring = rc.getWirings().get(resource);
+            System.out.println("  " + resource
+                 + " (" + ((wiring != null) ? "RESOLVED)" : "UNRESOLVED)"));
+            List<Requirement> reqs = (wiring != null)
+                ? wiring.getResourceRequirements(null)
+                : resource.getRequirements(null);
+            for (Requirement req : reqs)
+            {
+                List<Capability> candidates = m_candidateMap.get(req);
+                if ((candidates != null) && (candidates.size() > 0))
+                {
+                    System.out.println("    " + req + ": " + candidates);
+                }
+            }
+            reqs = (wiring != null)
+                ? Util.getDynamicRequirements(wiring.getResourceRequirements(null))
+                : Util.getDynamicRequirements(resource.getRequirements(null));
+            for (Requirement req : reqs)
+            {
+                List<Capability> candidates = m_candidateMap.get(req);
+                if ((candidates != null) && (candidates.size() > 0))
+                {
+                    System.out.println("    " + req + ": " + candidates);
+                }
+            }
+        }
+        System.out.println("=== END CANDIDATE MAP ===");
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/Logger.java b/resolver/src/main/java/org/apache/felix/resolver/Logger.java
new file mode 100644
index 0000000..9eaa212
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/Logger.java
@@ -0,0 +1,122 @@
+/*
+ * 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.resolver;
+
+/**
+ * <p>
+ * This class mimics the standard OSGi <tt>LogService</tt> interface. An
+ * instance of this class is used by the framework for all logging. By default
+ * this class logs messages to standard out. The log level can be set to
+ * control the amount of logging performed, where a higher number results in
+ * more logging. A log level of zero turns off logging completely.
+ * </p>
+ * <p>
+ * The log levels match those specified in the OSGi Log Service (i.e., 1 = error,
+ * 2 = warning, 3 = information, and 4 = debug). The default value is 1.
+ * </p>
+ * <p>
+ * This class also uses the System Bundle's context to track log services
+ * and will use the highest ranking log service, if present, as a back end
+ * instead of printing to standard out. The class uses reflection to invoking
+ * the log service's method to avoid a dependency on the log interface.
+ * </p>
+**/
+public class Logger
+{
+    public static final int LOG_ERROR = 1;
+    public static final int LOG_WARNING = 2;
+    public static final int LOG_INFO = 3;
+    public static final int LOG_DEBUG = 4;
+
+    private int m_logLevel = 1;
+
+    private final static int LOGGER_OBJECT_IDX = 0;
+    private final static int LOGGER_METHOD_IDX = 1;
+    private Object[] m_logger = null;
+
+    public Logger(int i)
+    {
+        m_logLevel = i;
+    }
+
+    public final synchronized void setLogLevel(int i)
+    {
+        m_logLevel = i;
+    }
+
+    public final synchronized int getLogLevel()
+    {
+        return m_logLevel;
+    }
+
+    public final void log(int level, String msg)
+    {
+        _log(level, msg, null);
+    }
+
+    public final void log(int level, String msg, Throwable throwable)
+    {
+        _log(level, msg, throwable);
+    }
+
+    protected void doLog(int level, String msg, Throwable throwable)
+    {
+        String s = "";
+        s = s + msg;
+        if (throwable != null)
+        {
+            s = s + " (" + throwable + ")";
+        }
+        switch (level)
+        {
+            case LOG_DEBUG:
+                System.out.println("DEBUG: " + s);
+                break;
+            case LOG_ERROR:
+                System.out.println("ERROR: " + s);
+                if (throwable != null)
+                {
+                    throwable.printStackTrace();
+                }
+                break;
+            case LOG_INFO:
+                System.out.println("INFO: " + s);
+                break;
+            case LOG_WARNING:
+                System.out.println("WARNING: " + s);
+                break;
+            default:
+                System.out.println("UNKNOWN[" + level + "]: " + s);
+        }
+    }
+
+    private void _log(
+        int level,
+        String msg, Throwable throwable)
+    {
+        // Save our own copy just in case it changes. We could try to do
+        // more conservative locking here, but let's be optimistic.
+        Object[] logger = m_logger;
+
+        if (m_logLevel >= level)
+        {
+            doLog(level, msg, throwable);
+        }
+    }
+}
diff --git a/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
new file mode 100644
index 0000000..e6d3311
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/ResolverImpl.java
@@ -0,0 +1,1830 @@
+/*
+ * 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.resolver;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.StringTokenizer;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.HostNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+import org.osgi.resource.Wiring;
+import org.osgi.service.resolver.HostedCapability;
+import org.osgi.service.resolver.ResolutionException;
+import org.osgi.service.resolver.ResolveContext;
+import org.osgi.service.resolver.Resolver;
+
+public class ResolverImpl implements Resolver
+{
+    private final Logger m_logger;
+
+    // Holds candidate permutations based on permutating "uses" chains.
+    // These permutations are given higher priority.
+    private final List<Candidates> m_usesPermutations = new ArrayList<Candidates>();
+    // Holds candidate permutations based on permutating requirement candidates.
+    // These permutations represent backtracking on previous decisions.
+    private final List<Candidates> m_importPermutations = new ArrayList<Candidates>();
+
+    public ResolverImpl(Logger logger)
+    {
+        m_logger = logger;
+    }
+
+    public Map<Resource, List<Wire>> resolve(ResolveContext rc) throws ResolutionException
+    {
+        Map<Resource, List<Wire>> wireMap =
+            new HashMap<Resource, List<Wire>>();
+        Map<Resource, Packages> resourcePkgMap =
+            new HashMap<Resource, Packages>();
+
+        Collection<Resource> mandatoryResources = rc.getMandatoryResources();
+        Collection<Resource> optionalResources = rc.getOptionalResources();
+// TODO: RFC-112 - Need impl-specific type.
+//        Collection<Resource> ondemandFragments = (rc instanceof ResolveContextImpl)
+//            ? ((ResolveContextImpl) rc).getOndemandResources() : Collections.EMPTY_LIST;
+        Collection<Resource> ondemandFragments =  Collections.EMPTY_LIST;
+
+        boolean retry;
+        do
+        {
+            retry = false;
+
+            try
+            {
+                // Create object to hold all candidates.
+                Candidates allCandidates = new Candidates();
+
+                // Populate mandatory resources; since these are mandatory
+                // resources, failure throws a resolve exception.
+                for (Iterator<Resource> it = mandatoryResources.iterator();
+                    it.hasNext(); )
+                {
+                    Resource resource = it.next();
+                    if (Util.isFragment(resource) || (rc.getWirings().get(resource) == null))
+                    {
+                        allCandidates.populate(rc, resource, Candidates.MANDATORY);
+                    }
+                    else
+                    {
+                        it.remove();
+                    }
+                }
+
+                // Populate optional resources; since these are optional
+                // resources, failure does not throw a resolve exception.
+                for (Resource resource : optionalResources)
+                {
+                    boolean isFragment = Util.isFragment(resource);
+                    if (isFragment || (rc.getWirings().get(resource) == null))
+                    {
+                        allCandidates.populate(rc, resource, Candidates.OPTIONAL);
+                    }
+                }
+
+                // Populate ondemand fragments; since these are optional
+                // resources, failure does not throw a resolve exception.
+                for (Resource resource : ondemandFragments)
+                {
+                    boolean isFragment = Util.isFragment(resource);
+                    if (isFragment)
+                    {
+                        allCandidates.populate(rc, resource, Candidates.ON_DEMAND);
+                    }
+                }
+
+                // Merge any fragments into hosts.
+                allCandidates.prepare(rc);
+
+                // Create a combined list of populated resources; for
+                // optional resources. We do not need to consider ondemand
+                // fragments, since they will only be pulled in if their
+                // host is already present.
+                Set<Resource> allResources =
+                    new HashSet<Resource>(mandatoryResources);
+                for (Resource resource : optionalResources)
+                {
+                    if (allCandidates.isPopulated(resource))
+                    {
+                        allResources.add(resource);
+                    }
+                }
+
+                // Record the initial candidate permutation.
+                m_usesPermutations.add(allCandidates);
+
+                ResolutionException rethrow = null;
+
+                // If a populated resource is a fragment, then its host
+                // must ultimately be verified, so store its host requirement
+                // to use for package space calculation.
+                Map<Resource, List<Requirement>> hostReqs =
+                    new HashMap<Resource, List<Requirement>>();
+                for (Resource resource : allResources)
+                {
+                    if (Util.isFragment(resource))
+                    {
+                        hostReqs.put(
+                            resource,
+                            resource.getRequirements(HostNamespace.HOST_NAMESPACE));
+                    }
+                }
+
+                do
+                {
+                    rethrow = null;
+
+                    resourcePkgMap.clear();
+                    m_packageSourcesCache.clear();
+
+                    allCandidates = (m_usesPermutations.size() > 0)
+                        ? m_usesPermutations.remove(0)
+                        : m_importPermutations.remove(0);
+//allCandidates.dump();
+
+                    for (Resource resource : allResources)
+                    {
+                        Resource target = resource;
+
+                        // If we are resolving a fragment, then get its
+                        // host candidate and verify it instead.
+                        List<Requirement> hostReq = hostReqs.get(resource);
+                        if (hostReq != null)
+                        {
+                            target = allCandidates.getCandidates(hostReq.get(0))
+                                .iterator().next().getResource();
+                        }
+
+                        calculatePackageSpaces(
+                            rc, allCandidates.getWrappedHost(target), allCandidates,
+                            resourcePkgMap, new HashMap(), new HashSet());
+//System.out.println("+++ PACKAGE SPACES START +++");
+//dumpResourcePkgMap(resourcePkgMap);
+//System.out.println("+++ PACKAGE SPACES END +++");
+
+                        try
+                        {
+                            checkPackageSpaceConsistency(
+                                rc, false, allCandidates.getWrappedHost(target),
+                                allCandidates, resourcePkgMap, new HashMap());
+                        }
+                        catch (ResolutionException 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 resource 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)
+                {
+                    Collection<Requirement> exReqs = rethrow.getUnresolvedRequirements();
+                    Requirement faultyReq = ((exReqs == null) || (exReqs.isEmpty()))
+                        ? null : exReqs.iterator().next();
+                    Resource faultyResource = (faultyReq == null)
+                        ? null : getDeclaredResource(faultyReq.getResource());
+                    // If the faulty requirement is wrapped, then it may
+                    // be from a fragment, so consider the fragment faulty
+                    // instead of the host.
+                    if (faultyReq instanceof WrappedRequirement)
+                    {
+                        faultyResource =
+                            ((WrappedRequirement) faultyReq)
+                                .getDeclaredRequirement().getResource();
+                    }
+                    // Try to ignore the faulty resource if it is not mandatory.
+                    if (optionalResources.remove(faultyResource))
+                    {
+                        retry = true;
+                    }
+                    else if (ondemandFragments.remove(faultyResource))
+                    {
+                        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 (Resource resource : allResources)
+                    {
+                        Resource target = resource;
+
+                        // If we are resolving a fragment, then we
+                        // actually want to populate its host's wires.
+                        List<Requirement> hostReq = hostReqs.get(resource);
+                        if (hostReq != null)
+                        {
+                            target = allCandidates.getCandidates(hostReq.get(0))
+                                .iterator().next().getResource();
+                        }
+
+                        if (allCandidates.isPopulated(target))
+                        {
+                            wireMap =
+                                populateWireMap(
+                                    rc, allCandidates.getWrappedHost(target),
+                                    resourcePkgMap, wireMap, allCandidates);
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                // Always clear the state.
+                m_usesPermutations.clear();
+                m_importPermutations.clear();
+            }
+        }
+        while (retry);
+
+        return wireMap;
+    }
+
+/*
+ TODO: RFC-112 - Modify dynamic import handling to be like obr-resolver prototype.
+    public Map<Resource, List<Wire>> resolve(
+        ResolveContext rc, Resouce resource, String pkgName)
+    {
+        // We can only create a dynamic import if the following
+        // conditions are met:
+        // 1. The specified resource is resolved.
+        // 2. The package in question is not already imported.
+        // 3. The package in question is not accessible via require-bundle.
+        // 4. The package in question is not exported by the resource.
+        // 5. The package in question matches a dynamic import of the resource.
+        // The following call checks all of these conditions and returns
+        // the associated dynamic import and matching capabilities.
+        Candidates allCandidates =
+            getDynamicImportCandidates(rc, resource, pkgName);
+        if (allCandidates != null)
+        {
+            Collection<Resource> ondemandFragments = (rc instanceof ResolveContextImpl)
+                ? ((ResolveContextImpl) rc).getOndemandResources() : Collections.EMPTY_LIST;
+
+            Map<Resource, List<ResolverWire>> wireMap =
+                new HashMap<Resource, List<ResolverWire>>();
+            Map<Resource, Packages> resourcePkgMap =
+                new HashMap<Resource, Packages>();
+
+            boolean retry;
+            do
+            {
+                retry = false;
+
+                try
+                {
+                    // Try to populate optional fragments.
+                    for (Resource r : ondemandFragments)
+                    {
+                        if (Util.isFragment(r))
+                        {
+                            allCandidates.populate(rc, r, Candidates.ON_DEMAND);
+                        }
+                    }
+
+                    // Merge any fragments into hosts.
+                    allCandidates.prepare(rc);
+
+                    // Record the initial candidate permutation.
+                    m_usesPermutations.add(allCandidates);
+
+                    ResolveException rethrow = null;
+
+                    do
+                    {
+                        rethrow = null;
+
+                        resourcePkgMap.clear();
+                        m_packageSourcesCache.clear();
+
+                        allCandidates = (m_usesPermutations.size() > 0)
+                            ? m_usesPermutations.remove(0)
+                            : m_importPermutations.remove(0);
+//allCandidates.dump();
+
+                        // For a dynamic import, the instigating resource
+                        // will never be a fragment since fragments never
+                        // execute code, so we don't need to check for
+                        // this case like we do for a normal resolve.
+
+                        calculatePackageSpaces(
+                            allCandidates.getWrappedHost(resource), allCandidates, resourcePkgMap,
+                            new HashMap(), new HashSet());
+//System.out.println("+++ PACKAGE SPACES START +++");
+//dumpResourcePkgMap(resourcePkgMap);
+//System.out.println("+++ PACKAGE SPACES END +++");
+
+                        try
+                        {
+                            checkPackageSpaceConsistency(
+                                false, allCandidates.getWrappedHost(resource),
+                                allCandidates, resourcePkgMap, 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 resource is to blame (typically a fragment).
+                    // If so, then remove the optionally resolved resource and try
+                    // again; otherwise, rethrow the resolve exception.
+                    if (rethrow != null)
+                    {
+                        Resource faultyResource =
+                            getDeclaredResource(rethrow.getResource()));
+                        if (rethrow.getRequirement() instanceof WrappedRequirement)
+                        {
+                            faultyResource =
+                                ((WrappedRequirement) rethrow.getRequirement())
+                                    .getOriginalRequirement().getResource());
+                        }
+                        if (ondemandFragments.remove(faultyResource))
+                        {
+                            retry = true;
+                        }
+                        else
+                        {
+                            throw rethrow;
+                        }
+                    }
+                    // If there is no exception to rethrow, then this was a clean
+                    // resolve, so populate the wire map.
+                    else
+                    {
+                        wireMap = populateDynamicWireMap(
+                            resource, pkgName, resourcePkgMap, wireMap, allCandidates);
+                        return wireMap;
+                    }
+                }
+                finally
+                {
+                    // Always clear the state.
+                    m_usesPermutations.clear();
+                    m_importPermutations.clear();
+                }
+            }
+            while (retry);
+        }
+
+        return null;
+    }
+
+    private static Candidates getDynamicImportCandidates(
+        ResolveContext rc, Resource resource, String pkgName)
+    {
+        // Unresolved resources cannot dynamically import, nor can the default
+        // package be dynamically imported.
+        if ((resource.getWiring() == null) || pkgName.length() == 0)
+        {
+            return null;
+        }
+
+        // If the resource doesn't have dynamic imports, then just return
+        // immediately.
+        List<Requirement> dynamics =
+            Util.getDynamicRequirements(resource.getWiring().getRequirements(null));
+        if ((dynamics == null) || dynamics.isEmpty())
+        {
+            return null;
+        }
+
+        // If the resource exports this package, then we cannot
+        // attempt to dynamically import it.
+        for (Capability cap : resource.getWiring().getCapabilities(null))
+        {
+            if (cap.getNamespace().equals(Resource.PACKAGE_NAMESPACE)
+                && cap.getAttributes().get(Resource.PACKAGE_NAMESPACE).equals(pkgName))
+            {
+                return null;
+            }
+        }
+
+        // If this resource already imports or requires this package, then
+        // we cannot dynamically import it.
+        if (((WiringImpl) resource.getWiring()).hasPackageSource(pkgName))
+        {
+            return null;
+        }
+
+        // Determine if any providers of the package exist.
+        Map<String, Object> attrs = Collections.singletonMap(
+            PackageNamespace.PACKAGE_NAMESPACE, (Object) pkgName);
+        RequirementImpl req = new RequirementImpl(
+            resource,
+            PackageNamespace.PACKAGE_NAMESPACE,
+            Collections.EMPTY_MAP,
+            attrs);
+        List<Capability> candidates = rc.findProviders(req, false);
+
+        // Try to find a dynamic requirement that matches the capabilities.
+        RequirementImpl dynReq = null;
+        for (int dynIdx = 0;
+            (candidates.size() > 0) && (dynReq == null) && (dynIdx < dynamics.size());
+            dynIdx++)
+        {
+            for (Iterator<Capability> itCand = candidates.iterator();
+                (dynReq == null) && itCand.hasNext(); )
+            {
+                Capability cap = itCand.next();
+                if (CapabilitySet.matches(
+                    (CapabilityImpl) cap,
+                    ((RequirementImpl) dynamics.get(dynIdx)).getFilter()))
+                {
+                    dynReq = (RequirementImpl) dynamics.get(dynIdx);
+                }
+            }
+        }
+
+        // If we found a matching dynamic requirement, then filter out
+        // any candidates that do not match it.
+        if (dynReq != null)
+        {
+            for (Iterator<Capability> itCand = candidates.iterator();
+                itCand.hasNext(); )
+            {
+                Capability cap = itCand.next();
+                if (!CapabilitySet.matches(
+                    (CapabilityImpl) cap, dynReq.getFilter()))
+                {
+                    itCand.remove();
+                }
+            }
+        }
+        else
+        {
+            candidates.clear();
+        }
+
+        Candidates allCandidates = null;
+
+        if (candidates.size() > 0)
+        {
+            allCandidates = new Candidates();
+            allCandidates.populateDynamic(rc, resource, dynReq, candidates);
+        }
+
+        return allCandidates;
+    }
+*/
+    private void calculatePackageSpaces(
+        ResolveContext rc,
+        Resource resource,
+        Candidates allCandidates,
+        Map<Resource, Packages> resourcePkgMap,
+        Map<Capability, List<Resource>> usesCycleMap,
+        Set<Resource> cycle)
+    {
+        if (cycle.contains(resource))
+        {
+            return;
+        }
+        cycle.add(resource);
+
+        // Create parallel arrays for requirement and proposed candidate
+        // capability or actual capability if resource is resolved or not.
+        List<Requirement> reqs = new ArrayList();
+        List<Capability> caps = new ArrayList();
+        boolean isDynamicImporting = false;
+        Wiring wiring = rc.getWirings().get(resource);
+        if (wiring != null)
+        {
+            // Use wires to get actual requirements and satisfying capabilities.
+            for (Wire wire : wiring.getRequiredResourceWires(null))
+            {
+                // Wrap the requirement as a hosted requirement if it comes
+                // from a fragment, since we will need to know the host. We
+                // also need to wrap if the requirement is a dynamic import,
+                // since that requirement will be shared with any other
+                // matching dynamic imports.
+                Requirement r = wire.getRequirement();
+                if (!r.getResource().equals(wire.getRequirer())
+                    || ((r.getDirectives()
+                        .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE) != null)
+                    && r.getDirectives()
+                        .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE)
+                            .equals(PackageNamespace.RESOLUTION_DYNAMIC)))
+                {
+                    r = new WrappedRequirement(wire.getRequirer(), r);
+                }
+                // Wrap the capability as a hosted capability if it comes
+                // from a fragment, since we will need to know the host.
+                Capability c = wire.getCapability();
+                if (!c.getResource().equals(wire.getProvider()))
+                {
+                    c = new WrappedCapability(wire.getProvider(), c);
+                }
+                reqs.add(r);
+                caps.add(c);
+            }
+
+            // Since the resource is resolved, it could be dynamically importing,
+            // so check to see if there are candidates for any of its dynamic
+            // imports.
+            for (Requirement req
+                : Util.getDynamicRequirements(wiring.getResourceRequirements(null)))
+            {
+                // Get the candidates for the current requirement.
+                List<Capability> candCaps = allCandidates.getCandidates(req);
+                // Optional requirements may not have any candidates.
+                if (candCaps == null)
+                {
+                    continue;
+                }
+
+                // Grab first (i.e., highest priority) candidate.
+                Capability cap = candCaps.get(0);
+                reqs.add(req);
+                caps.add(cap);
+                isDynamicImporting = true;
+                // Can only dynamically import one at a time, so break
+                // out of the loop after the first.
+                break;
+            }
+        }
+        else
+        {
+            for (Requirement req : resource.getRequirements(null))
+            {
+                String resolution = req.getDirectives()
+                    .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE);
+                if ((resolution == null)
+                    || !resolution.equals(PackageNamespace.RESOLUTION_DYNAMIC))
+                {
+                    // Get the candidates for the current requirement.
+                    List<Capability> candCaps = allCandidates.getCandidates(req);
+                    // Optional requirements may not have any candidates.
+                    if (candCaps == null)
+                    {
+                        continue;
+                    }
+
+                    // Grab first (i.e., highest priority) candidate.
+                    Capability cap = candCaps.get(0);
+                    reqs.add(req);
+                    caps.add(cap);
+                }
+            }
+        }
+
+        // First, add all exported packages to the target resource's package space.
+        calculateExportedPackages(rc, resource, allCandidates, resourcePkgMap);
+        Packages resourcePkgs = resourcePkgMap.get(resource);
+
+        // Second, add all imported packages to the target resource's package space.
+        for (int i = 0; i < reqs.size(); i++)
+        {
+            Requirement req = reqs.get(i);
+            Capability cap = caps.get(i);
+            calculateExportedPackages(rc, cap.getResource(), allCandidates, resourcePkgMap);
+            mergeCandidatePackages(
+                rc, resource, req, cap, resourcePkgMap, allCandidates,
+                new HashMap<Resource, List<Capability>>());
+        }
+
+        // Third, have all candidates to calculate their package spaces.
+        for (int i = 0; i < caps.size(); i++)
+        {
+            calculatePackageSpaces(
+                rc, caps.get(i).getResource(), allCandidates, resourcePkgMap,
+                usesCycleMap, cycle);
+        }
+
+        // Fourth, if the target resource is unresolved or is dynamically importing,
+        // then add all the uses constraints implied by its imported and required
+        // packages to its package space.
+        // NOTE: We do not need to do this for resolved resources because their
+        // package space is consistent by definition and these uses constraints
+        // are only needed to verify the consistency of a resolving resource. The
+        // only exception is if a resolved resource is dynamically importing, then
+        // we need to calculate its uses constraints again to make sure the new
+        // import is consistent with the existing package space.
+        if ((wiring == null) || isDynamicImporting)
+        {
+            // Merge uses constraints from required capabilities.
+            for (int i = 0; i < reqs.size(); i++)
+            {
+                Requirement req = reqs.get(i);
+                Capability cap = caps.get(i);
+                // Ignore bundle/package requirements, since they are
+                // considered below.
+                if (!req.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE)
+                    && !req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+                {
+                    List<Requirement> blameReqs = new ArrayList<Requirement>();
+                    blameReqs.add(req);
+
+                    mergeUses(
+                        rc,
+                        resource,
+                        resourcePkgs,
+                        cap,
+                        blameReqs,
+                        resourcePkgMap,
+                        allCandidates,
+                        usesCycleMap);
+                }
+            }
+            // Merge uses constraints from imported packages.
+            for (Entry<String, List<Blame>> entry : resourcePkgs.m_importedPkgs.entrySet())
+            {
+                for (Blame blame : entry.getValue())
+                {
+                    // Ignore resources that import from themselves.
+                    if (!blame.m_cap.getResource().equals(resource))
+                    {
+                        List<Requirement> blameReqs = new ArrayList();
+                        blameReqs.add(blame.m_reqs.get(0));
+
+                        mergeUses(
+                            rc,
+                            resource,
+                            resourcePkgs,
+                            blame.m_cap,
+                            blameReqs,
+                            resourcePkgMap,
+                            allCandidates,
+                            usesCycleMap);
+                    }
+                }
+            }
+            // Merge uses constraints from required bundles.
+            for (Entry<String, List<Blame>> entry : resourcePkgs.m_requiredPkgs.entrySet())
+            {
+                for (Blame blame : entry.getValue())
+                {
+                    List<Requirement> blameReqs = new ArrayList();
+                    blameReqs.add(blame.m_reqs.get(0));
+
+                    mergeUses(
+                        rc,
+                        resource,
+                        resourcePkgs,
+                        blame.m_cap,
+                        blameReqs,
+                        resourcePkgMap,
+                        allCandidates,
+                        usesCycleMap);
+                }
+            }
+        }
+    }
+
+    private void mergeCandidatePackages(
+        ResolveContext rc, Resource current, Requirement currentReq,
+        Capability candCap, Map<Resource, Packages> resourcePkgMap,
+        Candidates allCandidates, Map<Resource, List<Capability>> cycles)
+    {
+        List<Capability> cycleCaps = cycles.get(current);
+        if (cycleCaps == null)
+        {
+            cycleCaps = new ArrayList<Capability>();
+            cycles.put(current, cycleCaps);
+        }
+        if (cycleCaps.contains(candCap))
+        {
+            return;
+        }
+        cycleCaps.add(candCap);
+
+        if (candCap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+        {
+            mergeCandidatePackage(
+                current, false, currentReq, candCap, resourcePkgMap);
+        }
+        else if (candCap.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
+        {
+// TODO: FELIX3 - THIS NEXT LINE IS A HACK. IMPROVE HOW/WHEN WE CALCULATE EXPORTS.
+            calculateExportedPackages(
+                rc, candCap.getResource(), allCandidates, resourcePkgMap);
+
+            // Get the candidate's package space to determine which packages
+            // will be visible to the current resource.
+            Packages candPkgs = resourcePkgMap.get(candCap.getResource());
+
+            // We have to merge all exported packages from the candidate,
+            // since the current resource requires it.
+            for (Entry<String, Blame> entry : candPkgs.m_exportedPkgs.entrySet())
+            {
+                mergeCandidatePackage(
+                    current,
+                    true,
+                    currentReq,
+                    entry.getValue().m_cap,
+                    resourcePkgMap);
+            }
+
+            // If the candidate requires any other bundles with reexport visibility,
+            // then we also need to merge their packages too.
+            Wiring candWiring = rc.getWirings().get(candCap.getResource());
+            if (candWiring != null)
+            {
+                for (Wire w : candWiring.getRequiredResourceWires(null))
+                {
+                    if (w.getRequirement().getNamespace()
+                        .equals(BundleNamespace.BUNDLE_NAMESPACE))
+                    {
+                        String value = w.getRequirement()
+                            .getDirectives()
+                                .get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
+                        if ((value != null)
+                            && value.equals(BundleNamespace.VISIBILITY_REEXPORT))
+                        {
+                            mergeCandidatePackages(
+                                rc,
+                                current,
+                                currentReq,
+                                w.getCapability(),
+                                resourcePkgMap,
+                                allCandidates,
+                                cycles);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                for (Requirement req : candCap.getResource().getRequirements(null))
+                {
+                    if (req.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
+                    {
+                        String value =
+                            req.getDirectives()
+                                .get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE);
+                        if ((value != null)
+                            && value.equals(BundleNamespace.VISIBILITY_REEXPORT)
+                            && (allCandidates.getCandidates(req) != null))
+                        {
+                            mergeCandidatePackages(
+                                rc,
+                                current,
+                                currentReq,
+                                allCandidates.getCandidates(req).iterator().next(),
+                                resourcePkgMap,
+                                allCandidates,
+                                cycles);
+                        }
+                    }
+                }
+            }
+        }
+
+        cycles.remove(current);
+    }
+
+    private void mergeCandidatePackage(
+        Resource current, boolean requires,
+        Requirement currentReq, Capability candCap,
+        Map<Resource, Packages> resourcePkgMap)
+    {
+        if (candCap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+        {
+            // Merge the candidate capability into the resource's package space
+            // for imported or required packages, appropriately.
+
+            String pkgName = (String)
+                candCap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
+
+            List blameReqs = new ArrayList();
+            blameReqs.add(currentReq);
+
+            Packages currentPkgs = resourcePkgMap.get(current);
+
+            Map<String, List<Blame>> packages = (requires)
+                ? currentPkgs.m_requiredPkgs
+                : currentPkgs.m_importedPkgs;
+            List<Blame> blames = packages.get(pkgName);
+            if (blames == null)
+            {
+                blames = new ArrayList<Blame>();
+                packages.put(pkgName, blames);
+            }
+            blames.add(new Blame(candCap, blameReqs));
+
+//dumpResourcePkgs(current, currentPkgs);
+        }
+    }
+
+    private void mergeUses(
+        ResolveContext rc, Resource current, Packages currentPkgs,
+        Capability mergeCap, List<Requirement> blameReqs,
+        Map<Resource, Packages> resourcePkgMap,
+        Candidates allCandidates,
+        Map<Capability, List<Resource>> cycleMap)
+    {
+        // If there are no uses, then just return.
+        // If the candidate resource is the same as the current resource,
+        // then we don't need to verify and merge the uses constraints
+        // since this will happen as we build up the package space.
+        if (current.equals(mergeCap.getResource()))
+        {
+            return;
+        }
+
+        // Check for cycles.
+        List<Resource> list = cycleMap.get(mergeCap);
+        if ((list != null) && list.contains(current))
+        {
+            return;
+        }
+        list = (list == null) ? new ArrayList<Resource>() : list;
+        list.add(current);
+        cycleMap.put(mergeCap, list);
+
+        for (Capability candSourceCap : getPackageSources(rc, mergeCap, resourcePkgMap))
+        {
+            List<String> uses;
+// TODO: RFC-112 - Need impl-specific type
+//            if (candSourceCap instanceof FelixCapability)
+//            {
+//                uses = ((FelixCapability) candSourceCap).getUses();
+//            }
+//            else
+            {
+                uses = Collections.EMPTY_LIST;
+                String s = candSourceCap.getDirectives()
+                    .get(Namespace.CAPABILITY_USES_DIRECTIVE);
+                if (s != null)
+                {
+                    // Parse these uses directive.
+                    StringTokenizer tok = new StringTokenizer(s, ",");
+                    uses = new ArrayList(tok.countTokens());
+                    while (tok.hasMoreTokens())
+                    {
+                        uses.add(tok.nextToken().trim());
+                    }
+                }
+            }
+            for (String usedPkgName : uses)
+            {
+                Packages candSourcePkgs = resourcePkgMap.get(candSourceCap.getResource());
+                List<Blame> candSourceBlames = null;
+                // Check to see if the used package is exported.
+                Blame candExportedBlame = candSourcePkgs.m_exportedPkgs.get(usedPkgName);
+                if (candExportedBlame != null)
+                {
+                    candSourceBlames = new ArrayList(1);
+                    candSourceBlames.add(candExportedBlame);
+                }
+                else
+                {
+                    // If the used package is not exported, check to see if it
+                    // is required.
+                    candSourceBlames = candSourcePkgs.m_requiredPkgs.get(usedPkgName);
+                    // Lastly, if the used package is not required, check to see if it
+                    // is imported.
+                    candSourceBlames = (candSourceBlames != null)
+                        ? candSourceBlames : candSourcePkgs.m_importedPkgs.get(usedPkgName);
+                }
+
+                // If the used package cannot be found, then just ignore it
+                // since it has no impact.
+                if (candSourceBlames == null)
+                {
+                    continue;
+                }
+
+                List<Blame> usedCaps = currentPkgs.m_usedPkgs.get(usedPkgName);
+                if (usedCaps == null)
+                {
+                    usedCaps = new ArrayList<Blame>();
+                    currentPkgs.m_usedPkgs.put(usedPkgName, usedCaps);
+                }
+                for (Blame blame : candSourceBlames)
+                {
+                    if (blame.m_reqs != null)
+                    {
+                        List<Requirement> blameReqs2 = new ArrayList(blameReqs);
+                        blameReqs2.add(blame.m_reqs.get(blame.m_reqs.size() - 1));
+                        usedCaps.add(new Blame(blame.m_cap, blameReqs2));
+                        mergeUses(rc, current, currentPkgs, blame.m_cap, blameReqs2,
+                            resourcePkgMap, allCandidates, cycleMap);
+                    }
+                    else
+                    {
+                        usedCaps.add(new Blame(blame.m_cap, blameReqs));
+                        mergeUses(rc, current, currentPkgs, blame.m_cap, blameReqs,
+                            resourcePkgMap, allCandidates, cycleMap);
+                    }
+                }
+            }
+        }
+    }
+
+    private void checkPackageSpaceConsistency(
+        ResolveContext rc,
+        boolean isDynamicImporting,
+        Resource resource,
+        Candidates allCandidates,
+        Map<Resource, Packages> resourcePkgMap,
+        Map<Resource, Object> resultCache) throws ResolutionException
+    {
+        if (rc.getWirings().containsKey(resource) && !isDynamicImporting)
+        {
+            return;
+        }
+        else if(resultCache.containsKey(resource))
+        {
+            return;
+        }
+
+        Packages pkgs = resourcePkgMap.get(resource);
+
+        ResolutionException rethrow = null;
+        Candidates permutation = null;
+        Set<Requirement> mutated = null;
+
+        // Check for conflicting imports from fragments.
+        for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
+        {
+            if (entry.getValue().size() > 1)
+            {
+                Blame sourceBlame = null;
+                for (Blame blame : entry.getValue())
+                {
+                    if (sourceBlame == null)
+                    {
+                        sourceBlame = blame;
+                    }
+                    else if (!sourceBlame.m_cap.getResource().equals(blame.m_cap.getResource()))
+                    {
+                        // Try to permutate the conflicting requirement.
+                        permutate(allCandidates, blame.m_reqs.get(0), m_importPermutations);
+                        // Try to permutate the source requirement.
+                        permutate(allCandidates, sourceBlame.m_reqs.get(0), m_importPermutations);
+                        // Report conflict.
+                        ResolutionException ex = new ResolutionException(
+                            "Uses constraint violation. Unable to resolve resource "
+                            + Util.getSymbolicName(resource)
+                            + " [" + resource
+                            + "] because it is exposed to package '"
+                            + entry.getKey()
+                            + "' from resources "
+                            + Util.getSymbolicName(sourceBlame.m_cap.getResource())
+                            + " [" + sourceBlame.m_cap.getResource()
+                            + "] and "
+                            + Util.getSymbolicName(blame.m_cap.getResource())
+                            + " [" + blame.m_cap.getResource()
+                            + "] via two dependency chains.\n\nChain 1:\n"
+                            + toStringBlame(rc, allCandidates, sourceBlame)
+                            + "\n\nChain 2:\n"
+                            + toStringBlame(rc, allCandidates, blame),
+                            null,
+                            Collections.singleton(blame.m_reqs.get(0)));
+                        m_logger.log(
+                            Logger.LOG_DEBUG,
+                            "Candidate permutation failed due to a conflict with a "
+                            + "fragment import; will try another if possible.",
+                            ex);
+                        throw ex;
+                    }
+                }
+            }
+        }
+
+        // Check if there are any uses conflicts with exported packages.
+        for (Entry<String, Blame> entry : pkgs.m_exportedPkgs.entrySet())
+        {
+            String pkgName = entry.getKey();
+            Blame exportBlame = entry.getValue();
+            if (!pkgs.m_usedPkgs.containsKey(pkgName))
+            {
+                continue;
+            }
+            for (Blame usedBlame : pkgs.m_usedPkgs.get(pkgName))
+            {
+                if (!isCompatible(rc, exportBlame.m_cap, usedBlame.m_cap, resourcePkgMap))
+                {
+                    // Create a candidate permutation that eliminates all candidates
+                    // that conflict with existing selected candidates.
+                    permutation = (permutation != null)
+                        ? permutation
+                        : allCandidates.copy();
+                    rethrow = (rethrow != null)
+                        ? rethrow
+                        : new ResolutionException(
+                            "Uses constraint violation. Unable to resolve resource "
+                            + Util.getSymbolicName(resource)
+                            + " [" + resource
+                            + "] because it exports package '"
+                            + pkgName
+                            + "' and is also exposed to it from resource "
+                            + Util.getSymbolicName(usedBlame.m_cap.getResource())
+                            + " [" + usedBlame.m_cap.getResource()
+                            + "] via the following dependency chain:\n\n"
+                            + toStringBlame(rc, allCandidates, usedBlame),
+                            null,
+                            null);
+
+                    mutated = (mutated != null)
+                        ? mutated
+                        : new HashSet<Requirement>();
+
+                    for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
+                    {
+                        Requirement req = usedBlame.m_reqs.get(reqIdx);
+
+                        // If we've already permutated this requirement in another
+                        // uses constraint, don't permutate it again just continue
+                        // with the next uses constraint.
+                        if (mutated.contains(req))
+                        {
+                            break;
+                        }
+
+                        // See if we can permutate the candidates for blamed
+                        // requirement; there may be no candidates if the resource
+                        // associated with the requirement is already resolved.
+                        List<Capability> candidates = permutation.getCandidates(req);
+                        if ((candidates != null) && (candidates.size() > 1))
+                        {
+                            mutated.add(req);
+                            // Remove the conflicting candidate.
+                            candidates.remove(0);
+                            // Continue with the next uses constraint.
+                            break;
+                        }
+                    }
+                }
+            }
+
+            if (rethrow != null)
+            {
+                if (mutated.size() > 0)
+                {
+                    m_usesPermutations.add(permutation);
+                }
+                m_logger.log(
+                    Logger.LOG_DEBUG,
+                    "Candidate permutation failed due to a conflict between "
+                    + "an export and import; will try another if possible.",
+                    rethrow);
+                throw rethrow;
+            }
+        }
+
+        // Check if there are any uses conflicts with imported packages.
+        for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
+        {
+            for (Blame importBlame : entry.getValue())
+            {
+                String pkgName = entry.getKey();
+                if (!pkgs.m_usedPkgs.containsKey(pkgName))
+                {
+                    continue;
+                }
+                for (Blame usedBlame : pkgs.m_usedPkgs.get(pkgName))
+                {
+                    if (!isCompatible(rc, importBlame.m_cap, usedBlame.m_cap, resourcePkgMap))
+                    {
+                        // Create a candidate permutation that eliminates any candidates
+                        // that conflict with existing selected candidates.
+                        permutation = (permutation != null)
+                            ? permutation
+                            : allCandidates.copy();
+                        rethrow = (rethrow != null)
+                            ? rethrow
+                            : new ResolutionException(
+                                "Uses constraint violation. Unable to resolve resource "
+                                + Util.getSymbolicName(resource)
+                                + " [" + resource
+                                + "] because it is exposed to package '"
+                                + pkgName
+                                + "' from resources "
+                                + Util.getSymbolicName(importBlame.m_cap.getResource())
+                                + " [" + importBlame.m_cap.getResource()
+                                + "] and "
+                                + Util.getSymbolicName(usedBlame.m_cap.getResource())
+                                + " [" + usedBlame.m_cap.getResource()
+                                + "] via two dependency chains.\n\nChain 1:\n"
+                                + toStringBlame(rc, allCandidates, importBlame)
+                                + "\n\nChain 2:\n"
+                                + toStringBlame(rc, allCandidates, usedBlame),
+                                null,
+                                null);
+
+                        mutated = (mutated != null)
+                            ? mutated
+                            : new HashSet();
+
+                        for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
+                        {
+                            Requirement req = usedBlame.m_reqs.get(reqIdx);
+
+                            // If we've already permutated this requirement in another
+                            // uses constraint, don't permutate it again just continue
+                            // with the next uses constraint.
+                            if (mutated.contains(req))
+                            {
+                                break;
+                            }
+
+                            // See if we can permutate the candidates for blamed
+                            // requirement; there may be no candidates if the resource
+                            // associated with the requirement is already resolved.
+                            List<Capability> candidates = permutation.getCandidates(req);
+                            if ((candidates != null) && (candidates.size() > 1))
+                            {
+                                mutated.add(req);
+                                // Remove the conflicting candidate.
+                                candidates.remove(0);
+                                // Continue with the next uses constraint.
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                // If there was a uses conflict, then we should add a uses
+                // permutation if we were able to permutate any candidates.
+                // Additionally, we should try to push an import permutation
+                // for the original import to force a backtracking on the
+                // original candidate decision if no viable candidate is found
+                // for the conflicting uses constraint.
+                if (rethrow != null)
+                {
+                    // Add uses permutation if we mutated any candidates.
+                    if (mutated.size() > 0)
+                    {
+                        m_usesPermutations.add(permutation);
+                    }
+
+                    // Try to permutate the candidate for the original
+                    // import requirement; only permutate it if we haven't
+                    // done so already.
+                    Requirement req = importBlame.m_reqs.get(0);
+                    if (!mutated.contains(req))
+                    {
+                        // Since there may be lots of uses constraint violations
+                        // with existing import decisions, we may end up trying
+                        // to permutate the same import a lot of times, so we should
+                        // try to check if that the case and only permutate it once.
+                        permutateIfNeeded(allCandidates, req, m_importPermutations);
+                    }
+
+                    m_logger.log(
+                        Logger.LOG_DEBUG,
+                        "Candidate permutation failed due to a conflict between "
+                        + "imports; will try another if possible.",
+                        rethrow);
+                    throw rethrow;
+                }
+            }
+        }
+
+        resultCache.put(resource, Boolean.TRUE);
+
+        // Now check the consistency of all resources on which the
+        // current resource depends. Keep track of the current number
+        // of permutations so we know if the lower level check was
+        // able to create a permutation or not in the case of failure.
+        int permCount = m_usesPermutations.size() + m_importPermutations.size();
+        for (Entry<String, List<Blame>> entry : pkgs.m_importedPkgs.entrySet())
+        {
+            for (Blame importBlame : entry.getValue())
+            {
+                if (!resource.equals(importBlame.m_cap.getResource()))
+                {
+                    try
+                    {
+                        checkPackageSpaceConsistency(
+                            rc, false, importBlame.m_cap.getResource(),
+                            allCandidates, resourcePkgMap, resultCache);
+                    }
+                    catch (ResolutionException ex)
+                    {
+                        // If the lower level check didn't create any permutations,
+                        // then we should create an import permutation for the
+                        // requirement with the dependency on the failing resource
+                        // to backtrack on our current candidate selection.
+                        if (permCount == (m_usesPermutations.size() + m_importPermutations.size()))
+                        {
+                            Requirement req = importBlame.m_reqs.get(0);
+                            permutate(allCandidates, req, m_importPermutations);
+                        }
+                        throw ex;
+                    }
+                }
+            }
+        }
+    }
+
+    private static void permutate(
+        Candidates allCandidates, Requirement req, List<Candidates> permutations)
+    {
+        List<Capability> candidates = allCandidates.getCandidates(req);
+        if (candidates.size() > 1)
+        {
+            Candidates perm = allCandidates.copy();
+            candidates = perm.getCandidates(req);
+            candidates.remove(0);
+            permutations.add(perm);
+        }
+    }
+
+    private static void permutateIfNeeded(
+        Candidates allCandidates, Requirement req, List<Candidates> permutations)
+    {
+        List<Capability> candidates = allCandidates.getCandidates(req);
+        if (candidates.size() > 1)
+        {
+            // Check existing permutations to make sure we haven't
+            // already permutated this requirement. This check for
+            // duplicate permutations is simplistic. It assumes if
+            // there is any permutation that contains a different
+            // initial candidate for the requirement in question,
+            // then it has already been permutated.
+            boolean permutated = false;
+            for (Candidates existingPerm : permutations)
+            {
+                List<Capability> existingPermCands = existingPerm.getCandidates(req);
+                if (!existingPermCands.get(0).equals(candidates.get(0)))
+                {
+                    permutated = true;
+                }
+            }
+            // If we haven't already permutated the existing
+            // import, do so now.
+            if (!permutated)
+            {
+                permutate(allCandidates, req, permutations);
+            }
+        }
+    }
+
+    private static void calculateExportedPackages(
+        ResolveContext rc,
+        Resource resource,
+        Candidates allCandidates,
+        Map<Resource, Packages> resourcePkgMap)
+    {
+        Packages packages = resourcePkgMap.get(resource);
+        if (packages != null)
+        {
+            return;
+        }
+        packages = new Packages(resource);
+
+        // Get all exported packages.
+        Wiring wiring = rc.getWirings().get(resource);
+        List<Capability> caps = (wiring != null)
+            ? wiring.getResourceCapabilities(null)
+            : resource.getCapabilities(null);
+        Map<String, Capability> exports = new HashMap<String, Capability>(caps.size());
+        for (Capability cap : caps)
+        {
+            if (cap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+            {
+                if (!cap.getResource().equals(resource))
+                {
+                    cap = new WrappedCapability(resource, cap);
+                }
+                exports.put(
+                    (String) cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE),
+                    cap);
+            }
+        }
+        // Remove substitutable exports that were imported.
+        // For resolved resources Wiring.getCapabilities()
+        // already excludes imported substitutable exports, but
+        // for resolving resources we must look in the candidate
+        // map to determine which exports are substitutable.
+        if (!exports.isEmpty())
+        {
+            if (wiring == null)
+            {
+                for (Requirement req : resource.getRequirements(null))
+                {
+                    if (req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+                    {
+                        List<Capability> cands = allCandidates.getCandidates(req);
+                        if ((cands != null) && !cands.isEmpty())
+                        {
+                            String pkgName = (String) cands.get(0)
+                                .getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
+                            exports.remove(pkgName);
+                        }
+                    }
+                }
+            }
+
+            // Add all non-substituted exports to the resources's package space.
+            for (Entry<String, Capability> entry : exports.entrySet())
+            {
+                packages.m_exportedPkgs.put(
+                    entry.getKey(), new Blame(entry.getValue(), null));
+            }
+        }
+
+        resourcePkgMap.put(resource, packages);
+    }
+
+    private boolean isCompatible(
+        ResolveContext rc, Capability currentCap, Capability candCap,
+        Map<Resource, Packages> resourcePkgMap)
+    {
+        if ((currentCap != null) && (candCap != null))
+        {
+            if (currentCap.equals(candCap))
+            {
+                return true;
+            }
+
+            List<Capability> currentSources =
+                getPackageSources(
+                    rc,
+                    currentCap,
+                    resourcePkgMap);
+            List<Capability> candSources =
+                getPackageSources(
+                    rc,
+                    candCap,
+                    resourcePkgMap);
+
+            return currentSources.containsAll(candSources)
+                || candSources.containsAll(currentSources);
+        }
+        return true;
+    }
+
+    private Map<Capability, List<Capability>> m_packageSourcesCache = new HashMap();
+
+    private List<Capability> getPackageSources(
+        ResolveContext rc, Capability cap, Map<Resource, Packages> resourcePkgMap)
+    {
+        // If it is a package, then calculate sources for it.
+        if (cap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+        {
+            List<Capability> sources = m_packageSourcesCache.get(cap);
+            if (sources == null)
+            {
+                sources = getPackageSourcesInternal(
+                    rc, cap, resourcePkgMap, new ArrayList(), new HashSet());
+                m_packageSourcesCache.put(cap, sources);
+            }
+            return sources;
+        }
+
+        // Otherwise, need to return generic capabilies that have
+        // uses constraints so they are included for consistency
+        // checking.
+        String uses = cap.getDirectives().get(Namespace.CAPABILITY_USES_DIRECTIVE);
+        if ((uses != null) && (uses.length() > 0))
+        {
+            return Collections.singletonList(cap);
+        }
+
+        return Collections.EMPTY_LIST;
+    }
+
+    private static List<Capability> getPackageSourcesInternal(
+        ResolveContext rc, Capability cap, Map<Resource, Packages> resourcePkgMap,
+        List<Capability> sources, Set<Capability> cycleMap)
+    {
+        if (cap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+        {
+            if (cycleMap.contains(cap))
+            {
+                return sources;
+            }
+            cycleMap.add(cap);
+
+            // Get the package name associated with the capability.
+            String pkgName = cap.getAttributes()
+                .get(PackageNamespace.PACKAGE_NAMESPACE).toString();
+
+            // Since a resource can export the same package more than once, get
+            // all package capabilities for the specified package name.
+            Wiring wiring = rc.getWirings().get(cap.getResource());
+            List<Capability> caps = (wiring != null)
+                ? wiring.getResourceCapabilities(null)
+                : cap.getResource().getCapabilities(null);
+            for (Capability sourceCap : caps)
+            {
+                if (sourceCap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE)
+                    && sourceCap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE).equals(pkgName))
+                {
+                    // Since capabilities may come from fragments, we need to check
+                    // for that case and wrap them.
+                    if (!cap.getResource().equals(sourceCap.getResource()))
+                    {
+                        sources.add(new WrappedCapability(cap.getResource(), sourceCap));
+                    }
+                    else
+                    {
+                        sources.add(sourceCap);
+                    }
+                }
+            }
+
+            // Then get any addition sources for the package from required bundles.
+            Packages pkgs = resourcePkgMap.get(cap.getResource());
+            List<Blame> required = pkgs.m_requiredPkgs.get(pkgName);
+            if (required != null)
+            {
+                for (Blame blame : required)
+                {
+                    getPackageSourcesInternal(rc, blame.m_cap, resourcePkgMap, sources, cycleMap);
+                }
+            }
+        }
+
+        return sources;
+    }
+
+    private static Resource getDeclaredResource(Resource resource)
+    {
+        if (resource instanceof WrappedResource)
+        {
+            return ((WrappedResource) resource).getDeclaredResource();
+        }
+        return resource;
+    }
+
+    private static Capability getDeclaredCapability(Capability c)
+    {
+        if (c instanceof HostedCapability)
+        {
+            return ((HostedCapability) c).getDeclaredCapability();
+        }
+        return c;
+    }
+
+    private static Requirement getDeclaredRequirement(Requirement r)
+    {
+        if (r instanceof WrappedRequirement)
+        {
+            return ((WrappedRequirement) r).getDeclaredRequirement();
+        }
+        return r;
+    }
+
+    private static Map<Resource, List<Wire>> populateWireMap(
+        ResolveContext rc, Resource resource, Map<Resource, Packages> resourcePkgMap,
+        Map<Resource, List<Wire>> wireMap, Candidates allCandidates)
+    {
+        Resource unwrappedResource = getDeclaredResource(resource);
+        if (!rc.getWirings().containsKey(unwrappedResource)
+            && !wireMap.containsKey(unwrappedResource))
+        {
+            wireMap.put(unwrappedResource, (List<Wire>) Collections.EMPTY_LIST);
+
+            List<Wire> packageWires = new ArrayList<Wire>();
+            List<Wire> bundleWires = new ArrayList<Wire>();
+            List<Wire> capabilityWires = new ArrayList<Wire>();
+
+            for (Requirement req : resource.getRequirements(null))
+            {
+                List<Capability> cands = allCandidates.getCandidates(req);
+                if ((cands != null) && (cands.size() > 0))
+                {
+                    Capability cand = cands.get(0);
+                    // Ignore resources that import themselves.
+                    if (!resource.equals(cand.getResource()))
+                    {
+                        if (!rc.getWirings().containsKey(cand.getResource()))
+                        {
+                            populateWireMap(rc, cand.getResource(),
+                                resourcePkgMap, wireMap, allCandidates);
+                        }
+                        Packages candPkgs = resourcePkgMap.get(cand.getResource());
+                        Wire wire = new WireImpl(
+                            unwrappedResource,
+                            getDeclaredRequirement(req),
+                            getDeclaredResource(cand.getResource()),
+                            getDeclaredCapability(cand));
+                        if (req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+                        {
+                            packageWires.add(wire);
+                        }
+                        else if (req.getNamespace().equals(BundleNamespace.BUNDLE_NAMESPACE))
+                        {
+                            bundleWires.add(wire);
+                        }
+                        else
+                        {
+                            capabilityWires.add(wire);
+                        }
+                    }
+                }
+            }
+
+            // Combine package wires with require wires last.
+            packageWires.addAll(bundleWires);
+            packageWires.addAll(capabilityWires);
+            wireMap.put(unwrappedResource, packageWires);
+
+            // Add host wire for any fragments.
+            if (resource instanceof WrappedResource)
+            {
+                List<Resource> fragments = ((WrappedResource) resource).getFragments();
+                for (Resource fragment : fragments)
+                {
+                    List<Wire> hostWires = wireMap.get(fragment);
+                    if (hostWires == null)
+                    {
+                        hostWires = new ArrayList<Wire>();
+                        wireMap.put(fragment, hostWires);
+                    }
+                    hostWires.add(
+                        new WireImpl(
+                            getDeclaredResource(fragment),
+                            fragment.getRequirements(
+                                HostNamespace.HOST_NAMESPACE).get(0),
+                            unwrappedResource,
+                            unwrappedResource.getCapabilities(
+                                HostNamespace.HOST_NAMESPACE).get(0)));
+                }
+            }
+        }
+
+        return wireMap;
+    }
+
+    private static Map<Resource, List<Wire>> populateDynamicWireMap(
+        ResolveContext rc, Resource resource, Requirement dynReq,
+        Map<Resource, Packages> resourcePkgMap,
+        Map<Resource, List<Wire>> wireMap, Candidates allCandidates)
+    {
+        wireMap.put(resource, (List<Wire>) Collections.EMPTY_LIST);
+
+        List<Wire> packageWires = new ArrayList<Wire>();
+
+        // Get the candidates for the current dynamic requirement.
+        List<Capability> candCaps = allCandidates.getCandidates(dynReq);
+        // Record the dynamic candidate.
+        Capability dynCand = candCaps.get(0);
+
+        if (!rc.getWirings().containsKey(dynCand.getResource()))
+        {
+            populateWireMap(rc, dynCand.getResource(), resourcePkgMap,
+                wireMap, allCandidates);
+        }
+
+        packageWires.add(
+            new WireImpl(
+                resource,
+                dynReq,
+                getDeclaredResource(dynCand.getResource()),
+                getDeclaredCapability(dynCand)));
+
+        wireMap.put(resource, packageWires);
+
+        return wireMap;
+    }
+
+    private static void dumpResourcePkgMap(
+        ResolveContext rc, Map<Resource, Packages> resourcePkgMap)
+    {
+        System.out.println("+++RESOURCE PKG MAP+++");
+        for (Entry<Resource, Packages> entry : resourcePkgMap.entrySet())
+        {
+            dumpResourcePkgs(rc, entry.getKey(), entry.getValue());
+        }
+    }
+
+    private static void dumpResourcePkgs(
+        ResolveContext rc, Resource resource, Packages packages)
+    {
+        Wiring wiring = rc.getWirings().get(resource);
+        System.out.println(resource
+            + " (" + ((wiring != null) ? "RESOLVED)" : "UNRESOLVED)"));
+        System.out.println("  EXPORTED");
+        for (Entry<String, Blame> entry : packages.m_exportedPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+        System.out.println("  IMPORTED");
+        for (Entry<String, List<Blame>> entry : packages.m_importedPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+        System.out.println("  REQUIRED");
+        for (Entry<String, List<Blame>> entry : packages.m_requiredPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+        System.out.println("  USED");
+        for (Entry<String, List<Blame>> entry : packages.m_usedPkgs.entrySet())
+        {
+            System.out.println("    " + entry.getKey() + " - " + entry.getValue());
+        }
+    }
+
+    private static String toStringBlame(
+        ResolveContext rc, Candidates allCandidates, Blame blame)
+    {
+        StringBuffer sb = new StringBuffer();
+        if ((blame.m_reqs != null) && !blame.m_reqs.isEmpty())
+        {
+            for (int i = 0; i < blame.m_reqs.size(); i++)
+            {
+                Requirement req = blame.m_reqs.get(i);
+                sb.append("  ");
+                sb.append(Util.getSymbolicName(req.getResource()));
+                sb.append(" [");
+                sb.append(req.getResource().toString());
+                sb.append("]\n");
+                if (req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+                {
+                    sb.append("    import: ");
+                }
+                else
+                {
+                    sb.append("    require: ");
+                }
+                sb.append(req.getDirectives().get(Namespace.REQUIREMENT_FILTER_DIRECTIVE));
+                sb.append("\n     |");
+                if (req.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+                {
+                    sb.append("\n    export: ");
+                }
+                else
+                {
+                    sb.append("\n    provide: ");
+                }
+                if ((i + 1) < blame.m_reqs.size())
+                {
+                    Capability cap = getSatisfyingCapability(
+                        rc,
+                        allCandidates,
+                        blame.m_reqs.get(i));
+                    if (cap.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+                    {
+                        sb.append(PackageNamespace.PACKAGE_NAMESPACE);
+                        sb.append("=");
+                        sb.append(cap.getAttributes()
+                            .get(PackageNamespace.PACKAGE_NAMESPACE).toString());
+                        Capability usedCap =
+                            getSatisfyingCapability(
+                                rc,
+                                allCandidates,
+                                blame.m_reqs.get(i + 1));
+                        sb.append("; uses:=");
+                        sb.append(usedCap.getAttributes()
+                            .get(PackageNamespace.PACKAGE_NAMESPACE));
+                    }
+                    else
+                    {
+                        sb.append(cap);
+                    }
+                    sb.append("\n");
+                }
+                else
+                {
+                    Capability export = getSatisfyingCapability(
+                        rc,
+                        allCandidates,
+                        blame.m_reqs.get(i));
+                    sb.append(export.getNamespace());
+                    sb.append("=");
+                    sb.append(export.getAttributes().get(export.getNamespace()).toString());
+                    if (export.getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE)
+                        && !export.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE)
+                            .equals(blame.m_cap.getAttributes().get(
+                                PackageNamespace.PACKAGE_NAMESPACE)))
+                    {
+                        sb.append("; uses:=");
+                        sb.append(blame.m_cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE));
+                        sb.append("\n    export: ");
+                        sb.append(PackageNamespace.PACKAGE_NAMESPACE);
+                        sb.append("=");
+                        sb.append(blame.m_cap.getAttributes()
+                            .get(PackageNamespace.PACKAGE_NAMESPACE).toString());
+                    }
+                    sb.append("\n  ");
+                    sb.append(Util.getSymbolicName(blame.m_cap.getResource()));
+                    sb.append(" [");
+                    sb.append(blame.m_cap.getResource().toString());
+                    sb.append("]");
+                }
+            }
+        }
+        else
+        {
+            sb.append(blame.m_cap.getResource().toString());
+        }
+        return sb.toString();
+    }
+
+    private static Capability getSatisfyingCapability(
+        ResolveContext rc, Candidates allCandidates, Requirement req)
+    {
+        Capability cap = null;
+
+        // If the requiring revision is not resolved, then check in the
+        // candidate map for its matching candidate.
+        List<Capability> cands = allCandidates.getCandidates(req);
+        if (cands != null)
+        {
+            cap = cands.get(0);
+        }
+        // Otherwise, if the requiring revision is resolved then check
+        // in its wires for the capability satisfying the requirement.
+        else if (rc.getWirings().containsKey(req.getResource()))
+        {
+            List<Wire> wires =
+                rc.getWirings().get(req.getResource()).getRequiredResourceWires(null);
+            req = getDeclaredRequirement(req);
+            for (Wire w : wires)
+            {
+                if (w.getRequirement().equals(req))
+                {
+// TODO: RESOLVER - This is not 100% correct, since requirements for
+//       dynamic imports with wildcards will reside on many wires and
+//       this code only finds the first one, not necessarily the correct
+//       one. This is only used for the diagnostic message, but it still
+//       could confuse the user.
+                    cap = w.getCapability();
+                    break;
+                }
+            }
+        }
+
+        return cap;
+    }
+
+    private static class Packages
+    {
+        private final Resource m_resource;
+        public final Map<String, Blame> m_exportedPkgs = new HashMap();
+        public final Map<String, List<Blame>> m_importedPkgs = new HashMap();
+        public final Map<String, List<Blame>> m_requiredPkgs = new HashMap();
+        public final Map<String, List<Blame>> m_usedPkgs = new HashMap();
+
+        public Packages(Resource resource)
+        {
+            m_resource = resource;
+        }
+    }
+
+    private static class Blame
+    {
+        public final Capability m_cap;
+        public final List<Requirement> m_reqs;
+
+        public Blame(Capability cap, List<Requirement> reqs)
+        {
+            m_cap = cap;
+            m_reqs = reqs;
+        }
+
+        @Override
+        public String toString()
+        {
+            return m_cap.getResource()
+                + "." + m_cap.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE)
+                + (((m_reqs == null) || m_reqs.isEmpty())
+                    ? " NO BLAME"
+                    : " BLAMED ON " + m_reqs);
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            return (o instanceof Blame) && m_reqs.equals(((Blame) o).m_reqs)
+                && m_cap.equals(((Blame) o).m_cap);
+        }
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/ShadowList.java b/resolver/src/main/java/org/apache/felix/resolver/ShadowList.java
new file mode 100644
index 0000000..05734da
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/ShadowList.java
@@ -0,0 +1,157 @@
+/*
+ * 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.resolver;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+public class ShadowList<T> implements List<T>
+{
+    private final List<T> m_original;
+    private final List<T> m_shadow;
+
+    public ShadowList(List<T> original)
+    {
+        m_original = original;
+        m_shadow = new ArrayList<T>(original);
+    }
+
+    public List<T> getOriginal()
+    {
+        return m_original;
+    }
+
+    public int size()
+    {
+        return m_shadow.size();
+    }
+
+    public boolean isEmpty()
+    {
+        return m_shadow.isEmpty();
+    }
+
+    public boolean contains(Object o)
+    {
+        return m_shadow.contains(o);
+    }
+
+    public Iterator<T> iterator()
+    {
+        return m_shadow.iterator();
+    }
+
+    public Object[] toArray()
+    {
+        return m_shadow.toArray();
+    }
+
+    public <T> T[] toArray(T[] ts)
+    {
+        return m_shadow.toArray(ts);
+    }
+
+    public boolean add(T e)
+    {
+        return m_shadow.add(e);
+    }
+
+    public boolean remove(Object o)
+    {
+        return m_shadow.remove(o);
+    }
+
+    public boolean containsAll(Collection<?> clctn)
+    {
+        return m_shadow.containsAll(clctn);
+    }
+
+    public boolean addAll(Collection<? extends T> clctn)
+    {
+        return m_shadow.addAll(clctn);
+    }
+
+    public boolean addAll(int i, Collection<? extends T> clctn)
+    {
+        return m_shadow.addAll(i, clctn);
+    }
+
+    public boolean removeAll(Collection<?> clctn)
+    {
+        return m_shadow.removeAll(clctn);
+    }
+
+    public boolean retainAll(Collection<?> clctn)
+    {
+        return m_shadow.retainAll(clctn);
+    }
+
+    public void clear()
+    {
+        m_shadow.clear();
+    }
+
+    public T get(int i)
+    {
+        return m_shadow.get(i);
+    }
+
+    public T set(int i, T e)
+    {
+        return m_shadow.set(i, e);
+    }
+
+    public void add(int i, T e)
+    {
+        m_shadow.add(i, e);
+    }
+
+    public T remove(int i)
+    {
+        return m_shadow.remove(i);
+    }
+
+    public int indexOf(Object o)
+    {
+        return m_shadow.indexOf(o);
+    }
+
+    public int lastIndexOf(Object o)
+    {
+        return m_shadow.lastIndexOf(o);
+    }
+
+    public ListIterator<T> listIterator()
+    {
+        return m_shadow.listIterator();
+    }
+
+    public ListIterator<T> listIterator(int i)
+    {
+        return m_shadow.listIterator(i);
+    }
+
+    public List<T> subList(int i, int i1)
+    {
+        return m_shadow.subList(i, i1);
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/SimpleHostedCapability.java b/resolver/src/main/java/org/apache/felix/resolver/SimpleHostedCapability.java
new file mode 100644
index 0000000..98e2d4f
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/SimpleHostedCapability.java
@@ -0,0 +1,61 @@
+/*
+ * 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.resolver;
+
+import java.util.Map;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+import org.osgi.service.resolver.HostedCapability;
+
+class SimpleHostedCapability implements HostedCapability
+{
+    private final Resource m_host;
+    private final Capability m_cap;
+
+    SimpleHostedCapability(Resource host, Capability cap)
+    {
+        m_host = host;
+        m_cap = cap;
+    }
+
+    public Resource getResource()
+    {
+        return m_host;
+    }
+
+    public Capability getDeclaredCapability()
+    {
+        return m_cap;
+    }
+
+    public String getNamespace()
+    {
+        return m_cap.getNamespace();
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_cap.getDirectives();
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_cap.getAttributes();
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/Util.java b/resolver/src/main/java/org/apache/felix/resolver/Util.java
new file mode 100644
index 0000000..e9ab8e6
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/Util.java
@@ -0,0 +1,100 @@
+/*
+ * 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.resolver;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class Util
+{
+    public static String getSymbolicName(Resource resource)
+    {
+        List<Capability> caps = resource.getCapabilities(null);
+        for (Capability cap : caps)
+        {
+            if (cap.getNamespace().equals(IdentityNamespace.IDENTITY_NAMESPACE))
+            {
+                return cap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE).toString();
+            }
+        }
+        return null;
+    }
+
+    public static Version getVersion(Resource resource)
+    {
+        List<Capability> caps = resource.getCapabilities(null);
+        for (Capability cap : caps)
+        {
+            if (cap.getNamespace().equals(IdentityNamespace.IDENTITY_NAMESPACE))
+            {
+                return (Version)
+                    cap.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+            }
+        }
+        return null;
+    }
+
+    public static boolean isFragment(Resource resource)
+    {
+        List<Capability> caps = resource.getCapabilities(null);
+        for (Capability cap : caps)
+        {
+            if (cap.getNamespace().equals(IdentityNamespace.IDENTITY_NAMESPACE))
+            {
+                String type = (String)
+                    cap.getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE);
+                return (type != null) && type.equals(IdentityNamespace.TYPE_FRAGMENT);
+            }
+        }
+        return false;
+    }
+
+    public static boolean isOptional(Requirement req)
+    {
+        String resolution = req.getDirectives().get(Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE);
+        return ((resolution == null)
+            || resolution.equalsIgnoreCase(Namespace.RESOLUTION_OPTIONAL));
+    }
+
+    public static List<Requirement> getDynamicRequirements(List<Requirement> reqs)
+    {
+        List<Requirement> result = new ArrayList<Requirement>();
+        if (reqs != null)
+        {
+            for (Requirement req : reqs)
+            {
+                String resolution = req.getDirectives()
+                    .get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE);
+                if ((resolution != null)
+                    && resolution.equals(PackageNamespace.RESOLUTION_DYNAMIC))
+                {
+                    result.add(req);
+                }
+            }
+        }
+        return result;
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/WireImpl.java b/resolver/src/main/java/org/apache/felix/resolver/WireImpl.java
new file mode 100644
index 0000000..7af9b87
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/WireImpl.java
@@ -0,0 +1,70 @@
+/*
+ * 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.resolver;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+
+class WireImpl implements Wire
+{
+    private final Resource m_requirer;
+    private final Requirement m_req;
+    private final Resource m_provider;
+    private final Capability m_cap;
+
+    public WireImpl(
+        Resource requirer, Requirement req,
+        Resource provider, Capability cap)
+    {
+        m_requirer = requirer;
+        m_req = req;
+        m_provider = provider;
+        m_cap = cap;
+    }
+
+    public Resource getRequirer()
+    {
+        return m_requirer;
+    }
+
+    public Requirement getRequirement()
+    {
+        return m_req;
+    }
+
+    public Resource getProvider()
+    {
+        return m_provider;
+    }
+
+    public Capability getCapability()
+    {
+        return m_cap;
+    }
+
+    @Override
+    public String toString()
+    {
+        return m_req
+            + " -> "
+            + "[" + m_provider + "]";
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/WrappedCapability.java b/resolver/src/main/java/org/apache/felix/resolver/WrappedCapability.java
new file mode 100644
index 0000000..9bbec36
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/WrappedCapability.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
+ *
+ * Licensed 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.resolver;
+
+import java.util.Map;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+import org.osgi.service.resolver.HostedCapability;
+
+public class WrappedCapability implements HostedCapability
+{
+    private final Resource m_host;
+    private final Capability m_cap;
+
+    public WrappedCapability(Resource host, Capability cap)
+    {
+        m_host = host;
+        m_cap = cap;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        final WrappedCapability other = (WrappedCapability) obj;
+        if (m_host != other.m_host && (m_host == null || !m_host.equals(other.m_host)))
+        {
+            return false;
+        }
+        if (m_cap != other.m_cap && (m_cap == null || !m_cap.equals(other.m_cap)))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int hash = 7;
+        hash = 37 * hash + (m_host != null ? m_host.hashCode() : 0);
+        hash = 37 * hash + (m_cap != null ? m_cap.hashCode() : 0);
+        return hash;
+    }
+
+    public Capability getDeclaredCapability()
+    {
+        return m_cap;
+    }
+
+    public Resource getResource()
+    {
+        return m_host;
+    }
+
+    public String getNamespace()
+    {
+        return m_cap.getNamespace();
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_cap.getDirectives();
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_cap.getAttributes();
+    }
+
+// TODO: RFC-112 - Need impl-specific type.
+//    public List<String> getUses()
+//    {
+//        return m_cap.getUses();
+//    }
+
+    @Override
+    public String toString()
+    {
+        if (m_host == null)
+        {
+            return getAttributes().toString();
+        }
+        if (getNamespace().equals(PackageNamespace.PACKAGE_NAMESPACE))
+        {
+            return "[" + m_host + "] "
+                + getNamespace()
+                + "; "
+                + getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
+        }
+        return "[" + m_host + "] " + getNamespace() + "; " + getAttributes();
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/WrappedRequirement.java b/resolver/src/main/java/org/apache/felix/resolver/WrappedRequirement.java
new file mode 100644
index 0000000..a9d66df
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/WrappedRequirement.java
@@ -0,0 +1,102 @@
+/*
+ * 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.resolver;
+
+import java.util.Map;
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class WrappedRequirement implements Requirement
+{
+    private final Resource m_host;
+    private final Requirement m_req;
+
+    public WrappedRequirement(Resource host, Requirement req)
+    {
+        m_host = host;
+        m_req = req;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj == null)
+        {
+            return false;
+        }
+        if (getClass() != obj.getClass())
+        {
+            return false;
+        }
+        final WrappedRequirement other = (WrappedRequirement) obj;
+        if (m_host != other.m_host && (m_host == null || !m_host.equals(other.m_host)))
+        {
+            return false;
+        }
+        if (m_req != other.m_req && (m_req == null || !m_req.equals(other.m_req)))
+        {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int hash = 7;
+        hash = 37 * hash + (m_host != null ? m_host.hashCode() : 0);
+        hash = 37 * hash + (m_req != null ? m_req.hashCode() : 0);
+        return hash;
+    }
+
+    public Requirement getDeclaredRequirement()
+    {
+        return m_req;
+    }
+
+    public Resource getResource()
+    {
+        return m_host;
+    }
+
+    public String getNamespace()
+    {
+        return m_req.getNamespace();
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_req.getDirectives();
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_req.getAttributes();
+    }
+
+    @Override
+    public String toString()
+    {
+        return "[" + m_host + "] "
+            + getNamespace()
+            + "; "
+            + getDirectives().get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/WrappedResource.java b/resolver/src/main/java/org/apache/felix/resolver/WrappedResource.java
new file mode 100644
index 0000000..693e8bb
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/WrappedResource.java
@@ -0,0 +1,127 @@
+/*
+ * 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.resolver;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
+import org.osgi.framework.namespace.HostNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+class WrappedResource implements Resource
+{
+    private final Resource m_host;
+    private final List<Resource> m_fragments;
+    private List<Capability> m_cachedCapabilities = null;
+    private List<Requirement> m_cachedRequirements = null;
+
+    public WrappedResource(Resource host, List<Resource> fragments)
+    {
+        m_host = host;
+        m_fragments = fragments;
+    }
+
+    public Resource getDeclaredResource()
+    {
+        return m_host;
+    }
+
+    public List<Resource> getFragments()
+    {
+        return m_fragments;
+    }
+
+    public List<Capability> getCapabilities(String namespace)
+    {
+        if (m_cachedCapabilities == null)
+        {
+            List<Capability> caps = new ArrayList<Capability>();
+
+            // Wrap host capabilities.
+            for (Capability cap : m_host.getCapabilities(null))
+            {
+                caps.add(new WrappedCapability(this, cap));
+            }
+
+            // Wrap fragment capabilities.
+            if (m_fragments != null)
+            {
+                for (Resource fragment : m_fragments)
+                {
+                    for (Capability cap : fragment.getCapabilities(null))
+                    {
+                        // Filter out identity capabilities, since they
+                        // are not part of the fragment payload.
+                        if (!cap.getNamespace()
+                            .equals(IdentityNamespace.IDENTITY_NAMESPACE))
+                        {
+                            caps.add(new WrappedCapability(this,  cap));
+                        }
+                    }
+                }
+            }
+            m_cachedCapabilities = Collections.unmodifiableList(caps);
+        }
+        return m_cachedCapabilities;
+    }
+
+    public List<Requirement> getRequirements(String namespace)
+    {
+        if (m_cachedRequirements == null)
+        {
+            List<Requirement> reqs = new ArrayList<Requirement>();
+
+            // Wrap host requirements.
+            for (Requirement req : m_host.getRequirements(null))
+            {
+                reqs.add(new WrappedRequirement(this, req));
+            }
+
+            // Wrap fragment requirements.
+            if (m_fragments != null)
+            {
+                for (Resource fragment : m_fragments)
+                {
+                    for (Requirement req : fragment.getRequirements(null))
+                    {
+                        // Filter out host and execution environment requirements,
+                        // since they are not part of the fragment payload.
+                        if (!req.getNamespace().equals(HostNamespace.HOST_NAMESPACE)
+                            && !req.getNamespace().equals(
+                                ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE))
+                        {
+                            reqs.add(new WrappedRequirement(this, req));
+                        }
+                    }
+                }
+            }
+            m_cachedRequirements = Collections.unmodifiableList(reqs);
+        }
+        return m_cachedRequirements;
+    }
+
+    public String toString()
+    {
+        return m_host.toString();
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/BundleCapability.java b/resolver/src/main/java/org/apache/felix/resolver/test/BundleCapability.java
new file mode 100644
index 0000000..30e096d
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/BundleCapability.java
@@ -0,0 +1,79 @@
+/*
+ * 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.resolver.test;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+class BundleCapability implements Capability
+{
+    private final Resource m_resource;
+    private final Map<String, String> m_dirs;
+    private final Map<String, Object> m_attrs;
+
+    public BundleCapability(Resource resource, String name)
+    {
+        m_resource = resource;
+        m_dirs = new HashMap<String, String>();
+        m_attrs = new HashMap<String, Object>();
+        m_attrs.put(BundleNamespace.BUNDLE_NAMESPACE, name);
+    }
+
+    public String getNamespace()
+    {
+        return BundleNamespace.BUNDLE_NAMESPACE;
+    }
+
+    public void addDirective(String name, String value)
+    {
+        m_dirs.put(name, value);
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_dirs;
+    }
+
+    public void addAttribute(String name, Object value)
+    {
+        m_attrs.put(name, value);
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_attrs;
+    }
+
+    public Resource getResource()
+    {
+        return m_resource;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getNamespace() + "; "
+            + getAttributes().get(BundleNamespace.BUNDLE_NAMESPACE).toString();
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/BundleRequirement.java b/resolver/src/main/java/org/apache/felix/resolver/test/BundleRequirement.java
new file mode 100644
index 0000000..efc8b76
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/BundleRequirement.java
@@ -0,0 +1,71 @@
+/*
+ * 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.resolver.test;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+class BundleRequirement implements Requirement
+{
+    private final Resource m_resource;
+    private final Map<String, String> m_dirs;
+    private final Map<String, Object> m_attrs;
+
+    public BundleRequirement(Resource resource, String name)
+    {
+        m_resource = resource;
+        m_dirs = new HashMap<String, String>();
+        m_dirs.put(
+            BundleNamespace.REQUIREMENT_FILTER_DIRECTIVE,
+            "(" + BundleNamespace.BUNDLE_NAMESPACE + "=" + name + ")");
+        m_attrs = new HashMap<String, Object>();
+    }
+
+    public String getNamespace()
+    {
+        return BundleNamespace.BUNDLE_NAMESPACE;
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_dirs;
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_attrs;
+    }
+
+    public Resource getResource()
+    {
+        return m_resource;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getNamespace() + "; "
+            + getDirectives().get(BundleNamespace.REQUIREMENT_FILTER_DIRECTIVE).toString();
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/IdentityCapability.java b/resolver/src/main/java/org/apache/felix/resolver/test/IdentityCapability.java
new file mode 100644
index 0000000..033efdb
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/IdentityCapability.java
@@ -0,0 +1,67 @@
+/*
+ * 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.resolver.test;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+class IdentityCapability implements Capability
+{
+    private final Resource m_resource;
+    private final Map<String, String> m_dirs;
+    private final Map<String, Object> m_attrs;
+
+    public IdentityCapability(Resource resource, String name)
+    {
+        m_resource = resource;
+        m_dirs = new HashMap<String, String>();
+        m_attrs = new HashMap<String, Object>();
+        m_attrs.put(IdentityNamespace.IDENTITY_NAMESPACE, name);
+    }
+
+    public String getNamespace()
+    {
+        return IdentityNamespace.IDENTITY_NAMESPACE;
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_dirs;
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_attrs;
+    }
+
+    public Resource getResource()
+    {
+        return m_resource;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getNamespace() + "; "
+            + getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE).toString();
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/Main.java b/resolver/src/main/java/org/apache/felix/resolver/test/Main.java
new file mode 100644
index 0000000..6392e73
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/Main.java
@@ -0,0 +1,180 @@
+/*
+ * 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.resolver.test;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.felix.resolver.Logger;
+import org.apache.felix.resolver.ResolverImpl;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+import org.osgi.resource.Wiring;
+import org.osgi.service.resolver.ResolutionException;
+import org.osgi.service.resolver.Resolver;
+
+public class Main
+{
+    public static void main(String[] args) throws ResolutionException
+    {
+        Resolver resolver = new ResolverImpl(new Logger(Logger.LOG_DEBUG));
+
+        Map<Resource, Wiring> wirings = new HashMap<Resource, Wiring>();
+        Map<Requirement, List<Capability>> candMap = new HashMap<Requirement, List<Capability>>();
+
+        System.out.println("\nSCENARIO 1\n");
+        List<Resource> mandatory = populateScenario1(wirings, candMap);
+        ResolveContextImpl rci = new ResolveContextImpl(wirings, candMap, mandatory, Collections.EMPTY_LIST);
+        Map<Resource, List<Wire>> wireMap = resolver.resolve(rci);
+        System.out.println("RESULT " + wireMap);
+
+        System.out.println("\nSCENARIO 2\n");
+        mandatory = populateScenario2(wirings, candMap);
+        rci = new ResolveContextImpl(wirings, candMap, mandatory, Collections.EMPTY_LIST);
+        wireMap = resolver.resolve(rci);
+        System.out.println("RESULT " + wireMap);
+
+        System.out.println("\nSCENARIO 3\n");
+        mandatory = populateScenario3(wirings, candMap);
+        rci = new ResolveContextImpl(wirings, candMap, mandatory, Collections.EMPTY_LIST);
+        wireMap = resolver.resolve(rci);
+        System.out.println("RESULT " + wireMap);
+    }
+
+    private static List<Resource> populateScenario1(
+        Map<Resource, Wiring> wirings, Map<Requirement, List<Capability>> candMap)
+    {
+        wirings.clear();
+        candMap.clear();
+
+        ResourceImpl exporter = new ResourceImpl("A");
+        exporter.addCapability(new PackageCapability(exporter, "foo"));
+        ResourceImpl importer = new ResourceImpl("B");
+        importer.addRequirement(new PackageRequirement(importer, "foo"));
+        candMap.put(
+            importer.getRequirements(null).get(0),
+            exporter.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE));
+        List<Resource> resources = new ArrayList<Resource>();
+        resources.add(importer);
+        return resources;
+    }
+
+    private static List<Resource> populateScenario2(
+        Map<Resource, Wiring> wirings, Map<Requirement, List<Capability>> candMap)
+    {
+        wirings.clear();
+        candMap.clear();
+
+        List<Capability> fooCands = new ArrayList<Capability>();
+        List<Capability> barCands = new ArrayList<Capability>();
+
+        // A
+        ResourceImpl a = new ResourceImpl("A");
+        PackageCapability p = new PackageCapability(a, "foo");
+        a.addCapability(p);
+        fooCands.add(p);
+
+        // B
+        ResourceImpl b = new ResourceImpl("B");
+        p = new PackageCapability(b, "foo");
+        b.addCapability(p);
+        fooCands.add(p);
+
+        p = new PackageCapability(b, "bar");
+        p.addDirective(PackageNamespace.CAPABILITY_USES_DIRECTIVE, "foo");
+        b.addCapability(p);
+        barCands.add(p);
+
+        // C
+        ResourceImpl c = new ResourceImpl("C");
+        Requirement r = new PackageRequirement(c, "foo");
+        c.addRequirement(r);
+        candMap.put(r, fooCands);
+
+        r = new PackageRequirement(c, "bar");
+        c.addRequirement(r);
+        candMap.put(r, barCands);
+
+        // Mandatory resources
+        List<Resource> resources = new ArrayList<Resource>();
+        resources.add(c);
+        return resources;
+    }
+
+    private static List<Resource> populateScenario3(
+        Map<Resource, Wiring> wirings, Map<Requirement, List<Capability>> candMap)
+    {
+        wirings.clear();
+        candMap.clear();
+
+        List<Capability> resourcesCands = new ArrayList<Capability>();
+        List<Capability> dResourcesCands = new ArrayList<Capability>();
+        List<Capability> eBundleDCands = new ArrayList<Capability>();
+        List<Capability> eResourcesCands = new ArrayList<Capability>();
+
+        // B
+        ResourceImpl b = new ResourceImpl("B");
+        PackageCapability pc = new PackageCapability(b, "resources");
+        b.addCapability(pc);
+        eResourcesCands.add(pc);
+
+        // C
+        ResourceImpl c = new ResourceImpl("C");
+        pc = new PackageCapability(c, "resources");
+        c.addCapability(pc);
+        eResourcesCands.add(pc);
+        dResourcesCands.add(pc);
+
+        // D
+        ResourceImpl d = new ResourceImpl("D");
+        pc = new PackageCapability(d, "export");
+        pc.addDirective(Namespace.CAPABILITY_USES_DIRECTIVE, "resources");
+        d.addCapability(pc);
+
+        BundleCapability bc = new BundleCapability(d, "D");
+        bc.addDirective(Namespace.CAPABILITY_USES_DIRECTIVE, "resources");
+        d.addCapability(bc);
+        eBundleDCands.add(bc);
+
+        Requirement r = new PackageRequirement(d, "resources");
+        d.addRequirement(r);
+        candMap.put(r, dResourcesCands);
+
+        // E
+        ResourceImpl e = new ResourceImpl("E");
+        r = new BundleRequirement(e, "D");
+        e.addRequirement(r);
+        candMap.put(r, eBundleDCands);
+
+        r = new PackageRequirement(e, "resources");
+        e.addRequirement(r);
+        candMap.put(r, eResourcesCands);
+
+        // Mandatory resources
+        List<Resource> resources = new ArrayList<Resource>();
+        resources.add(e);
+        return resources;
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/PackageCapability.java b/resolver/src/main/java/org/apache/felix/resolver/test/PackageCapability.java
new file mode 100644
index 0000000..c672ada
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/PackageCapability.java
@@ -0,0 +1,78 @@
+/*
+ * 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.resolver.test;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+class PackageCapability implements Capability
+{
+    private final Resource m_resource;
+    private final Map<String, String> m_dirs;
+    private final Map<String, Object> m_attrs;
+
+    public PackageCapability(Resource resource, String name)
+    {
+        m_resource = resource;
+        m_dirs = new HashMap<String, String>();
+        m_attrs = new HashMap<String, Object>();
+        m_attrs.put(PackageNamespace.PACKAGE_NAMESPACE, name);
+    }
+
+    public String getNamespace()
+    {
+        return PackageNamespace.PACKAGE_NAMESPACE;
+    }
+
+    public void addDirective(String name, String value)
+    {
+        m_dirs.put(name, value);
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_dirs;
+    }
+
+    public void addAttribute(String name, Object value)
+    {
+        m_attrs.put(name, value);
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_attrs;
+    }
+
+    public Resource getResource()
+    {
+        return m_resource;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getNamespace() + "; "
+            + getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE).toString();
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/PackageRequirement.java b/resolver/src/main/java/org/apache/felix/resolver/test/PackageRequirement.java
new file mode 100644
index 0000000..eaf6a9b
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/PackageRequirement.java
@@ -0,0 +1,70 @@
+/*
+ * 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.resolver.test;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+class PackageRequirement implements Requirement
+{
+    private final Resource m_resource;
+    private final Map<String, String> m_dirs;
+    private final Map<String, Object> m_attrs;
+
+    public PackageRequirement(Resource resource, String name)
+    {
+        m_resource = resource;
+        m_dirs = new HashMap<String, String>();
+        m_dirs.put(
+            PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE,
+            "(" + PackageNamespace.PACKAGE_NAMESPACE + "=" + name + ")");
+        m_attrs = new HashMap<String, Object>();
+    }
+
+    public String getNamespace()
+    {
+        return PackageNamespace.PACKAGE_NAMESPACE;
+    }
+
+    public Map<String, String> getDirectives()
+    {
+        return m_dirs;
+    }
+
+    public Map<String, Object> getAttributes()
+    {
+        return m_attrs;
+    }
+
+    public Resource getResource()
+    {
+        return m_resource;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getNamespace() + "; "
+            + getDirectives().get(PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE).toString();
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/ResolveContextImpl.java b/resolver/src/main/java/org/apache/felix/resolver/test/ResolveContextImpl.java
new file mode 100644
index 0000000..2a5f112
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/ResolveContextImpl.java
@@ -0,0 +1,86 @@
+/*
+ * 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.resolver.test;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wiring;
+import org.osgi.service.resolver.HostedCapability;
+import org.osgi.service.resolver.ResolveContext;
+
+class ResolveContextImpl extends ResolveContext
+{
+    private final Map<Resource, Wiring> m_wirings;
+    private final Map<Requirement, List<Capability>> m_candMap;
+    private final Collection<Resource> m_mandatory;
+    private final Collection<Resource> m_optional;
+
+    public ResolveContextImpl(
+        Map<Resource, Wiring> wirings, Map<Requirement, List<Capability>> candMap,
+        Collection<Resource> mandatory, Collection<Resource> optional)
+    {
+        m_wirings = wirings;
+        m_candMap = candMap;
+        m_mandatory = mandatory;
+        m_optional = optional;
+    }
+
+    @Override
+    public Collection<Resource> getMandatoryResources()
+    {
+        return new ArrayList<Resource>(m_mandatory);
+    }
+
+    @Override
+    public Collection<Resource> getOptionalResources()
+    {
+        return new ArrayList<Resource>(m_optional);
+    }
+
+    @Override
+    public List<Capability> findProviders(Requirement r)
+    {
+        return new ArrayList<Capability>(m_candMap.get(r));
+    }
+
+    @Override
+    public int insertHostedCapability(List<Capability> capabilities, HostedCapability hostedCapability)
+    {
+        int idx = 0;
+        capabilities.add(idx, hostedCapability);
+        return idx;
+    }
+
+    @Override
+    public boolean isEffective(Requirement requirement)
+    {
+        return true;
+    }
+
+    @Override
+    public Map<Resource, Wiring> getWirings()
+    {
+        return m_wirings;
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/apache/felix/resolver/test/ResourceImpl.java b/resolver/src/main/java/org/apache/felix/resolver/test/ResourceImpl.java
new file mode 100644
index 0000000..7971f97
--- /dev/null
+++ b/resolver/src/main/java/org/apache/felix/resolver/test/ResourceImpl.java
@@ -0,0 +1,89 @@
+/*
+ * 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.resolver.test;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+
+public class ResourceImpl implements Resource
+{
+    private final List<Capability> m_caps;
+    private final List<Requirement> m_reqs;
+
+    public ResourceImpl(String name)
+    {
+        m_caps = new ArrayList<Capability>();
+        m_caps.add(0, new IdentityCapability(this, name));
+        m_reqs = new ArrayList<Requirement>();
+    }
+
+    public void addCapability(Capability cap)
+    {
+        m_caps.add(cap);
+    }
+
+    public List<Capability> getCapabilities(String namespace)
+    {
+        List<Capability> result = m_caps;
+        if (namespace != null)
+        {
+            result = new ArrayList<Capability>();
+            for (Capability cap : m_caps)
+            {
+                if (cap.getNamespace().equals(namespace))
+                {
+                    result.add(cap);
+                }
+            }
+        }
+        return result;
+    }
+
+    public void addRequirement(Requirement req)
+    {
+        m_reqs.add(req);
+    }
+
+    public List<Requirement> getRequirements(String namespace)
+    {
+        List<Requirement> result = m_reqs;
+        if (namespace != null)
+        {
+            result = new ArrayList<Requirement>();
+            for (Requirement req : m_reqs)
+            {
+                if (req.getNamespace().equals(namespace))
+                {
+                    result.add(req);
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public String toString()
+    {
+        return getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE).get(0).toString();
+    }
+}
\ No newline at end of file
diff --git a/resolver/src/main/java/org/osgi/service/resolver/HostedCapability.java b/resolver/src/main/java/org/osgi/service/resolver/HostedCapability.java
new file mode 100644
index 0000000..3c176de
--- /dev/null
+++ b/resolver/src/main/java/org/osgi/service/resolver/HostedCapability.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) OSGi Alliance (2012). All Rights Reserved.
+ * 
+ * Licensed 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.osgi.service.resolver;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+/**
+ * A capability hosted by a resource.
+ * 
+ * <p>
+ * A HostedCapability is a Capability where the {@link #getResource()} method
+ * returns a Resource that hosts this Capability instead of declaring it. This
+ * is necessary for cases where the declaring Resource of a Capability does not
+ * match the runtime state. For example, this is the case for fragments attached
+ * to a host. Most fragment declared capabilities and requirements become hosted
+ * by the host resource. Since a fragment can attach to multiple hosts, a single
+ * capability can actually be hosted multiple times.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: db698baa07e2ee8b5467871239adb5f0806dc183 $
+ */
+public interface HostedCapability extends Capability {
+
+	/**
+	 * Return the Resource that hosts this Capability.
+	 * 
+	 * @return The Resource that hosts this Capability.
+	 */
+	Resource getResource();
+
+	/**
+	 * Return the Capability hosted by the Resource.
+	 * 
+	 * @return The Capability hosted by the Resource.
+	 */
+	Capability getDeclaredCapability();
+}
diff --git a/resolver/src/main/java/org/osgi/service/resolver/ResolutionException.java b/resolver/src/main/java/org/osgi/service/resolver/ResolutionException.java
new file mode 100644
index 0000000..a626fc9
--- /dev/null
+++ b/resolver/src/main/java/org/osgi/service/resolver/ResolutionException.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ * 
+ * Licensed 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.osgi.service.resolver;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import org.osgi.resource.Requirement;
+
+/**
+ * Indicates failure to resolve a set of requirements.
+ * 
+ * <p>
+ * If a resolution failure is caused by a missing mandatory dependency a
+ * resolver may include any requirements it has considered in the resolution
+ * exception. Clients may access this set of dependencies via the
+ * {@link #getUnresolvedRequirements()} method.
+ * 
+ * <p>
+ * Resolver implementations may extend this class to provide extra state
+ * information about the reason for the resolution failure.
+ * 
+ * @version $Id: 42e5773e3b7e240673874329e5d9e705d0b698c5 $
+ */
+public class ResolutionException extends Exception {
+
+	private static final long				serialVersionUID	= 1L;
+
+	private final Collection<Requirement>	unresolvedRequirements;
+
+	/**
+	 * Create a {@code ResolutionException} with the specified message, cause
+	 * and unresolved requirements.
+	 * 
+	 * @param message The message.
+	 * @param cause The cause of this exception.
+	 * @param unresolvedRequirements The unresolved mandatory requirements from
+	 *        mandatory resources or {@code null} if no unresolved requirements
+	 *        information is provided.
+	 */
+	public ResolutionException(String message, Throwable cause, Collection<Requirement> unresolvedRequirements) {
+		super(message, cause);
+		if ((unresolvedRequirements == null) || unresolvedRequirements.isEmpty()) {
+			this.unresolvedRequirements = emptyCollection();
+		} else {
+			this.unresolvedRequirements = Collections.unmodifiableCollection(new ArrayList<Requirement>(unresolvedRequirements));
+		}
+	}
+
+	/**
+	 * Create a {@code ResolutionException} with the specified message.
+	 * 
+	 * @param message The message.
+	 */
+	public ResolutionException(String message) {
+		super(message);
+		unresolvedRequirements = emptyCollection();
+	}
+
+	/**
+	 * Create a {@code ResolutionException} with the specified cause.
+	 * 
+	 * @param cause The cause of this exception.
+	 */
+	public ResolutionException(Throwable cause) {
+		super(cause);
+		unresolvedRequirements = emptyCollection();
+	}
+
+	private static <T> Collection<T> emptyCollection() {
+		return Collections.EMPTY_LIST;
+	}
+
+	/**
+	 * Return the unresolved requirements, if any, for this exception.
+	 * 
+	 * <p>
+	 * The unresolved requirements are provided for informational purposes and
+	 * the specific set of unresolved requirements that are provided after a
+	 * resolve failure is not defined.
+	 * 
+	 * @return A collection of the unresolved requirements for this exception.
+	 *         The returned collection may be empty if no unresolved
+	 *         requirements information is provided.
+	 */
+	public Collection<Requirement> getUnresolvedRequirements() {
+		return unresolvedRequirements;
+	}
+}
diff --git a/resolver/src/main/java/org/osgi/service/resolver/ResolveContext.java b/resolver/src/main/java/org/osgi/service/resolver/ResolveContext.java
new file mode 100644
index 0000000..5dac453
--- /dev/null
+++ b/resolver/src/main/java/org/osgi/service/resolver/ResolveContext.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) OSGi Alliance (2011, 2012). All Rights Reserved.
+ *
+ * Licensed 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.osgi.service.resolver;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wiring;
+
+/**
+ * A resolve context provides resources, options and constraints to the
+ * potential solution of a {@link Resolver#resolve(ResolveContext) resolve}
+ * operation.
+ * 
+ * <p>
+ * Resolve Contexts:
+ * <ul>
+ * <li>Specify the mandatory and optional resources to resolve. The mandatory
+ * and optional resources must be consistent and correct. For example, they must
+ * not violate the singleton policy of the implementer.</li>
+ * <li>Provide {@link Capability capabilities} that the Resolver can use to
+ * satisfy {@link Requirement requirements} via the
+ * {@link #findProviders(Requirement)} method</li>
+ * <li>Constrain solutions via the {@link #getWirings()} method. A wiring
+ * consists of a map of existing {@link Resource resources} to {@link Wiring
+ * wiring}.</li>
+ * <li>Filter requirements that are part of a resolve operation via the
+ * {@link #isEffective(Requirement)}.</li>
+ * </ul>
+ * 
+ * <p>
+ * A resolver may call the methods on the resolve context any number of times
+ * during a resolve operation using any thread. Implementors should ensure that
+ * this class is properly thread safe.
+ * 
+ * <p>
+ * Except for {@link #insertHostedCapability(List, HostedCapability)}, the
+ * resolve context methods must be <i>idempotent</i>. This means that resources
+ * must have constant capabilities and requirements and the resolve context must
+ * return a consistent set of capabilities, wires and effective requirements.
+ * 
+ * @ThreadSafe
+ * @version $Id: f92eae32ab6fadb25e13d226458d6af50e8dcbba $
+ */
+public abstract class ResolveContext {
+	/**
+	 * Return the resources that must be resolved for this resolve context.
+	 * 
+	 * <p>
+	 * The default implementation returns an empty collection.
+	 * 
+	 * @return The resources that must be resolved for this resolve context. May
+	 *         be empty if there are no mandatory resources.
+	 */
+	public Collection<Resource> getMandatoryResources() {
+		return emptyCollection();
+	}
+
+	/**
+	 * Return the resources that the resolver should attempt to resolve for this
+	 * resolve context. Inability to resolve one of the specified resources will
+	 * not result in a resolution exception.
+	 * 
+	 * <p>
+	 * The default implementation returns an empty collection.
+	 * 
+	 * @return The resources that the resolver should attempt to resolve for
+	 *         this resolve context. May be empty if there are no mandatory
+	 *         resources.
+	 */
+	public Collection<Resource> getOptionalResources() {
+		return emptyCollection();
+	}
+
+	private static <T> Collection<T> emptyCollection() {
+		return Collections.EMPTY_LIST;
+	}
+
+	/**
+	 * Find Capabilities that match the given Requirement.
+	 * <p>
+	 * The returned list contains {@link Capability} objects where the Resource
+	 * must be the declared Resource of the Capability. The Resolver can then
+	 * add additional {@link HostedCapability} objects with the
+	 * {@link #insertHostedCapability(List, HostedCapability)} method when it,
+	 * for example, attaches fragments. Those {@link HostedCapability} objects
+	 * will then use the host's Resource which likely differs from the declared
+	 * Resource of the corresponding Capability.
+	 * 
+	 * <p>
+	 * The returned list is in priority order such that the Capabilities with a
+	 * lower index have a preference over those with a higher index. The
+	 * resolver must use the
+	 * {@link #insertHostedCapability(List, HostedCapability)} method to add
+	 * additional Capabilities to maintain priority order. In general, this is
+	 * necessary when the Resolver uses Capabilities declared in a Resource but
+	 * that must originate from an attached host.
+	 * 
+	 * <p>
+	 * Each returned Capability must match the given Requirement. This implies
+	 * that the filter in the Requirement must match as well as any namespace
+	 * specific directives. For example, the mandatory attributes for the
+	 * {@code osgi.wiring.package} namespace.
+	 * 
+	 * @param requirement The requirement that a resolver is attempting to
+	 *        satisfy. Must not be {@code null}.
+	 * @return A list of {@link Capability} objects that match the specified
+	 *         requirement.
+	 */
+	public abstract List<Capability> findProviders(Requirement requirement);
+
+	/**
+	 * Add a {@link HostedCapability} to the list of capabilities returned from
+	 * {@link #findProviders(Requirement)}.
+	 * 
+	 * <p>
+	 * This method is used by the {@link Resolver} to add Capabilities that are
+	 * hosted by another Resource to the list of Capabilities returned from
+	 * {@link #findProviders(Requirement)}. This function is necessary to allow
+	 * fragments to attach to hosts, thereby changing the origin of a
+	 * Capability. This method must insert the specified HostedCapability in a
+	 * place that makes the list maintain the preference order. It must return
+	 * the index in the list of the inserted {@link HostedCapability}.
+	 * 
+	 * @param capabilities The list returned from
+	 *        {@link #findProviders(Requirement)}. Must not be {@code null}.
+	 * @param hostedCapability The HostedCapability to insert in the specified
+	 *        list. Must not be {@code null}.
+	 * @return The index in the list of the inserted HostedCapability.
+	 * 
+	 */
+	public abstract int insertHostedCapability(List<Capability> capabilities, HostedCapability hostedCapability);
+
+	/**
+	 * Test if a given requirement should be wired in the resolve operation. If
+	 * this method returns {@code false}, then the resolver should ignore this
+	 * requirement during the resolve operation.
+	 * 
+	 * <p>
+	 * The primary use case for this is to test the {@code effective} directive
+	 * on the requirement, though implementations are free to use any effective
+	 * test.
+	 * 
+	 * @param requirement The Requirement to test. Must not be {@code null}.
+	 * @return {@code true} if the requirement should be considered as part of
+	 *         the resolve operation.
+	 */
+	public abstract boolean isEffective(Requirement requirement);
+
+	/**
+	 * Returns the wirings for existing resolved resources.
+	 * 
+	 * <p>
+	 * For example, if this resolve context is for an OSGi framework, then the
+	 * result would contain all the currently resolved bundles with each
+	 * bundle's current wiring.
+	 * 
+	 * <p>
+	 * Multiple calls to this method for this resolve context must return the
+	 * same result.
+	 * 
+	 * @return The wirings for existing resolved resources. The returned map is
+	 *         unmodifiable.
+	 */
+	public abstract Map<Resource, Wiring> getWirings();
+}
diff --git a/resolver/src/main/java/org/osgi/service/resolver/Resolver.java b/resolver/src/main/java/org/osgi/service/resolver/Resolver.java
new file mode 100644
index 0000000..d451ade
--- /dev/null
+++ b/resolver/src/main/java/org/osgi/service/resolver/Resolver.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) OSGi Alliance (2006, 2012). All Rights Reserved.
+ *
+ * Licensed 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.
+ */
+
+// This document is an experimental draft to enable interoperability
+// between bundle repositories. There is currently no commitment to
+// turn this draft into an official specification.
+
+package org.osgi.service.resolver;
+
+import java.util.List;
+import java.util.Map;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
+
+/**
+ * A resolver service resolves the specified resources in the context supplied
+ * by the caller.
+ * 
+ * @ThreadSafe
+ * @noimplement
+ * @version $Id: dfb89b8d09af62ecf62321b80d7e2310512f27a1 $
+ */
+public interface Resolver {
+	/**
+	 * Resolve the specified resolve context and return any new resources and
+	 * wires to the caller.
+	 * 
+	 * <p>
+	 * The resolver considers two groups of resources:
+	 * <ul>
+	 * <li>Mandatory - any resource in the
+	 * {@link ResolveContext#getMandatoryResources() mandatory group} must be
+	 * resolved. A failure to satisfy any mandatory requirement for these
+	 * resources will result in throwing a {@link ResolutionException}</li>
+	 * <li>Optional - any resource in the
+	 * {@link ResolveContext#getOptionalResources() optional group} may be
+	 * resolved. A failure to satisfy a mandatory requirement for a resource in
+	 * this group will not fail the overall resolution but no resources or wires
+	 * will be returned for that resource.</li>
+	 * </ul>
+	 * 
+	 * <p>
+	 * The resolve method returns the delta between the start state defined by
+	 * {@link ResolveContext#getWirings()} and the end resolved state. That is,
+	 * only new resources and wires are included.
+	 * 
+	 * <p>
+	 * The behavior of the resolver is not defined if the specified resolve
+	 * context supplies inconsistent information.
+	 * 
+	 * @param context The resolve context for the resolve operation. Must not be
+	 *        {@code null}.
+	 * @return The new resources and wires required to satisfy the specified
+	 *         resolve context. The returned map is the property of the caller
+	 *         and can be modified by the caller.
+	 * @throws ResolutionException If the resolution cannot be satisfied.
+	 */
+	Map<Resource, List<Wire>> resolve(ResolveContext context) throws ResolutionException;
+}
diff --git a/resolver/src/main/java/org/osgi/service/resolver/package-info.java b/resolver/src/main/java/org/osgi/service/resolver/package-info.java
new file mode 100644
index 0000000..d361a48
--- /dev/null
+++ b/resolver/src/main/java/org/osgi/service/resolver/package-info.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) OSGi Alliance (2010, 2012). All Rights Reserved.
+ * 
+ * Licensed 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.
+ */
+
+/**
+ * Resolver Service Package Version 1.0.
+ * 
+ * <p>
+ * Bundles wishing to use this package must list the package in the
+ * Import-Package header of the bundle's manifest. This package has two types of
+ * users: the consumers that use the API in this package and the providers that
+ * implement the API in this package.
+ * 
+ * <p>
+ * Example import for consumers using the API in this package:
+ * <p>
+ * {@code  Import-Package: org.osgi.service.resolver; version="[1.0,2.0)"}
+ * <p>
+ * Example import for providers implementing the API in this package:
+ * <p>
+ * {@code  Import-Package: org.osgi.service.resolver; version="[1.0,1.1)"}
+ * 
+ * @version $Id: db1706d83ca104187f77cb1feb7cf52b92b3740d $
+ */
+
+package org.osgi.service.resolver;
+
diff --git a/resolver/src/main/java/org/osgi/service/resolver/packageinfo b/resolver/src/main/java/org/osgi/service/resolver/packageinfo
new file mode 100644
index 0000000..7c8de03
--- /dev/null
+++ b/resolver/src/main/java/org/osgi/service/resolver/packageinfo
@@ -0,0 +1 @@
+version 1.0