You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2013/04/04 11:43:37 UTC

svn commit: r1464402 [8/11] - in /ace/trunk: org.apache.ace.deployment.api/ org.apache.ace.deployment.deploymentadmin/ org.apache.ace.deployment.itest/ org.apache.ace.deployment.itest/src/org/apache/ace/it/deployment/ org.apache.ace.deployment.provider...

Added: ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverImpl.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverImpl.java (added)
+++ ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverImpl.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,1781 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import java.util.ArrayList;
+import java.util.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.SortedSet;
+//import org.apache.felix.framework.BundleWiringImpl;
+import org.apache.felix.framework.Logger;
+import org.apache.felix.framework.capabilityset.CapabilitySet;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.wiring.BundleCapabilityImpl;
+import org.apache.felix.framework.wiring.BundleRequirementImpl;
+import org.osgi.framework.Constants;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+
+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<BundleRevision, List<ResolverWire>> resolve(
+        ResolverState state,
+        Set<BundleRevision> mandatoryRevisions,
+        Set<BundleRevision> optionalRevisions,
+        Set<BundleRevision> ondemandFragments)
+    {
+        Map<BundleRevision, List<ResolverWire>> wireMap =
+            new HashMap<BundleRevision, List<ResolverWire>>();
+        Map<BundleRevision, Packages> revisionPkgMap =
+            new HashMap<BundleRevision, Packages>();
+
+        boolean retry;
+        do
+        {
+            retry = false;
+
+            try
+            {
+                // Create object to hold all candidates.
+                Candidates allCandidates = new Candidates();
+
+                // Populate mandatory revisions; since these are mandatory
+                // revisions, failure throws a resolve exception.
+                for (Iterator<BundleRevision> it = mandatoryRevisions.iterator();
+                    it.hasNext(); )
+                {
+                    BundleRevision br = it.next();
+                    if (Util.isFragment(br) || (br.getWiring() == null))
+                    {
+                        allCandidates.populate(state, br, Candidates.MANDATORY);
+                    }
+                    else
+                    {
+                        it.remove();
+                    }
+                }
+
+                // Populate optional revisions; since these are optional
+                // revisions, failure does not throw a resolve exception.
+                for (BundleRevision br : optionalRevisions)
+                {
+                    boolean isFragment = Util.isFragment(br);
+                    if (isFragment || (br.getWiring() == null))
+                    {
+                        allCandidates.populate(state, br, Candidates.OPTIONAL);
+                    }
+                }
+
+                // Populate ondemand fragments; since these are optional
+                // revisions, failure does not throw a resolve exception.
+                for (BundleRevision br : ondemandFragments)
+                {
+                    boolean isFragment = Util.isFragment(br);
+                    if (isFragment)
+                    {
+                        allCandidates.populate(state, br, Candidates.ON_DEMAND);
+                    }
+                }
+
+                // Merge any fragments into hosts.
+                allCandidates.prepare(getResolvedSingletons(state));
+
+                // Create a combined list of populated revisions; for
+                // optional revisions. We do not need to consider ondemand
+                // fragments, since they will only be pulled in if their
+                // host is already present.
+                Set<BundleRevision> allRevisions =
+                    new HashSet<BundleRevision>(mandatoryRevisions);
+                for (BundleRevision br : optionalRevisions)
+                {
+                    if (allCandidates.isPopulated(br))
+                    {
+                        allRevisions.add(br);
+                    }
+                }
+
+                // Record the initial candidate permutation.
+                m_usesPermutations.add(allCandidates);
+
+                ResolveException rethrow = null;
+
+                // If a populated revision is a fragment, then its host
+                // must ultimately be verified, so store its host requirement
+                // to use for package space calculation.
+                Map<BundleRevision, List<BundleRequirement>> hostReqs =
+                    new HashMap<BundleRevision, List<BundleRequirement>>();
+                for (BundleRevision br : allRevisions)
+                {
+                    if (Util.isFragment(br))
+                    {
+                        hostReqs.put(
+                            br,
+                            br.getDeclaredRequirements(BundleRevision.HOST_NAMESPACE));
+                    }
+                }
+
+                do
+                {
+                    rethrow = null;
+
+                    revisionPkgMap.clear();
+                    m_packageSourcesCache.clear();
+
+                    allCandidates = (m_usesPermutations.size() > 0)
+                        ? m_usesPermutations.remove(0)
+                        : m_importPermutations.remove(0);
+//allCandidates.dump();
+
+                    for (BundleRevision br : allRevisions)
+                    {
+                        BundleRevision target = br;
+
+                        // If we are resolving a fragment, then get its
+                        // host candidate and verify it instead.
+                        List<BundleRequirement> hostReq = hostReqs.get(br);
+                        if (hostReq != null)
+                        {
+                            target = allCandidates.getCandidates(hostReq.get(0))
+                                .iterator().next().getRevision();
+                        }
+
+                        calculatePackageSpaces(
+                            allCandidates.getWrappedHost(target), allCandidates, revisionPkgMap,
+                            new HashMap(), new HashSet());
+//System.out.println("+++ PACKAGE SPACES START +++");
+//dumpRevisionPkgMap(revisionPkgMap);
+//System.out.println("+++ PACKAGE SPACES END +++");
+
+                        try
+                        {
+                            checkPackageSpaceConsistency(
+                                false, allCandidates.getWrappedHost(target),
+                                allCandidates, revisionPkgMap, new HashMap());
+                        }
+                        catch (ResolveException ex)
+                        {
+                            rethrow = ex;
+                        }
+                    }
+                }
+                while ((rethrow != null)
+                    && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
+
+                // If there is a resolve exception, then determine if an
+                // optionally resolved revision is to blame (typically a fragment).
+                // If so, then remove the optionally resolved resolved and try
+                // again; otherwise, rethrow the resolve exception.
+                if (rethrow != null)
+                {
+                    BundleRevision faultyRevision =
+                        getActualBundleRevision(rethrow.getRevision());
+                    if (rethrow.getRequirement() instanceof HostedRequirement)
+                    {
+                        faultyRevision =
+                            ((HostedRequirement) rethrow.getRequirement())
+                                .getOriginalRequirement().getRevision();
+                    }
+                    if (optionalRevisions.remove(faultyRevision))
+                    {
+                        retry = true;
+                    }
+                    else if (ondemandFragments.remove(faultyRevision))
+                    {
+                        retry = true;
+                    }
+                    else
+                    {
+                        throw rethrow;
+                    }
+                }
+                // If there is no exception to rethrow, then this was a clean
+                // resolve, so populate the wire map.
+                else
+                {
+                    for (BundleRevision br : allRevisions)
+                    {
+                        BundleRevision target = br;
+
+                        // If we are resolving a fragment, then we
+                        // actually want to populate its host's wires.
+                        List<BundleRequirement> hostReq = hostReqs.get(br);
+                        if (hostReq != null)
+                        {
+                            target = allCandidates.getCandidates(hostReq.get(0))
+                                .iterator().next().getRevision();
+                        }
+
+                        if (allCandidates.isPopulated(target))
+                        {
+                            wireMap =
+                                populateWireMap(
+                                    allCandidates.getWrappedHost(target),
+                                    revisionPkgMap, wireMap, allCandidates);
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                // Always clear the state.
+                m_usesPermutations.clear();
+                m_importPermutations.clear();
+            }
+        }
+        while (retry);
+
+        return wireMap;
+    }
+
+    public Map<BundleRevision, List<ResolverWire>> resolve(
+        ResolverState state, BundleRevision revision, String pkgName,
+        Set<BundleRevision> ondemandFragments)
+    {
+        // We can only create a dynamic import if the following
+        // conditions are met:
+        // 1. The specified revision 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 revision.
+        // 5. The package in question matches a dynamic import of the revision.
+        // The following call checks all of these conditions and returns
+        // the associated dynamic import and matching capabilities.
+        Candidates allCandidates =
+            getDynamicImportCandidates(state, revision, pkgName);
+        if (allCandidates != null)
+        {
+            Map<BundleRevision, List<ResolverWire>> wireMap = new HashMap<BundleRevision, List<ResolverWire>>();
+            Map<BundleRevision, Packages> revisionPkgMap = new HashMap<BundleRevision, Packages>();
+
+            boolean retry;
+            do
+            {
+                retry = false;
+
+                try
+                {
+                    // Try to populate optional fragments.
+                    for (BundleRevision br : ondemandFragments)
+                    {
+                        if (Util.isFragment(br))
+                        {
+                            allCandidates.populate(state, br, Candidates.ON_DEMAND);
+                        }
+                    }
+
+                    // Merge any fragments into hosts.
+                    allCandidates.prepare(getResolvedSingletons(state));
+
+                    // Record the initial candidate permutation.
+                    m_usesPermutations.add(allCandidates);
+
+                    ResolveException rethrow = null;
+
+                    do
+                    {
+                        rethrow = null;
+
+                        revisionPkgMap.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 revision
+                        // 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(revision), allCandidates, revisionPkgMap,
+                            new HashMap(), new HashSet());
+//System.out.println("+++ PACKAGE SPACES START +++");
+//dumpRevisionPkgMap(revisionPkgMap);
+//System.out.println("+++ PACKAGE SPACES END +++");
+
+                        try
+                        {
+                            checkPackageSpaceConsistency(
+                                false, allCandidates.getWrappedHost(revision),
+                                allCandidates, revisionPkgMap, new HashMap());
+                        }
+                        catch (ResolveException ex)
+                        {
+                            rethrow = ex;
+                        }
+                    }
+                    while ((rethrow != null)
+                        && ((m_usesPermutations.size() > 0) || (m_importPermutations.size() > 0)));
+
+                    // If there is a resolve exception, then determine if an
+                    // optionally resolved revision is to blame (typically a fragment).
+                    // If so, then remove the optionally resolved revision and try
+                    // again; otherwise, rethrow the resolve exception.
+                    if (rethrow != null)
+                    {
+                        BundleRevision faultyRevision =
+                            getActualBundleRevision(rethrow.getRevision());
+                        if (rethrow.getRequirement() instanceof HostedRequirement)
+                        {
+                            faultyRevision =
+                                ((HostedRequirement) rethrow.getRequirement())
+                                    .getOriginalRequirement().getRevision();
+                        }
+                        if (ondemandFragments.remove(faultyRevision))
+                        {
+                            retry = true;
+                        }
+                        else
+                        {
+                            throw rethrow;
+                        }
+                    }
+                    // If there is no exception to rethrow, then this was a clean
+                    // resolve, so populate the wire map.
+                    else
+                    {
+                        wireMap = populateDynamicWireMap(
+                            revision, pkgName, revisionPkgMap, wireMap, allCandidates);
+                        return wireMap;
+                    }
+                }
+                finally
+                {
+                    // Always clear the state.
+                    m_usesPermutations.clear();
+                    m_importPermutations.clear();
+                }
+            }
+            while (retry);
+        }
+
+        return null;
+    }
+
+    private static List<BundleRevision> getResolvedSingletons(ResolverState state)
+    {
+        BundleRequirementImpl req = new BundleRequirementImpl(
+            null,
+            BundleCapabilityImpl.SINGLETON_NAMESPACE,
+            Collections.EMPTY_MAP,
+            Collections.EMPTY_MAP);
+        SortedSet<BundleCapability> caps = state.getCandidates(req, true);
+        List<BundleRevision> singletons = new ArrayList();
+        for (BundleCapability cap : caps)
+        {
+            if (cap.getRevision().getWiring() != null)
+            {
+                singletons.add(cap.getRevision());
+            }
+        }
+        return singletons;
+    }
+
+    private static Candidates getDynamicImportCandidates(
+        ResolverState state, BundleRevision revision, String pkgName)
+    {
+        // Unresolved revisions cannot dynamically import, nor can the default
+        // package be dynamically imported.
+        if ((revision.getWiring() == null) || pkgName.length() == 0)
+        {
+            return null;
+        }
+
+        // If the revision doesn't have dynamic imports, then just return
+        // immediately.
+        List<BundleRequirement> dynamics =
+            Util.getDynamicRequirements(revision.getWiring().getRequirements(null));
+        if ((dynamics == null) || dynamics.isEmpty())
+        {
+            return null;
+        }
+
+        // If the revision exports this package, then we cannot
+        // attempt to dynamically import it.
+        for (BundleCapability cap : revision.getWiring().getCapabilities(null))
+        {
+            if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                && cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
+            {
+                return null;
+            }
+        }
+
+        // If this revision already imports or requires this package, then
+        // we cannot dynamically import it.
+        /* TODO: karl - is this correct? 
+         * if (((BundleWiringImpl) revision.getWiring()).hasPackageSource(pkgName))
+         * {
+         *  return null;
+         * }
+         */
+
+        // Determine if any providers of the package exist.
+        Map<String, Object> attrs = Collections.singletonMap(
+            BundleRevision.PACKAGE_NAMESPACE, (Object) pkgName);
+        BundleRequirementImpl req = new BundleRequirementImpl(
+            revision,
+            BundleRevision.PACKAGE_NAMESPACE,
+            Collections.EMPTY_MAP,
+            attrs);
+        SortedSet<BundleCapability> candidates = state.getCandidates(req, false);
+
+        // Try to find a dynamic requirement that matches the capabilities.
+        BundleRequirementImpl dynReq = null;
+        for (int dynIdx = 0;
+            (candidates.size() > 0) && (dynReq == null) && (dynIdx < dynamics.size());
+            dynIdx++)
+        {
+            for (Iterator<BundleCapability> itCand = candidates.iterator();
+                (dynReq == null) && itCand.hasNext(); )
+            {
+                BundleCapability cap = itCand.next();
+                if (CapabilitySet.matches(
+                    (BundleCapabilityImpl) cap,
+                    ((BundleRequirementImpl) dynamics.get(dynIdx)).getFilter()))
+                {
+                    dynReq = (BundleRequirementImpl) 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<BundleCapability> itCand = candidates.iterator();
+                itCand.hasNext(); )
+            {
+                BundleCapability cap = itCand.next();
+                if (!CapabilitySet.matches(
+                    (BundleCapabilityImpl) cap, dynReq.getFilter()))
+                {
+                    itCand.remove();
+                }
+            }
+        }
+        else
+        {
+            candidates.clear();
+        }
+
+        Candidates allCandidates = null;
+
+        if (candidates.size() > 0)
+        {
+            allCandidates = new Candidates();
+            allCandidates.populateDynamic(state, revision, dynReq, candidates);
+        }
+
+        return allCandidates;
+    }
+
+    private void calculatePackageSpaces(
+        BundleRevision revision,
+        Candidates allCandidates,
+        Map<BundleRevision, Packages> revisionPkgMap,
+        Map<BundleCapability, List<BundleRevision>> usesCycleMap,
+        Set<BundleRevision> cycle)
+    {
+        if (cycle.contains(revision))
+        {
+            return;
+        }
+        cycle.add(revision);
+
+        // Create parallel arrays for requirement and proposed candidate
+        // capability or actual capability if revision is resolved or not.
+        List<BundleRequirement> reqs = new ArrayList();
+        List<BundleCapability> caps = new ArrayList();
+        boolean isDynamicImporting = false;
+        if (revision.getWiring() != null)
+        {
+            // Use wires to get actual requirements and satisfying capabilities.
+            for (BundleWire wire : revision.getWiring().getRequiredWires(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.
+                BundleRequirement r = wire.getRequirement();
+                if (!r.getRevision().equals(wire.getRequirerWiring().getRevision())
+                    || ((r.getDirectives().get(Constants.RESOLUTION_DIRECTIVE) != null)
+                        && r.getDirectives().get(Constants.RESOLUTION_DIRECTIVE).equals("dynamic")))
+                {
+                    r = new HostedRequirement(
+                        wire.getRequirerWiring().getRevision(),
+                        (BundleRequirementImpl) r);
+                }
+                // Wrap the capability as a hosted capability if it comes
+                // from a fragment, since we will need to know the host.
+                BundleCapability c = wire.getCapability();
+                if (!c.getRevision().equals(wire.getProviderWiring().getRevision()))
+                {
+                    c = new HostedCapability(
+                        wire.getProviderWiring().getRevision(),
+                        (BundleCapabilityImpl) c);
+                }
+                reqs.add(r);
+                caps.add(c);
+            }
+
+            // Since the revision is resolved, it could be dynamically importing,
+            // so check to see if there are candidates for any of its dynamic
+            // imports.
+            for (BundleRequirement req
+                : Util.getDynamicRequirements(revision.getWiring().getRequirements(null)))
+            {
+                // Get the candidates for the current requirement.
+                SortedSet<BundleCapability> candCaps =
+                    allCandidates.getCandidates((BundleRequirementImpl) req);
+                // Optional requirements may not have any candidates.
+                if (candCaps == null)
+                {
+                    continue;
+                }
+
+                BundleCapability cap = candCaps.iterator().next();
+                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 (BundleRequirement req : revision.getDeclaredRequirements(null))
+            {
+                String resolution = req.getDirectives().get(Constants.RESOLUTION_DIRECTIVE);
+                if ((resolution == null)
+                    || !resolution.equals(FelixConstants.RESOLUTION_DYNAMIC))
+                {
+                    // Get the candidates for the current requirement.
+                    SortedSet<BundleCapability> candCaps =
+                        allCandidates.getCandidates((BundleRequirementImpl) req);
+                    // Optional requirements may not have any candidates.
+                    if (candCaps == null)
+                    {
+                        continue;
+                    }
+
+                    BundleCapability cap = candCaps.iterator().next();
+                    reqs.add(req);
+                    caps.add(cap);
+                }
+            }
+        }
+
+        // First, add all exported packages to the target revision's package space.
+        calculateExportedPackages(revision, allCandidates, revisionPkgMap);
+        Packages revisionPkgs = revisionPkgMap.get(revision);
+
+        // Second, add all imported packages to the target revision's package space.
+        for (int i = 0; i < reqs.size(); i++)
+        {
+            BundleRequirement req = reqs.get(i);
+            BundleCapability cap = caps.get(i);
+            calculateExportedPackages(cap.getRevision(), allCandidates, revisionPkgMap);
+            mergeCandidatePackages(
+                revision, req, cap, revisionPkgMap, allCandidates,
+                new HashMap<BundleRevision, List<BundleCapability>>());
+        }
+
+        // Third, have all candidates to calculate their package spaces.
+        for (int i = 0; i < caps.size(); i++)
+        {
+            calculatePackageSpaces(
+                caps.get(i).getRevision(), allCandidates, revisionPkgMap,
+                usesCycleMap, cycle);
+        }
+
+        // Fourth, if the target revision 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 revisions because their
+        // package space is consistent by definition and these uses constraints
+        // are only needed to verify the consistency of a resolving revision. The
+        // only exception is if a resolved revision 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 ((revision.getWiring() == null) || isDynamicImporting)
+        {
+            // Merge uses constraints from required capabilities.
+            for (int i = 0; i < reqs.size(); i++)
+            {
+                BundleRequirement req = reqs.get(i);
+                BundleCapability cap = caps.get(i);
+                // Ignore bundle/package requirements, since they are
+                // considered below.
+                if (!req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE)
+                    && !req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                {
+                    List<BundleRequirement> blameReqs = new ArrayList();
+                    blameReqs.add(req);
+
+                    mergeUses(
+                        revision,
+                        revisionPkgs,
+                        cap,
+                        blameReqs,
+                        revisionPkgMap,
+                        allCandidates,
+                        usesCycleMap);
+                }
+            }
+            // Merge uses constraints from imported packages.
+            for (Entry<String, List<Blame>> entry : revisionPkgs.m_importedPkgs.entrySet())
+            {
+                for (Blame blame : entry.getValue())
+                {
+                    // Ignore revisions that import from themselves.
+                    if (!blame.m_cap.getRevision().equals(revision))
+                    {
+                        List<BundleRequirement> blameReqs = new ArrayList();
+                        blameReqs.add(blame.m_reqs.get(0));
+
+                        mergeUses(
+                            revision,
+                            revisionPkgs,
+                            blame.m_cap,
+                            blameReqs,
+                            revisionPkgMap,
+                            allCandidates,
+                            usesCycleMap);
+                    }
+                }
+            }
+            // Merge uses constraints from required bundles.
+            for (Entry<String, List<Blame>> entry : revisionPkgs.m_requiredPkgs.entrySet())
+            {
+                for (Blame blame : entry.getValue())
+                {
+                    List<BundleRequirement> blameReqs = new ArrayList();
+                    blameReqs.add(blame.m_reqs.get(0));
+
+                    mergeUses(
+                        revision,
+                        revisionPkgs,
+                        blame.m_cap,
+                        blameReqs,
+                        revisionPkgMap,
+                        allCandidates,
+                        usesCycleMap);
+                }
+            }
+        }
+    }
+
+    private void mergeCandidatePackages(
+        BundleRevision current, BundleRequirement currentReq, BundleCapability candCap,
+        Map<BundleRevision, Packages> revisionPkgMap,
+        Candidates allCandidates, Map<BundleRevision, List<BundleCapability>> cycles)
+    {
+        List<BundleCapability> cycleCaps = cycles.get(current);
+        if (cycleCaps == null)
+        {
+            cycleCaps = new ArrayList<BundleCapability>();
+            cycles.put(current, cycleCaps);
+        }
+        if (cycleCaps.contains(candCap))
+        {
+            return;
+        }
+        cycleCaps.add(candCap);
+
+        if (candCap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+        {
+            mergeCandidatePackage(
+                current, false, currentReq, candCap, revisionPkgMap);
+        }
+        else if (candCap.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
+        {
+// TODO: FELIX3 - THIS NEXT LINE IS A HACK. IMPROVE HOW/WHEN WE CALCULATE EXPORTS.
+            calculateExportedPackages(
+                candCap.getRevision(), allCandidates, revisionPkgMap);
+
+            // Get the candidate's package space to determine which packages
+            // will be visible to the current revision.
+            Packages candPkgs = revisionPkgMap.get(candCap.getRevision());
+
+            // We have to merge all exported packages from the candidate,
+            // since the current revision requires it.
+            for (Entry<String, Blame> entry : candPkgs.m_exportedPkgs.entrySet())
+            {
+                mergeCandidatePackage(
+                    current,
+                    true,
+                    currentReq,
+                    entry.getValue().m_cap,
+                    revisionPkgMap);
+            }
+
+            // If the candidate requires any other bundles with reexport visibility,
+            // then we also need to merge their packages too.
+            if (candCap.getRevision().getWiring() != null)
+            {
+                for (BundleWire bw
+                    : candCap.getRevision().getWiring().getRequiredWires(null))
+                {
+                    if (bw.getRequirement().getNamespace()
+                        .equals(BundleRevision.BUNDLE_NAMESPACE))
+                    {
+                        String value = bw.getRequirement()
+                            .getDirectives().get(Constants.VISIBILITY_DIRECTIVE);
+                        if ((value != null)
+                            && value.equals(Constants.VISIBILITY_REEXPORT))
+                        {
+                            mergeCandidatePackages(
+                                current,
+                                currentReq,
+                                bw.getCapability(),
+                                revisionPkgMap,
+                                allCandidates,
+                                cycles);
+                        }
+                    }
+                }
+            }
+            else
+            {
+                List<BundleRequirement> reqs = (candCap.getRevision().getWiring() != null)
+                    ? candCap.getRevision().getWiring().getRequirements(null)
+                    : candCap.getRevision().getDeclaredRequirements(null);
+                for (BundleRequirement req
+                    : candCap.getRevision().getDeclaredRequirements(null))
+                {
+                    if (req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
+                    {
+                        String value =
+                            req.getDirectives().get(Constants.VISIBILITY_DIRECTIVE);
+                        if ((value != null)
+                            && value.equals(Constants.VISIBILITY_REEXPORT)
+                            && (allCandidates.getCandidates(req) != null))
+                        {
+                            mergeCandidatePackages(
+                                current,
+                                currentReq,
+                                allCandidates.getCandidates(req).iterator().next(),
+                                revisionPkgMap,
+                                allCandidates,
+                                cycles);
+                        }
+                    }
+                }
+            }
+        }
+
+        cycles.remove(current);
+    }
+
+    private void mergeCandidatePackage(
+        BundleRevision current, boolean requires,
+        BundleRequirement currentReq, BundleCapability candCap,
+        Map<BundleRevision, Packages> revisionPkgMap)
+    {
+        if (candCap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+        {
+            // Merge the candidate capability into the revision's package space
+            // for imported or required packages, appropriately.
+
+            String pkgName = (String)
+                candCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
+
+            List blameReqs = new ArrayList();
+            blameReqs.add(currentReq);
+
+            Packages currentPkgs = revisionPkgMap.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));
+
+//dumpRevisionPkgs(current, currentPkgs);
+        }
+    }
+
+    private void mergeUses(
+        BundleRevision current, Packages currentPkgs,
+        BundleCapability mergeCap, List<BundleRequirement> blameReqs,
+        Map<BundleRevision, Packages> revisionPkgMap,
+        Candidates allCandidates,
+        Map<BundleCapability, List<BundleRevision>> cycleMap)
+    {
+        // If there are no uses, then just return.
+        // If the candidate revision is the same as the current revision,
+        // 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.getRevision()))
+        {
+            return;
+        }
+
+        // Check for cycles.
+        List<BundleRevision> list = cycleMap.get(mergeCap);
+        if ((list != null) && list.contains(current))
+        {
+            return;
+        }
+        list = (list == null) ? new ArrayList<BundleRevision>() : list;
+        list.add(current);
+        cycleMap.put(mergeCap, list);
+
+        for (BundleCapability candSourceCap : getPackageSources(mergeCap, revisionPkgMap))
+        {
+            for (String usedPkgName : ((BundleCapabilityImpl) candSourceCap).getUses())
+            {
+                Packages candSourcePkgs = revisionPkgMap.get(candSourceCap.getRevision());
+                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<BundleRequirement> blameReqs2 = new ArrayList(blameReqs);
+                        blameReqs2.add(blame.m_reqs.get(blame.m_reqs.size() - 1));
+                        usedCaps.add(new Blame(blame.m_cap, blameReqs2));
+                        mergeUses(current, currentPkgs, blame.m_cap, blameReqs2,
+                            revisionPkgMap, allCandidates, cycleMap);
+                    }
+                    else
+                    {
+                        usedCaps.add(new Blame(blame.m_cap, blameReqs));
+                        mergeUses(current, currentPkgs, blame.m_cap, blameReqs,
+                            revisionPkgMap, allCandidates, cycleMap);
+                    }
+                }
+            }
+        }
+    }
+
+    private void checkPackageSpaceConsistency(
+        boolean isDynamicImporting,
+        BundleRevision revision,
+        Candidates allCandidates,
+        Map<BundleRevision, Packages> revisionPkgMap,
+        Map<BundleRevision, Object> resultCache)
+    {
+        if ((revision.getWiring() != null) && !isDynamicImporting)
+        {
+            return;
+        }
+        else if(resultCache.containsKey(revision))
+        {
+            return;
+        }
+
+        Packages pkgs = revisionPkgMap.get(revision);
+
+        ResolveException rethrow = null;
+        Candidates permutation = null;
+        Set<BundleRequirement> 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.getRevision().equals(blame.m_cap.getRevision()))
+                    {
+                        // 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.
+                        ResolveException ex = new ResolveException(
+                            "Uses constraint violation. Unable to resolve bundle revision "
+                            + revision.getSymbolicName()
+                            + " [" + revision
+                            + "] because it is exposed to package '"
+                            + entry.getKey()
+                            + "' from bundle revisions "
+                            + sourceBlame.m_cap.getRevision().getSymbolicName()
+                            + " [" + sourceBlame.m_cap.getRevision()
+                            + "] and "
+                            + blame.m_cap.getRevision().getSymbolicName()
+                            + " [" + blame.m_cap.getRevision()
+                            + "] via two dependency chains.\n\nChain 1:\n"
+                            + toStringBlame(sourceBlame)
+                            + "\n\nChain 2:\n"
+                            + toStringBlame(blame),
+                            revision,
+                            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;
+                    }
+                }
+            }
+        }
+
+        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(exportBlame.m_cap, usedBlame.m_cap, revisionPkgMap))
+                {
+                    // 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 ResolveException(
+                            "Uses constraint violation. Unable to resolve bundle revision "
+                            + revision.getSymbolicName()
+                            + " [" + revision
+                            + "] because it exports package '"
+                            + pkgName
+                            + "' and is also exposed to it from bundle revision "
+                            + usedBlame.m_cap.getRevision().getSymbolicName()
+                            + " [" + usedBlame.m_cap.getRevision()
+                            + "] via the following dependency chain:\n\n"
+                            + toStringBlame(usedBlame),
+                            null,
+                            null);
+
+                    mutated = (mutated != null)
+                        ? mutated
+                        : new HashSet<BundleRequirement>();
+
+                    for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
+                    {
+                        BundleRequirement 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 revision
+                        // associated with the requirement is already resolved.
+                        SortedSet<BundleCapability> candidates =
+                            permutation.getCandidates(req);
+                        if ((candidates != null) && (candidates.size() > 1))
+                        {
+                            mutated.add(req);
+                            Iterator it = candidates.iterator();
+                            it.next();
+                            it.remove();
+                            // 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(importBlame.m_cap, usedBlame.m_cap, revisionPkgMap))
+                    {
+                        // 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 ResolveException(
+                                "Uses constraint violation. Unable to resolve bundle revision "
+                                + revision.getSymbolicName()
+                                + " [" + revision
+                                + "] because it is exposed to package '"
+                                + pkgName
+                                + "' from bundle revisions "
+                                + importBlame.m_cap.getRevision().getSymbolicName()
+                                + " [" + importBlame.m_cap.getRevision()
+                                + "] and "
+                                + usedBlame.m_cap.getRevision().getSymbolicName()
+                                + " [" + usedBlame.m_cap.getRevision()
+                                + "] via two dependency chains.\n\nChain 1:\n"
+                                + toStringBlame(importBlame)
+                                + "\n\nChain 2:\n"
+                                + toStringBlame(usedBlame),
+                                null,
+                                null);
+
+                        mutated = (mutated != null)
+                            ? mutated
+                            : new HashSet();
+
+                        for (int reqIdx = usedBlame.m_reqs.size() - 1; reqIdx >= 0; reqIdx--)
+                        {
+                            BundleRequirement 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 revision
+                            // associated with the requirement is already resolved.
+                            SortedSet<BundleCapability> candidates =
+                                permutation.getCandidates(req);
+                            if ((candidates != null) && (candidates.size() > 1))
+                            {
+                                mutated.add(req);
+                                Iterator it = candidates.iterator();
+                                it.next();
+                                it.remove();
+                                // 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.
+                    BundleRequirement 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(revision, Boolean.TRUE);
+
+        // Now check the consistency of all revisions on which the
+        // current revision 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 (!revision.equals(importBlame.m_cap.getRevision()))
+                {
+                    try
+                    {
+                        checkPackageSpaceConsistency(
+                            false, importBlame.m_cap.getRevision(),
+                            allCandidates, revisionPkgMap, resultCache);
+                    }
+                    catch (ResolveException 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 revision
+                        // to backtrack on our current candidate selection.
+                        if (permCount == (m_usesPermutations.size() + m_importPermutations.size()))
+                        {
+                            BundleRequirement req = importBlame.m_reqs.get(0);
+                            permutate(allCandidates, req, m_importPermutations);
+                        }
+                        throw ex;
+                    }
+                }
+            }
+        }
+    }
+
+    private static void permutate(
+        Candidates allCandidates, BundleRequirement req, List<Candidates> permutations)
+    {
+        SortedSet<BundleCapability> candidates = allCandidates.getCandidates(req);
+        if (candidates.size() > 1)
+        {
+            Candidates perm = allCandidates.copy();
+            candidates = perm.getCandidates(req);
+            Iterator it = candidates.iterator();
+            it.next();
+            it.remove();
+            permutations.add(perm);
+        }
+    }
+
+    private static void permutateIfNeeded(
+        Candidates allCandidates, BundleRequirement req, List<Candidates> permutations)
+    {
+        SortedSet<BundleCapability> 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)
+            {
+                Set<BundleCapability> existingPermCands = existingPerm.getCandidates(req);
+                if (!existingPermCands.iterator().next().equals(candidates.iterator().next()))
+                {
+                    permutated = true;
+                }
+            }
+            // If we haven't already permutated the existing
+            // import, do so now.
+            if (!permutated)
+            {
+                permutate(allCandidates, req, permutations);
+            }
+        }
+    }
+
+    private static void calculateExportedPackages(
+        BundleRevision revision,
+        Candidates allCandidates,
+        Map<BundleRevision, Packages> revisionPkgMap)
+    {
+        Packages packages = revisionPkgMap.get(revision);
+        if (packages != null)
+        {
+            return;
+        }
+        packages = new Packages(revision);
+
+        // Get all exported packages.
+        List<BundleCapability> caps = (revision.getWiring() != null)
+            ? revision.getWiring().getCapabilities(null)
+            : revision.getDeclaredCapabilities(null);
+        Map<String, BundleCapability> exports =
+            new HashMap<String, BundleCapability>(caps.size());
+        for (BundleCapability cap : caps)
+        {
+            if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+            {
+                if (!cap.getRevision().equals(revision))
+                {
+                    cap = new HostedCapability(revision, (BundleCapabilityImpl) cap);
+                }
+                exports.put(
+                    (String) cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE),
+                    cap);
+            }
+        }
+        // Remove substitutable exports that were imported.
+        // For resolved revisions BundleWiring.getCapabilities()
+        // already excludes imported substitutable exports, but
+        // for resolving revisions we must look in the candidate
+        // map to determine which exports are substitutable.
+        if (!exports.isEmpty())
+        {
+            if (revision.getWiring() == null)
+            {
+                for (BundleRequirement req : revision.getDeclaredRequirements(null))
+                {
+                    if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                    {
+                        Set<BundleCapability> cands =
+                            allCandidates.getCandidates((BundleRequirementImpl) req);
+                        if ((cands != null) && !cands.isEmpty())
+                        {
+                            String pkgName = (String) cands.iterator().next()
+                                .getAttributes().get(BundleRevision.PACKAGE_NAMESPACE);
+                            exports.remove(pkgName);
+                        }
+                    }
+                }
+            }
+
+            // Add all non-substituted exports to the revisions's package space.
+            for (Entry<String, BundleCapability> entry : exports.entrySet())
+            {
+                packages.m_exportedPkgs.put(
+                    entry.getKey(), new Blame(entry.getValue(), null));
+            }
+        }
+
+        revisionPkgMap.put(revision, packages);
+    }
+
+    private boolean isCompatible(
+        BundleCapability currentCap, BundleCapability candCap,
+        Map<BundleRevision, Packages> revisionPkgMap)
+    {
+        if ((currentCap != null) && (candCap != null))
+        {
+            if (currentCap.equals(candCap))
+            {
+                return true;
+            }
+
+            List<BundleCapability> currentSources =
+                getPackageSources(
+                    currentCap,
+                    revisionPkgMap);
+            List<BundleCapability> candSources =
+                getPackageSources(
+                    candCap,
+                    revisionPkgMap);
+
+            return currentSources.containsAll(candSources)
+                || candSources.containsAll(currentSources);
+        }
+        return true;
+    }
+
+    private Map<BundleCapability, List<BundleCapability>> m_packageSourcesCache
+        = new HashMap();
+
+    private List<BundleCapability> getPackageSources(
+        BundleCapability cap, Map<BundleRevision, Packages> revisionPkgMap)
+    {
+        if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+        {
+            List<BundleCapability> sources = m_packageSourcesCache.get(cap);
+            if (sources == null)
+            {
+                sources = getPackageSourcesInternal(
+                    cap, revisionPkgMap, new ArrayList(), new HashSet());
+                m_packageSourcesCache.put(cap, sources);
+            }
+            return sources;
+        }
+
+        if (!((BundleCapabilityImpl) cap).getUses().isEmpty())
+        {
+            List<BundleCapability> caps = new ArrayList<BundleCapability>(1);
+            caps.add(cap);
+            return caps;
+        }
+
+        return Collections.EMPTY_LIST;
+    }
+
+    private static List<BundleCapability> getPackageSourcesInternal(
+        BundleCapability cap, Map<BundleRevision, Packages> revisionPkgMap,
+        List<BundleCapability> sources, Set<BundleCapability> cycleMap)
+    {
+        if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+        {
+            if (cycleMap.contains(cap))
+            {
+                return sources;
+            }
+            cycleMap.add(cap);
+
+            // Get the package name associated with the capability.
+            String pkgName = cap.getAttributes()
+                .get(BundleRevision.PACKAGE_NAMESPACE).toString();
+
+            // Since a revision can export the same package more than once, get
+            // all package capabilities for the specified package name.
+            List<BundleCapability> caps = (cap.getRevision().getWiring() != null)
+                ? cap.getRevision().getWiring().getCapabilities(null)
+                : cap.getRevision().getDeclaredCapabilities(null);
+            for (int capIdx = 0; capIdx < caps.size(); capIdx++)
+            {
+                if (caps.get(capIdx).getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                    && caps.get(capIdx).getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).equals(pkgName))
+                {
+                    sources.add(caps.get(capIdx));
+                }
+            }
+
+            // Then get any addition sources for the package from required bundles.
+            Packages pkgs = revisionPkgMap.get(cap.getRevision());
+            List<Blame> required = pkgs.m_requiredPkgs.get(pkgName);
+            if (required != null)
+            {
+                for (Blame blame : required)
+                {
+                    getPackageSourcesInternal(blame.m_cap, revisionPkgMap, sources, cycleMap);
+                }
+            }
+        }
+
+        return sources;
+    }
+
+    private static BundleRevision getActualBundleRevision(BundleRevision br)
+    {
+        if (br instanceof HostBundleRevision)
+        {
+            return ((HostBundleRevision) br).getHost();
+        }
+        return br;
+    }
+
+    private static BundleCapability getActualCapability(BundleCapability c)
+    {
+        if (c instanceof HostedCapability)
+        {
+            return ((HostedCapability) c).getOriginalCapability();
+        }
+        return c;
+    }
+
+    private static BundleRequirement getActualRequirement(BundleRequirement r)
+    {
+        if (r instanceof HostedRequirement)
+        {
+            return ((HostedRequirement) r).getOriginalRequirement();
+        }
+        return r;
+    }
+
+    private static Map<BundleRevision, List<ResolverWire>> populateWireMap(
+        BundleRevision revision, Map<BundleRevision, Packages> revisionPkgMap,
+        Map<BundleRevision, List<ResolverWire>> wireMap,
+        Candidates allCandidates)
+    {
+        BundleRevision unwrappedRevision = getActualBundleRevision(revision);
+        if ((unwrappedRevision.getWiring() == null)
+            && !wireMap.containsKey(unwrappedRevision))
+        {
+            wireMap.put(unwrappedRevision, (List<ResolverWire>) Collections.EMPTY_LIST);
+
+            List<ResolverWire> packageWires = new ArrayList<ResolverWire>();
+            List<ResolverWire> bundleWires = new ArrayList<ResolverWire>();
+            List<ResolverWire> capabilityWires = new ArrayList<ResolverWire>();
+
+            for (BundleRequirement req : revision.getDeclaredRequirements(null))
+            {
+                SortedSet<BundleCapability> cands = allCandidates.getCandidates(req);
+                if ((cands != null) && (cands.size() > 0))
+                {
+                    BundleCapability cand = cands.iterator().next();
+                    // Ignore revisions that import themselves.
+                    if (!revision.equals(cand.getRevision()))
+                    {
+                        if (cand.getRevision().getWiring() == null)
+                        {
+                            populateWireMap(cand.getRevision(),
+                                revisionPkgMap, wireMap, allCandidates);
+                        }
+                        Packages candPkgs = revisionPkgMap.get(cand.getRevision());
+                        ResolverWire wire = new ResolverWireImpl(
+                            unwrappedRevision,
+                            getActualRequirement(req),
+                            getActualBundleRevision(cand.getRevision()),
+                            getActualCapability(cand));
+                        if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                        {
+                            packageWires.add(wire);
+                        }
+                        else if (req.getNamespace().equals(BundleRevision.BUNDLE_NAMESPACE))
+                        {
+                            bundleWires.add(wire);
+                        }
+                        else
+                        {
+                            capabilityWires.add(wire);
+                        }
+                    }
+                }
+            }
+
+            // Combine package wires with require wires last.
+            packageWires.addAll(bundleWires);
+            packageWires.addAll(capabilityWires);
+            wireMap.put(unwrappedRevision, packageWires);
+
+            // Add host wire for any fragments.
+            if (revision instanceof HostBundleRevision)
+            {
+                List<BundleRevision> fragments = ((HostBundleRevision) revision).getFragments();
+                for (BundleRevision fragment : fragments)
+                {
+                    List<ResolverWire> hostWires = wireMap.get(fragment);
+                    if (hostWires == null)
+                    {
+                        hostWires = new ArrayList<ResolverWire>();
+                        wireMap.put(fragment, hostWires);
+                    }
+                    hostWires.add(
+                        new ResolverWireImpl(
+                            getActualBundleRevision(fragment),
+                            fragment.getDeclaredRequirements(
+                                BundleRevision.HOST_NAMESPACE).get(0),
+                            unwrappedRevision,
+                            unwrappedRevision.getDeclaredCapabilities(
+                                BundleRevision.HOST_NAMESPACE).get(0)));
+                }
+            }
+        }
+
+        return wireMap;
+    }
+
+    private static Map<BundleRevision, List<ResolverWire>> populateDynamicWireMap(
+        BundleRevision revision, String pkgName, Map<BundleRevision, Packages> revisionPkgMap,
+        Map<BundleRevision, List<ResolverWire>> wireMap, Candidates allCandidates)
+    {
+        wireMap.put(revision, (List<ResolverWire>) Collections.EMPTY_LIST);
+
+        List<ResolverWire> packageWires = new ArrayList<ResolverWire>();
+
+        BundleRequirement dynReq = null;
+        BundleCapability dynCand = null;
+        for (BundleRequirement req
+            : Util.getDynamicRequirements(revision.getWiring().getRequirements(null)))
+        {
+            // Get the candidates for the current dynamic requirement.
+            SortedSet<BundleCapability> candCaps =
+                allCandidates.getCandidates((BundleRequirementImpl) req);
+            // Optional requirements may not have any candidates.
+            if ((candCaps == null) || candCaps.isEmpty())
+            {
+                continue;
+            }
+
+            // Record the dynamic requirement.
+            dynReq = req;
+            dynCand = candCaps.first();
+
+            // Can only dynamically import one at a time, so break
+            // out of the loop after the first.
+            break;
+        }
+
+        if (dynReq != null)
+        {
+            if (dynCand.getRevision().getWiring() == null)
+            {
+                populateWireMap(dynCand.getRevision(), revisionPkgMap, wireMap,
+                    allCandidates);
+            }
+
+            Map<String, Object> attrs = new HashMap(1);
+            attrs.put(BundleRevision.PACKAGE_NAMESPACE, pkgName);
+            packageWires.add(
+                new ResolverWireImpl(
+                    revision,
+                    dynReq,
+                    getActualBundleRevision(dynCand.getRevision()),
+                    getActualCapability(dynCand)));
+        }
+
+        wireMap.put(revision, packageWires);
+
+        return wireMap;
+    }
+
+    private static void dumpRevisionPkgMap(Map<BundleRevision, Packages> revisionPkgMap)
+    {
+        System.out.println("+++BUNDLE REVISION PKG MAP+++");
+        for (Entry<BundleRevision, Packages> entry : revisionPkgMap.entrySet())
+        {
+            dumpRevisionPkgs(entry.getKey(), entry.getValue());
+        }
+    }
+
+    private static void dumpRevisionPkgs(BundleRevision revision, Packages packages)
+    {
+        System.out.println(revision
+            + " (" + ((revision.getWiring() != 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(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++)
+            {
+                BundleRequirement req = blame.m_reqs.get(i);
+                sb.append("  ");
+                sb.append(req.getRevision().getSymbolicName());
+                sb.append(" [");
+                sb.append(req.getRevision().toString());
+                sb.append("]\n");
+                if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                {
+                    sb.append("    import: ");
+                }
+                else
+                {
+                    sb.append("    require: ");
+                }
+                sb.append(((BundleRequirementImpl) req).getFilter().toString());
+                sb.append("\n     |");
+                if (req.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                {
+                    sb.append("\n    export: ");
+                }
+                else
+                {
+                    sb.append("\n    provide: ");
+                }
+                if ((i + 1) < blame.m_reqs.size())
+                {
+                    BundleCapability cap = Util.getSatisfyingCapability(
+                        blame.m_reqs.get(i + 1).getRevision(),
+                        (BundleRequirementImpl) blame.m_reqs.get(i));
+                    if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
+                    {
+                        sb.append(BundleRevision.PACKAGE_NAMESPACE);
+                        sb.append("=");
+                        sb.append(cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).toString());
+                        BundleCapability usedCap;
+                        if ((i + 2) < blame.m_reqs.size())
+                        {
+                            usedCap = Util.getSatisfyingCapability(
+                                blame.m_reqs.get(i + 2).getRevision(),
+                                (BundleRequirementImpl) blame.m_reqs.get(i + 1));
+                        }
+                        else
+                        {
+                            usedCap = Util.getSatisfyingCapability(
+                                blame.m_cap.getRevision(),
+                                (BundleRequirementImpl) blame.m_reqs.get(i + 1));
+                        }
+                        sb.append("; uses:=");
+                        sb.append(usedCap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
+                    }
+                    else
+                    {
+                        sb.append(cap);
+                    }
+                    sb.append("\n");
+                }
+                else
+                {
+                    BundleCapability export = Util.getSatisfyingCapability(
+                        blame.m_cap.getRevision(),
+                        (BundleRequirementImpl) blame.m_reqs.get(i));
+                    sb.append(export.getNamespace());
+                    sb.append("=");
+                    sb.append(export.getAttributes().get(export.getNamespace()).toString());
+                    if (export.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
+                        && !export.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE)
+                            .equals(blame.m_cap.getAttributes().get(
+                                BundleRevision.PACKAGE_NAMESPACE)))
+                    {
+                        sb.append("; uses:=");
+                        sb.append(blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE));
+                        sb.append("\n    export: ");
+                        sb.append(BundleRevision.PACKAGE_NAMESPACE);
+                        sb.append("=");
+                        sb.append(blame.m_cap.getAttributes().get(BundleRevision.PACKAGE_NAMESPACE).toString());
+                    }
+                    sb.append("\n  ");
+                    sb.append(blame.m_cap.getRevision().getSymbolicName());
+                    sb.append(" [");
+                    sb.append(blame.m_cap.getRevision().toString());
+                    sb.append("]");
+                }
+            }
+        }
+        else
+        {
+            sb.append(blame.m_cap.getRevision().toString());
+        }
+        return sb.toString();
+    }
+
+    private static class Packages
+    {
+        private final BundleRevision m_revision;
+        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(BundleRevision revision)
+        {
+            m_revision = revision;
+        }
+    }
+
+    private static class Blame
+    {
+        public final BundleCapability m_cap;
+        public final List<BundleRequirement> m_reqs;
+
+        public Blame(BundleCapability cap, List<BundleRequirement> reqs)
+        {
+            m_cap = cap;
+            m_reqs = reqs;
+        }
+
+        @Override
+        public String toString()
+        {
+            return m_cap.getRevision()
+                + "." + m_cap.getAttributes().get(BundleRevision.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

Added: ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverWire.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverWire.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverWire.java (added)
+++ ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverWire.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+
+public interface ResolverWire
+{
+    /**
+     * Returns the importing bundle revision.
+     * @return The importing bundle revision.
+    **/
+    public BundleRevision getRequirer();
+    /**
+     * Returns the associated requirement from the importing bundle revision
+     * that resulted in the creation of this wire.
+     * @return
+    **/
+    public BundleRequirement getRequirement();
+    /**
+     * Returns the exporting bundle revision.
+     * @return The exporting bundle revision.
+    **/
+    public BundleRevision getProvider();
+    /**
+     * Returns the associated capability from the exporting bundle revision
+     * that satisfies the requirement of the importing bundle revision.
+     * @return
+    **/
+    public BundleCapability getCapability();
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverWireImpl.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverWireImpl.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverWireImpl.java (added)
+++ ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResolverWireImpl.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+
+class ResolverWireImpl implements ResolverWire
+{
+    private final BundleRevision m_requirer;
+    private final BundleRequirement m_req;
+    private final BundleRevision m_provider;
+    private final BundleCapability m_cap;
+
+    public ResolverWireImpl(
+        BundleRevision requirer, BundleRequirement req,
+        BundleRevision provider, BundleCapability cap)
+    {
+        m_requirer = requirer;
+        m_req = req;
+        m_provider = provider;
+        m_cap = cap;
+    }
+
+    public BundleRevision getRequirer()
+    {
+        return m_requirer;
+    }
+
+    public BundleRequirement getRequirement()
+    {
+        return m_req;
+    }
+
+    public BundleRevision getProvider()
+    {
+        return m_provider;
+    }
+
+    public BundleCapability getCapability()
+    {
+        return m_cap;
+    }
+
+    public String toString()
+    {
+        return m_req
+            + " -> "
+            + "[" + m_provider + "]";
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResourceNotFoundException.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResourceNotFoundException.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResourceNotFoundException.java (added)
+++ ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/resolver/ResourceNotFoundException.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.resolver;
+
+public class ResourceNotFoundException extends Exception
+{
+    public ResourceNotFoundException(String msg)
+    {
+        super(msg);
+    }
+
+    public ResourceNotFoundException(String msg, Throwable th)
+    {
+        super(msg, th);
+    }
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/util/FelixConstants.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/util/FelixConstants.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/util/FelixConstants.java (added)
+++ ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/util/FelixConstants.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+public interface FelixConstants extends org.osgi.framework.Constants
+{
+    String SYSTEM_BUNDLE_SYMBOLICNAME = "org.apache.felix.framework";
+    // Framework constants and values.
+    String FRAMEWORK_VERSION_VALUE = "1.5";
+    String FRAMEWORK_VENDOR_VALUE = "Apache Software Foundation";
+
+    // Framework constants and values.
+    String FELIX_VERSION_PROPERTY = "felix.version";
+
+    // Miscellaneous manifest constants.
+    String DIRECTIVE_SEPARATOR = ":=";
+    String ATTRIBUTE_SEPARATOR = "=";
+    String CLASS_PATH_SEPARATOR = ",";
+    String CLASS_PATH_DOT = ".";
+    String PACKAGE_SEPARATOR = ";";
+    String VERSION_SEGMENT_SEPARATOR = ".";
+    int VERSION_SEGMENT_COUNT = 3;
+    String BUNDLE_NATIVECODE_OPTIONAL = "*";
+
+    // Miscellaneous OSGi constants.
+    String BUNDLE_URL_PROTOCOL = "bundle";
+
+    // Miscellaneous framework configuration property names.
+    String FRAMEWORK_BUNDLECACHE_IMPL = "felix.bundlecache.impl";
+    String LOG_LEVEL_PROP = "felix.log.level";
+    String LOG_LOGGER_PROP = "felix.log.logger";
+    String SYSTEMBUNDLE_ACTIVATORS_PROP = "felix.systembundle.activators";
+    String BUNDLE_STARTLEVEL_PROP = "felix.startlevel.bundle";
+    String SERVICE_URLHANDLERS_PROP = "felix.service.urlhandlers";
+    String IMPLICIT_BOOT_DELEGATION_PROP = "felix.bootdelegation.implicit";
+    String BOOT_CLASSLOADERS_PROP = "felix.bootdelegation.classloaders";
+    String USE_LOCALURLS_PROP = "felix.jarurls";
+
+    // Missing OSGi constant for resolution directive.
+    String RESOLUTION_DYNAMIC = "dynamic";
+
+    // Start level-related constants.
+    int FRAMEWORK_INACTIVE_STARTLEVEL = 0;
+    int FRAMEWORK_DEFAULT_STARTLEVEL = 1;
+    int SYSTEMBUNDLE_DEFAULT_STARTLEVEL = 0;
+    int BUNDLE_DEFAULT_STARTLEVEL = 1;
+
+    // Miscellaneous properties values.
+    String FAKE_URL_PROTOCOL_VALUE = "location:";
+    String FELIX_EXTENSION_ACTIVATOR = "Felix-Activator";
+	int EAGER_ACTIVATION = 0;
+	int LAZY_ACTIVATION = 1;
+}
\ No newline at end of file

Added: ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/util/MapToDictionary.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/util/MapToDictionary.java?rev=1464402&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/util/MapToDictionary.java (added)
+++ ace/trunk/org.apache.ace.verifier/src/org/apache/felix/framework/util/MapToDictionary.java Thu Apr  4 09:43:34 2013
@@ -0,0 +1,83 @@
+/* 
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.util;
+
+import java.util.*;
+
+
+/**
+ * This is a simple class that implements a <tt>Dictionary</tt>
+ * from a <tt>Map</tt>. The resulting dictionary is immutable.
+**/
+public class MapToDictionary extends Dictionary
+{
+    /**
+     * Map source.
+    **/
+    private Map m_map = null;
+
+    public MapToDictionary(Map map)
+    {
+        if (map == null)
+        {
+            throw new IllegalArgumentException("Source map cannot be null.");
+        }
+        m_map = map;
+    }
+
+    public Enumeration elements()
+    {
+        return Collections.enumeration(m_map.values());
+    }
+
+    public Object get(Object key)
+    {
+        return m_map.get(key);
+    }
+
+    public boolean isEmpty()
+    {
+        return m_map.isEmpty();
+    }
+
+    public Enumeration keys()
+    {
+        return Collections.enumeration(m_map.keySet());
+    }
+
+    public Object put(Object key, Object value)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object remove(Object key)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    public int size()
+    {
+        return m_map.size();
+    }
+
+    public String toString()
+    {
+        return m_map.toString();
+    }
+}
\ No newline at end of file