You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2008/12/23 21:07:49 UTC

svn commit: r729083 [1/3] - in /felix/trunk/framework/src/main/java/org/apache/felix/framework: ./ searchpolicy/

Author: rickhall
Date: Tue Dec 23 12:07:49 2008
New Revision: 729083

URL: http://svn.apache.org/viewvc?rev=729083&view=rev
Log:
Started refactoring of resolver code, breaking R4SearchPolicyCore into
the class loader delegation portion and the resolver portion. (FELIX-851)

Added:
    felix/trunk/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/PackageSource.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/ResolvedPackage.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/Resolver.java
Modified:
    felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicyCore.java
    felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/R4WireModule.java

Modified: felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java?rev=729083&r1=729082&r2=729083&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java (original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java Tue Dec 23 12:07:49 2008
@@ -27,6 +27,8 @@
 import org.apache.felix.framework.cache.*;
 import org.apache.felix.framework.ext.SecurityProvider;
 import org.apache.felix.framework.searchpolicy.*;
+import org.apache.felix.framework.searchpolicy.PackageSource;
+import org.apache.felix.framework.searchpolicy.Resolver.ResolverState;
 import org.apache.felix.framework.util.*;
 import org.apache.felix.framework.util.manifestparser.*;
 import org.apache.felix.moduleloader.*;
@@ -52,6 +54,9 @@
 
     // MODULE FACTORY.
     private IModuleFactory m_factory = null;
+    private final Resolver m_resolver;
+    private final FelixResolverState m_resolverState;
+    private final FelixResolver m_felixResolver;
     private R4SearchPolicyCore m_policyCore = null;
 
     // Lock object used to determine if an individual bundle
@@ -283,74 +288,16 @@
         // Create default bundle stream handler.
         m_bundleStreamHandler = new URLHandlersBundleStreamHandler(this);
 
-        // Create search policy for module loader.
-        m_policyCore = new R4SearchPolicyCore(m_logger, m_configMap);
+        // Create a resolver and its state.
+        m_resolver = new Resolver(m_logger);
+        m_resolverState = new FelixResolverState(m_logger);
+        m_felixResolver = new FelixResolver(m_resolver, m_resolverState);
 
-        // Add a resolver listener to the search policy
-        // so that we will be notified when modules are resolved
-        // in order to update the bundle state.
-        m_policyCore.addResolverListener(new ResolveListener() {
-            public void moduleResolved(ModuleEvent event)
-            {
-                FelixBundle bundle = null;
-                try
-                {
-                    long id = Util.getBundleIdFromModuleId(
-                        event.getModule().getId());
-                    if (id > 0)
-                    {
-                        // Update the bundle's state to resolved when the
-                        // current module is resolved; just ignore resolve
-                        // events for older revisions since this only occurs
-                        // when an update is done on an unresolved bundle
-                        // and there was no refresh performed.
-                        bundle = (FelixBundle) getBundle(id);
-
-                        // Lock the bundle first.
-                        try
-                        {
-                            acquireBundleLock(bundle);
-                            if (bundle.getInfo().getCurrentModule() == event.getModule())
-                            {
-                                if (bundle.getInfo().getState() != Bundle.INSTALLED)
-                                {
-                                    m_logger.log(
-                                        Logger.LOG_WARNING,
-                                        "Received a resolve event for a bundle that has already been resolved.");
-                                }
-                                else
-                                {
-                                    bundle.getInfo().setState(Bundle.RESOLVED);
-                                    fireBundleEvent(BundleEvent.RESOLVED, bundle);
-                                }
-                            }
-                        }
-                        finally
-                        {
-                            releaseBundleLock(bundle);
-                        }
-                    }
-                }
-                catch (NumberFormatException ex)
-                {
-                    // Ignore.
-                }
-            }
-
-            public void moduleUnresolved(ModuleEvent event)
-            {
-                // We can ignore this, because the only time it
-                // should happen is when a refresh occurs. The
-                // refresh operation resets the bundle's state
-                // by calling BundleInfo.reset(), thus it is not
-                // necessary for us to reset the bundle's state
-                // here.
-            }
-        });
+        // Create search policy for module loader.
+        m_policyCore = new R4SearchPolicyCore(m_logger, m_configMap, m_felixResolver);
 
         // Create the module factory and attach it to the search policy.
         m_factory = new ModuleFactoryImpl(m_logger);
-        m_policyCore.setModuleFactory(m_factory);
 
         // Create the system bundle info object, which will hold state info.
         m_sbi = new SystemBundleInfo(m_logger, null);
@@ -358,6 +305,7 @@
         // definition for creating the system bundle module.
         m_extensionManager = new ExtensionManager(m_logger, m_configMap, m_sbi);
         m_sbi.addModule(m_factory.createModule("0", m_extensionManager));
+        m_resolverState.addModule(m_sbi.getCurrentModule());
         // Set the extension manager as the content loader for the system
         // bundle module.
         m_extensionManager.setSearchPolicy(
@@ -749,7 +697,7 @@
             // state to be set to RESOLVED.
             try
             {
-                m_policyCore.resolve(m_sbi.getCurrentModule());
+                m_felixResolver.resolve(m_sbi.getCurrentModule());
             }
             catch (ResolveException ex)
             {
@@ -1783,7 +1731,7 @@
         IModule module = bundle.getInfo().getCurrentModule();
         try
         {
-            m_policyCore.resolve(module);
+            m_felixResolver.resolve(module);
         }
         catch (ResolveException ex)
         {
@@ -2989,8 +2937,8 @@
     protected ExportedPackage[] getExportedPackages(String pkgName)
     {
         // First, get all exporters of the package.
-        R4SearchPolicyCore.PackageSource[] exporters =
-            m_policyCore.getResolvedCandidates(
+        PackageSource[] exporters =
+            m_resolverState.getResolvedCandidates(
                 new Requirement(
                     ICapability.PACKAGE_NAMESPACE,
                     null,
@@ -3120,7 +3068,7 @@
                     // "in use" exporters of the package.
                     if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
                     {
-                        R4SearchPolicyCore.PackageSource[] inUseModules = m_policyCore.getResolvedCandidates(
+                        PackageSource[] inUseModules = m_resolverState.getResolvedCandidates(
                             new Requirement(
                                 ICapability.PACKAGE_NAMESPACE,
                                 null,
@@ -3478,6 +3426,7 @@
         // Create the module using the module definition.
         IModule module = m_factory.createModule(
             Long.toString(targetId) + "." + Integer.toString(revision), md);
+        m_resolverState.addModule(module);
 
         // Create the content loader from the module archive.
         IContentLoader contentLoader = new ContentLoaderImpl(
@@ -3513,6 +3462,11 @@
                     // has not yet been added to the bundle to be removed later.
                     m_factory.removeModule(module);
                     throw new BundleException("Native library does not exist: " + entryName);
+// TODO: REFACTOR - We have a memory leak here since we added a module above
+//                  and then don't remove it in case of an error; this may also
+//                  be a general issue for installing/updating bundles, so check.
+//                  This will likely go away when we refactor out the module
+//                  factory, but we will track it under FELIX-835 until then.
                 }
             }
         }
@@ -3575,6 +3529,7 @@
             for (int i = 0; i < modules.length; i++)
             {
                 m_factory.removeModule(modules[i]);
+                m_resolverState.removeModule(modules[i]);
             }
 
             // Purge all bundle revisions, but the current one.
@@ -3599,6 +3554,7 @@
         for (int i = 0; i < modules.length; i++)
         {
             m_factory.removeModule(modules[i]);
+            m_resolverState.removeModule(modules[i]);
         }
 
         // Remove the bundle from the cache.
@@ -3818,6 +3774,191 @@
     // Miscellaneous inner classes.
     //
 
+    public class FelixResolver
+    {
+        private final Resolver m_resolver;
+        private final FelixResolverState m_resolverState;
+
+        public FelixResolver(Resolver resolver, FelixResolverState resolverState)
+        {
+            m_resolver = resolver;
+            m_resolverState = resolverState;
+        }
+
+        public void resolve(IModule rootModule) throws ResolveException
+        {
+            if (!m_resolverState.isResolved(rootModule))
+            {
+                Resolver.Result result = m_resolver.resolve(m_resolverState, rootModule);
+
+                // Mark all modules as resolved.
+                markResolvedModules(result.m_resolvedModuleWireMap);
+
+                // Attach and mark all fragments as resolved.
+                attachFragments(result.m_host, result.m_fragmentMap);
+            }
+        }
+
+        public IWire resolveDynamicImport(IModule importer, String pkgName) throws ResolveException
+        {
+            IWire candidateWire = null;
+
+            if (m_resolverState.isResolved(importer))
+            {
+                Object[] result = m_resolver.resolveDynamicImport(m_resolverState, importer, pkgName);
+                if (result != null)
+                {
+                    candidateWire = (IWire) result[0];
+                    Map resolvedModuleWireMap = (Map) result[1];
+
+                    // Mark all modules as resolved.
+                    markResolvedModules(resolvedModuleWireMap);
+
+                    // Dynamically add new wire to importing module.
+                    if (candidateWire != null)
+                    {
+                        IWire[] wires = importer.getWires();
+                        IWire[] newWires = null;
+                        if (wires == null)
+                        {
+                            newWires = new IWire[1];
+                        }
+                        else
+                        {
+                            newWires = new IWire[wires.length + 1];
+                            System.arraycopy(wires, 0, newWires, 0, wires.length);
+                        }
+
+                        newWires[newWires.length - 1] = candidateWire;
+                        ((ModuleImpl) importer).setWires(newWires);
+m_logger.log(Logger.LOG_DEBUG, "DYNAMIC WIRE: " + newWires[newWires.length - 1]);
+                    }
+                }
+            }
+
+            return candidateWire;
+        }
+
+        public synchronized PackageSource[] getResolvedCandidates(IRequirement req)
+        {
+            return m_resolverState.getResolvedCandidates(req);
+        }
+
+        public synchronized PackageSource[] getUnresolvedCandidates(IRequirement req)
+        {
+            return m_resolverState.getUnresolvedCandidates(req);
+        }
+
+        private void markResolvedModules(Map resolvedModuleWireMap)
+        {
+            Iterator iter = resolvedModuleWireMap.entrySet().iterator();
+            // Iterate over the map to mark the modules as resolved and
+            // update our resolver data structures.
+            while (iter.hasNext())
+            {
+                Map.Entry entry = (Map.Entry) iter.next();
+                IModule module = (IModule) entry.getKey();
+                IWire[] wires = (IWire[]) entry.getValue();
+
+                // Only add wires attribute if some exist; export
+                // only modules may not have wires.
+                if (wires.length > 0)
+                {
+                    ((ModuleImpl) module).setWires(wires);
+for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
+{
+    m_logger.log(Logger.LOG_DEBUG, "WIRE: " + wires[wireIdx]);
+}
+                }
+
+                // Update the resolver state to show the module as resolved.
+                m_resolverState.setResolved(module, true);
+                // Update the state of the module's bundle to resolved as well.
+                markBundleResolved(module);
+            }
+        }
+
+        private void attachFragments(IModule host, Map fragmentMap)
+        {
+            // Attach fragments to host module.
+            if ((fragmentMap != null) && (fragmentMap.size() > 0))
+            {
+                List list = new ArrayList();
+                for (Iterator iter = fragmentMap.entrySet().iterator(); iter.hasNext(); )
+                {
+                    Map.Entry entry = (Map.Entry) iter.next();
+                    String symName = (String) entry.getKey();
+                    IModule[] fragments = (IModule[]) entry.getValue();
+// TODO: FRAGMENT - For now, just attach first candidate.
+                    list.add(fragments[0]);
+m_logger.log(Logger.LOG_DEBUG, "(FRAGMENT) WIRE: "
+    + host + " -> " + symName + " -> " + fragments[0]);
+
+                    // Update the resolver state to show the module as resolved.
+                    m_resolverState.setResolved(fragments[0], true);
+                    // Update the state of the module's bundle to resolved as well.
+                    markBundleResolved(fragments[0]);
+                }
+                try
+                {
+                    ((ModuleImpl) host).attachFragments(
+                        (IModule[]) list.toArray(new IModule[list.size()]));
+                }
+                catch (Exception ex)
+                {
+                    m_logger.log(Logger.LOG_ERROR, "Unable to attach fragments", ex);
+                }
+            }
+        }
+
+        private void markBundleResolved(IModule module)
+        {
+            // Mark associated bundle as resolved.
+            try
+            {
+                long id = Util.getBundleIdFromModuleId(module.getId());
+                if (id > 0)
+                {
+                    // Update the bundle's state to resolved when the
+                    // current module is resolved; just ignore resolve
+                    // events for older revisions since this only occurs
+                    // when an update is done on an unresolved bundle
+                    // and there was no refresh performed.
+                    FelixBundle bundle = (FelixBundle) getBundle(id);
+
+                    // Lock the bundle first.
+                    try
+                    {
+// TODO: RESOLVER - Seems like we should release the lock before we fire the event.
+                        acquireBundleLock(bundle);
+                        if (bundle.getInfo().getCurrentModule() == module)
+                        {
+                            if (bundle.getInfo().getState() != Bundle.INSTALLED)
+                            {
+                                m_logger.log(
+                                    Logger.LOG_WARNING,
+                                    "Received a resolve event for a bundle that has already been resolved.");
+                            }
+                            else
+                            {
+                                bundle.getInfo().setState(Bundle.RESOLVED);
+                                fireBundleEvent(BundleEvent.RESOLVED, bundle);
+                            }
+                        }
+                    }
+                    finally
+                    {
+                        releaseBundleLock(bundle);
+                    }
+                }
+            }
+            catch (NumberFormatException ex)
+            {
+                // Ignore.
+            }
+        }
+    }
+
     class SystemBundleActivator implements BundleActivator, Runnable
     {
         public void start(BundleContext context) throws Exception
@@ -3895,6 +4036,7 @@
                     try
                     {
                         m_factory.removeModule(modules[j]);
+                        m_resolverState.removeModule(modules[j]);
                     }
                     catch (Exception ex)
                     {

Added: felix/trunk/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java?rev=729083&view=auto
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java (added)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java Tue Dec 23 12:07:49 2008
@@ -0,0 +1,735 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.apache.felix.framework.searchpolicy.Resolver;
+import org.apache.felix.framework.searchpolicy.PackageSource;
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.Requirement;
+import org.apache.felix.moduleloader.ICapability;
+import org.apache.felix.moduleloader.IModule;
+import org.apache.felix.moduleloader.IRequirement;
+import org.apache.felix.moduleloader.IWire;
+import org.apache.felix.moduleloader.ModuleImpl;
+import org.osgi.framework.Constants;
+import org.osgi.framework.PackagePermission;
+import org.osgi.framework.Version;
+
+public class FelixResolverState implements Resolver.ResolverState
+{
+    private final Logger m_logger;
+    // List of all modules.
+    private final List m_moduleList = new ArrayList();
+    // Maps a package name to an array of modules.
+    private final Map m_unresolvedPkgIndexMap = new HashMap();
+    // Maps a package name to an array of modules.
+    private final Map m_resolvedPkgIndexMap = new HashMap();
+    // Maps a module to an array of capabilities.
+    private final Map m_resolvedCapMap = new HashMap();
+
+    private final Map m_moduleDataMap = new HashMap();
+
+    // Reusable empty array.
+    private static final IModule[] m_emptyModules = new IModule[0];
+    private static final PackageSource[] m_emptySources = new PackageSource[0];
+
+    public FelixResolverState(Logger logger)
+    {
+        m_logger = logger;
+    }
+
+    public synchronized void addModule(IModule module)
+    {
+        // When a module is added, create an aggregated list of unresolved
+        // exports to simplify later processing when resolving bundles.
+        m_moduleList.add(module);
+
+        ICapability[] caps = module.getDefinition().getCapabilities();
+
+        // Add exports to unresolved package map.
+        for (int i = 0; (caps != null) && (i < caps.length); i++)
+        {
+            if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+            {
+                indexPackageCapability(m_unresolvedPkgIndexMap, module, caps[i]);
+            }
+        }
+
+System.out.println("UNRESOLVED PACKAGES:");
+dumpPackageIndexMap(m_unresolvedPkgIndexMap);
+System.out.println("RESOLVED PACKAGES:");
+dumpPackageIndexMap(m_resolvedPkgIndexMap);
+    }
+
+    public synchronized void removeModule(IModule module)
+    {
+        // When a module is removed from the system, we need remove
+        // its exports from the "resolved" and "unresolved" package maps,
+        // remove the module's dependencies on fragments and exporters,
+        // and remove the module from the module data map.
+
+        m_moduleList.remove(module);
+
+        // Remove exports from package maps.
+        ICapability[] caps = module.getDefinition().getCapabilities();
+        for (int i = 0; (caps != null) && (i < caps.length); i++)
+        {
+            if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+            {
+                // Get package name.
+                String pkgName = (String)
+                    caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY);
+                // Remove from "unresolved" package map.
+                IModule[] modules = (IModule[]) m_unresolvedPkgIndexMap.get(pkgName);
+                if (modules != null)
+                {
+                    modules = removeModuleFromArray(modules, module);
+                    m_unresolvedPkgIndexMap.put(pkgName, modules);
+                }
+
+                // Remove from "resolved" package map.
+                modules = (IModule[]) m_resolvedPkgIndexMap.get(pkgName);
+                if (modules != null)
+                {
+                    modules = removeModuleFromArray(modules, module);
+                    m_resolvedPkgIndexMap.put(pkgName, modules);
+                }
+            }
+        }
+
+        // Set fragments to null, which will remove the module from all
+        // of its dependent fragment modules.
+        try
+        {
+            ((ModuleImpl) module).attachFragments(null);
+        }
+        catch (Exception ex)
+        {
+            m_logger.log(Logger.LOG_ERROR, "Error detaching fragments.", ex);
+        }
+        // Set wires to null, which will remove the module from all
+        // of its dependent modules.
+        ((ModuleImpl) module).setWires(null);
+        // Remove the module from the "resolved" map.
+// TODO: RB - Maybe this can be merged with ModuleData.
+        m_resolvedCapMap.remove(module);
+        // Finally, remove module data.
+        m_moduleDataMap.remove(module);
+    }
+
+    private void dumpPackageIndexMap(Map pkgIndexMap)
+    {
+        for (Iterator i = pkgIndexMap.entrySet().iterator(); i.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) i.next();
+            IModule[] modules = (IModule[]) entry.getValue();
+            if ((modules != null) && (modules.length > 0))
+            {
+                if (!((modules.length == 1) && modules[0].getId().equals("0")))
+                {
+                    System.out.println("  " + entry.getKey());
+                    for (int j = 0; j < modules.length; j++)
+                    {
+                        System.out.println("    " + modules[j]);
+                    }
+                }
+            }
+        }
+    }
+
+    public synchronized IModule[] getModules()
+    {
+        return (IModule[]) m_moduleList.toArray(new IModule[m_moduleList.size()]);
+    }
+
+    public String getBundleSymbolicName(IModule module)
+    {
+        ICapability[] caps = module.getDefinition().getCapabilities();
+        for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+        {
+            if (caps[capIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
+            {
+                return (String)
+                    caps[capIdx].getProperties().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+            }
+        }
+        return null;
+    }
+
+// TODO: FRAGMENT - Not very efficient.
+    private static Version getBundleVersion(IModule module)
+    {
+        ICapability[] caps = module.getDefinition().getCapabilities();
+        for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+        {
+            if (caps[capIdx].getNamespace().equals(ICapability.MODULE_NAMESPACE))
+            {
+                return (Version)
+                    caps[capIdx].getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+            }
+        }
+        return Version.emptyVersion;
+    }
+
+    public synchronized boolean isResolved(IModule module)
+    {
+        ModuleData data = (ModuleData) m_moduleDataMap.get(module);
+        if (data != null)
+        {
+            return data.m_resolved;
+        }
+        return false;
+    }
+
+    public synchronized void setResolved(IModule module, boolean b)
+    {
+        ModuleData data = (ModuleData) m_moduleDataMap.get(module);
+        if (data == null)
+        {
+            data = new ModuleData(module);
+            m_moduleDataMap.put(module, data);
+        }
+        data.m_resolved = b;
+
+        if (data.m_resolved)
+        {
+            // At this point, we need to remove all of the resolved module's
+            // capabilities from the "unresolved" package map and put them in
+            // in the "resolved" package map, with the exception of any
+            // package exports that are also imported. In that case we need
+            // to make sure that the import actually points to the resolved
+            // module and not another module. If it points to another module
+            // then the capability should be ignored, since the framework
+            // decided to honor the import and discard the export.
+            ICapability[] caps = module.getDefinition().getCapabilities();
+
+            // First remove all existing capabilities from the "unresolved" map.
+            for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+            {
+                if (caps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+                {
+                    // Get package name.
+                    String pkgName = (String)
+                        caps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY);
+                    // Remove the module's capability for the package.
+                    m_unresolvedPkgIndexMap.put(
+                        pkgName,
+                        removeModuleFromArray(
+                            (IModule[]) m_unresolvedPkgIndexMap.get(pkgName),
+                            module));
+                }
+            }
+
+            // Next create a copy of the module's capabilities so we can
+            // null out any capabilities that should be ignored.
+            ICapability[] capsCopy = (caps == null) ? null : new ICapability[caps.length];
+            if (capsCopy != null)
+            {
+                System.arraycopy(caps, 0, capsCopy, 0, caps.length);
+            }
+            // Loop through the module's capabilities to determine which ones
+            // can be ignored by seeing which ones satifies the wire requirements.
+// TODO: RB - Bug here because a requirement for a package need not overlap the
+//            capability for that package and this assumes it does. This might
+//            require us to introduce the notion of a substitutable capability.
+            IWire[] wires = module.getWires();
+            for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
+            {
+                // Loop through all wires to see if the current capability
+                // satisfies any of the wire requirements.
+                for (int wireIdx = 0; (wires != null) && (wireIdx < wires.length); wireIdx++)
+                {
+                    // If the wire requirement is satisfied by the current capability,
+                    // then check to see if the wire is to the module itself. If it
+                    // is to another module, then null the current capability since
+                    // it was both providing and requiring the same capability and
+                    // the resolve process chose to import rather than provide that
+                    // capability, therefore we should ignore it.
+                    if (wires[wireIdx].getRequirement().isSatisfied(capsCopy[capIdx]))
+                    {
+                        if (!wires[wireIdx].getExporter().equals(module))
+                        {
+                            capsCopy[capIdx] = null;
+                        }
+                        break;
+                    }
+                }
+            }
+
+            // Now loop through all capabilities and add them to the "resolved"
+            // capability and package index maps, ignoring any that were nulled out.
+            for (int capIdx = 0; (capsCopy != null) && (capIdx < capsCopy.length); capIdx++)
+            {
+                if (capsCopy[capIdx] != null)
+                {
+                    ICapability[] resolvedCaps = (ICapability[]) m_resolvedCapMap.get(module);
+                    resolvedCaps = addCapabilityToArray(resolvedCaps, capsCopy[capIdx]);
+                    m_resolvedCapMap.put(module, resolvedCaps);
+
+                    // If the capability is a package, then add the exporter module
+                    // of the wire to the "resolved" package index and remove it
+                    // from the "unresolved" package index.
+                    if (capsCopy[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+                    {
+                        // Add to "resolved" package index.
+                        indexPackageCapability(
+                            m_resolvedPkgIndexMap,
+                            module,
+                            capsCopy[capIdx]);
+                    }
+                }
+            }
+        }
+
+System.out.println("UNRESOLVED PACKAGES:");
+dumpPackageIndexMap(m_unresolvedPkgIndexMap);
+System.out.println("RESOLVED PACKAGES:");
+dumpPackageIndexMap(m_resolvedPkgIndexMap);
+    }
+
+    // TODO: FRAGMENT - Not very efficient.
+    public synchronized List getPotentialHosts(IModule fragment)
+    {
+        List hostList = new ArrayList();
+
+        IRequirement[] reqs = fragment.getDefinition().getRequirements();
+        IRequirement hostReq = null;
+        for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
+        {
+            if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
+            {
+                hostReq = reqs[reqIdx];
+                break;
+            }
+        }
+
+        IModule[] modules = getModules();
+        for (int modIdx = 0; (hostReq != null) && (modIdx < modules.length); modIdx++)
+        {
+            if (!fragment.equals(modules[modIdx]) && !isResolved(modules[modIdx]))
+            {
+                ICapability[] caps = modules[modIdx].getDefinition().getCapabilities();
+                for (int capIdx = 0; (caps != null) && (capIdx < caps.length); capIdx++)
+                {
+                    if (caps[capIdx].getNamespace().equals(ICapability.HOST_NAMESPACE)
+                        && hostReq.isSatisfied(caps[capIdx])
+                        && !modules[modIdx].isStale())
+                    {
+                        hostList.add(modules[modIdx]);
+                        break;
+                    }
+                }
+            }
+        }
+
+        return hostList;
+    }
+
+// TODO: FRAGMENT - Not very efficient.
+    public synchronized Map getPotentialFragments(IModule host)
+    {
+// TODO: FRAGMENT - This should check to make sure that the host allows fragments.
+        Map fragmentMap = new HashMap();
+
+        ICapability[] caps = host.getDefinition().getCapabilities();
+        ICapability bundleCap = null;
+        for (int capIdx = 0; capIdx < caps.length; capIdx++)
+        {
+            if (caps[capIdx].getNamespace().equals(ICapability.HOST_NAMESPACE))
+            {
+                bundleCap = caps[capIdx];
+                break;
+            }
+        }
+
+        IModule[] modules = getModules();
+        for (int modIdx = 0; (bundleCap != null) && (modIdx < modules.length); modIdx++)
+        {
+            if (!host.equals(modules[modIdx]))
+            {
+                IRequirement[] reqs = modules[modIdx].getDefinition().getRequirements();
+                for (int reqIdx = 0; (reqs != null) && (reqIdx < reqs.length); reqIdx++)
+                {
+                    if (reqs[reqIdx].getNamespace().equals(ICapability.HOST_NAMESPACE)
+                        && reqs[reqIdx].isSatisfied(bundleCap)
+                        && !modules[modIdx].isStale())
+                    {
+                        indexFragment(fragmentMap, modules[modIdx]);
+                        break;
+                    }
+                }
+            }
+        }
+
+        return fragmentMap;
+    }
+
+    public synchronized PackageSource[] getResolvedCandidates(IRequirement req)
+    {
+        // Synchronized on the module manager to make sure that no
+        // modules are added, removed, or resolved.
+        PackageSource[] candidates = m_emptySources;
+        if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE)
+            && (((Requirement) req).getPackageName() != null))
+        {
+            String pkgName = ((Requirement) req).getPackageName();
+            IModule[] modules = (IModule[]) m_resolvedPkgIndexMap.get(pkgName);
+
+            for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
+            {
+                ICapability resolvedCap = Util.getSatisfyingCapability(modules[modIdx], req);
+                if (resolvedCap != null)
+                {
+// TODO: RB - Is this permission check correct.
+                    if ((System.getSecurityManager() != null) &&
+                        !((BundleProtectionDomain) modules[modIdx].getContentLoader().getSecurityContext()).impliesDirect(
+                            new PackagePermission(pkgName,
+                                PackagePermission.EXPORT)))
+                    {
+                        m_logger.log(Logger.LOG_DEBUG,
+                            "PackagePermission.EXPORT denied for "
+                            + pkgName
+                            + "from " + modules[modIdx].getId());
+                    }
+                    else
+                    {
+                        PackageSource[] tmp = new PackageSource[candidates.length + 1];
+                        System.arraycopy(candidates, 0, tmp, 0, candidates.length);
+                        tmp[candidates.length] =
+                            new PackageSource(modules[modIdx], resolvedCap);
+                        candidates = tmp;
+                    }
+                }
+            }
+        }
+        else
+        {
+            Iterator i = m_resolvedCapMap.entrySet().iterator();
+            while (i.hasNext())
+            {
+                Map.Entry entry = (Map.Entry) i.next();
+                IModule module = (IModule) entry.getKey();
+                ICapability[] resolvedCaps = (ICapability[]) entry.getValue();
+                for (int capIdx = 0; capIdx < resolvedCaps.length; capIdx++)
+                {
+                    if (req.isSatisfied(resolvedCaps[capIdx]))
+                    {
+// TODO: RB - Is this permission check correct.
+                        if (resolvedCaps[capIdx].getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
+                            (System.getSecurityManager() != null) &&
+                            !((BundleProtectionDomain) module.getContentLoader().getSecurityContext()).impliesDirect(
+                                new PackagePermission(
+                                    (String) resolvedCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY),
+                                    PackagePermission.EXPORT)))
+                        {
+                            m_logger.log(Logger.LOG_DEBUG,
+                                "PackagePermission.EXPORT denied for "
+                                + resolvedCaps[capIdx].getProperties().get(ICapability.PACKAGE_PROPERTY)
+                                + "from " + module.getId());
+                        }
+                        else
+                        {
+                            PackageSource[] tmp = new PackageSource[candidates.length + 1];
+                            System.arraycopy(candidates, 0, tmp, 0, candidates.length);
+                            tmp[candidates.length] = new PackageSource(module, resolvedCaps[capIdx]);
+                            candidates = tmp;
+                        }
+                    }
+                }
+            }
+        }
+        Arrays.sort(candidates);
+        return candidates;
+    }
+
+    public synchronized PackageSource[] getUnresolvedCandidates(IRequirement req)
+    {
+        // Get all modules.
+        IModule[] modules = null;
+        if (req.getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
+            (((Requirement) req).getPackageName() != null))
+        {
+            modules = (IModule[]) m_unresolvedPkgIndexMap.get(((Requirement) req).getPackageName());
+        }
+        else
+        {
+            modules = getModules();
+        }
+
+        // Create list of compatible providers.
+        PackageSource[] candidates = m_emptySources;
+        for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
+        {
+            // Get the module's export package for the target package.
+            ICapability cap = Util.getSatisfyingCapability(modules[modIdx], req);
+            // If compatible and it is not currently resolved, then add
+            // the unresolved candidate to the list.
+            if ((cap != null) && !isResolved(modules[modIdx]))
+            {
+                PackageSource[] tmp = new PackageSource[candidates.length + 1];
+                System.arraycopy(candidates, 0, tmp, 0, candidates.length);
+                tmp[candidates.length] = new PackageSource(modules[modIdx], cap);
+                candidates = tmp;
+            }
+        }
+        Arrays.sort(candidates);
+        return candidates;
+    }
+
+    //
+    // Utility methods.
+    //
+
+    private void indexPackageCapability(Map map, IModule module, ICapability capability)
+    {
+        if (capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+        {
+            String pkgName = (String)
+                capability.getProperties().get(ICapability.PACKAGE_PROPERTY);
+            IModule[] modules = (IModule[]) map.get(pkgName);
+
+            // We want to add the module into the list of exporters
+            // in sorted order (descending version and ascending bundle
+            // identifier). Insert using a simple binary search algorithm.
+            if (modules == null)
+            {
+                modules = new IModule[] { module };
+            }
+            else
+            {
+                Version version = (Version)
+                    capability.getProperties().get(ICapability.VERSION_PROPERTY);
+                Version middleVersion = null;
+                int top = 0, bottom = modules.length - 1, middle = 0;
+                while (top <= bottom)
+                {
+                    middle = (bottom - top) / 2 + top;
+                    middleVersion = (Version)
+                        getExportPackageCapability(
+                            modules[middle], pkgName)
+                                .getProperties()
+                                    .get(ICapability.VERSION_PROPERTY);
+                    // Sort in reverse version order.
+                    int cmp = middleVersion.compareTo(version);
+                    if (cmp < 0)
+                    {
+                        bottom = middle - 1;
+                    }
+                    else if (cmp == 0)
+                    {
+                        // Sort further by ascending bundle ID.
+                        long middleId = Util.getBundleIdFromModuleId(modules[middle].getId());
+                        long exportId = Util.getBundleIdFromModuleId(module.getId());
+                        if (middleId < exportId)
+                        {
+                            top = middle + 1;
+                        }
+                        else
+                        {
+                            bottom = middle - 1;
+                        }
+                    }
+                    else
+                    {
+                        top = middle + 1;
+                    }
+                }
+
+                // Ignore duplicates.
+                if ((top >= modules.length) || (modules[top] != module))
+                {
+                    IModule[] newMods = new IModule[modules.length + 1];
+                    System.arraycopy(modules, 0, newMods, 0, top);
+                    System.arraycopy(modules, top, newMods, top + 1, modules.length - top);
+                    newMods[top] = module;
+                    modules = newMods;
+                }
+            }
+
+            map.put(pkgName, modules);
+        }
+    }
+
+    private void indexFragment(Map map, IModule module)
+    {
+        String symName = getBundleSymbolicName(module);
+        IModule[] modules = (IModule[]) map.get(symName);
+
+        // We want to add the fragment into the list of matching
+        // fragments in sorted order (descending version and
+        // ascending bundle identifier). Insert using a simple
+        // binary search algorithm.
+        if (modules == null)
+        {
+            modules = new IModule[] { module };
+        }
+        else
+        {
+            Version version = getBundleVersion(module);
+            Version middleVersion = null;
+            int top = 0, bottom = modules.length - 1, middle = 0;
+            while (top <= bottom)
+            {
+                middle = (bottom - top) / 2 + top;
+                middleVersion = getBundleVersion(modules[middle]);
+                // Sort in reverse version order.
+                int cmp = middleVersion.compareTo(version);
+                if (cmp < 0)
+                {
+                    bottom = middle - 1;
+                }
+                else if (cmp == 0)
+                {
+                    // Sort further by ascending bundle ID.
+                    long middleId = Util.getBundleIdFromModuleId(modules[middle].getId());
+                    long exportId = Util.getBundleIdFromModuleId(module.getId());
+                    if (middleId < exportId)
+                    {
+                        top = middle + 1;
+                    }
+                    else
+                    {
+                        bottom = middle - 1;
+                    }
+                }
+                else
+                {
+                    top = middle + 1;
+                }
+            }
+
+            // Ignore duplicates.
+            if ((top >= modules.length) || (modules[top] != module))
+            {
+                IModule[] newMods = new IModule[modules.length + 1];
+                System.arraycopy(modules, 0, newMods, 0, top);
+                System.arraycopy(modules, top, newMods, top + 1, modules.length - top);
+                newMods[top] = module;
+                modules = newMods;
+            }
+        }
+
+        map.put(symName, modules);
+    }
+
+    private static IModule[] removeModuleFromArray(IModule[] modules, IModule m)
+    {
+        if (modules == null)
+        {
+            return m_emptyModules;
+        }
+
+        int idx = -1;
+        do
+        {
+            idx = -1;
+            for (int i = 0; i < modules.length; i++)
+            {
+                if (modules[i] == m)
+                {
+                    idx = i;
+                    break;
+                }
+            }
+
+            if (idx >= 0)
+            {
+                // If this is the module, then point to empty list.
+                if ((modules.length - 1) == 0)
+                {
+                    modules = m_emptyModules;
+                }
+                // Otherwise, we need to do some array copying.
+                else
+                {
+                    IModule[] newModules = new IModule[modules.length - 1];
+                    System.arraycopy(modules, 0, newModules, 0, idx);
+                    if (idx < newModules.length)
+                    {
+                        System.arraycopy(
+                            modules, idx + 1, newModules, idx, newModules.length - idx);
+                    }
+                    modules = newModules;
+                }
+            }
+        }
+        while (idx >= 0);
+
+        return modules;
+    }
+
+    public static ICapability getExportPackageCapability(IModule m, String pkgName)
+    {
+        ICapability[] caps = m.getDefinition().getCapabilities();
+        for (int i = 0; (caps != null) && (i < caps.length); i++)
+        {
+            if (caps[i].getNamespace().equals(ICapability.PACKAGE_NAMESPACE) &&
+                caps[i].getProperties().get(ICapability.PACKAGE_PROPERTY).equals(pkgName))
+            {
+                return caps[i];
+            }
+        }
+        return null;
+    }
+
+    private static ICapability[] addCapabilityToArray(ICapability[] caps, ICapability cap)
+    {
+        // Verify that the capability is not already in the array.
+        for (int i = 0; (caps != null) && (i < caps.length); i++)
+        {
+            if (caps[i].equals(cap))
+            {
+                return caps;
+            }
+        }
+
+        if (caps != null)
+        {
+            ICapability[] newCaps = new ICapability[caps.length + 1];
+            System.arraycopy(caps, 0, newCaps, 0, caps.length);
+            newCaps[caps.length] = cap;
+            caps = newCaps;
+        }
+        else
+        {
+            caps = new ICapability[] { cap };
+        }
+
+        return caps;
+    }
+    //
+    // Simple utility classes.
+    //
+
+    private static class ModuleData
+    {
+        public IModule m_module = null;
+        public boolean m_resolved = false;
+        public ModuleData(IModule module)
+        {
+            m_module = module;
+        }
+    }
+}
\ No newline at end of file

Added: felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/PackageSource.java
URL: http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/PackageSource.java?rev=729083&view=auto
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/PackageSource.java (added)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/searchpolicy/PackageSource.java Tue Dec 23 12:07:49 2008
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework.searchpolicy;
+
+import org.apache.felix.framework.util.Util;
+import org.apache.felix.framework.util.manifestparser.Capability;
+import org.apache.felix.moduleloader.ICapability;
+import org.apache.felix.moduleloader.IModule;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+/**
+ * This utility class represents a source for a given package, where
+ * the package is indicated by a particular module and the module's
+ * capability associated with that package. This class also implements
+ * <tt>Comparable</tt> so that two package sources can be compared based
+ * on version and bundle identifiers.
+ */
+public class PackageSource implements Comparable
+{
+    public IModule m_module = null;
+    public ICapability m_capability = null;
+
+    public PackageSource(IModule module, ICapability capability)
+    {
+        super();
+        m_module = module;
+        m_capability = capability;
+    }
+
+    public int compareTo(Object o)
+    {
+        PackageSource ps = (PackageSource) o;
+        Version thisVersion = null;
+        Version version = null;
+        if (m_capability.getNamespace().equals(ICapability.PACKAGE_NAMESPACE))
+        {
+            thisVersion = ((Capability) m_capability).getPackageVersion();
+            version = ((Capability) ps.m_capability).getPackageVersion();
+        }
+        else if (m_capability.getNamespace().equals(ICapability.MODULE_NAMESPACE))
+        {
+            thisVersion = (Version) m_capability.getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+            version = (Version) ps.m_capability.getProperties().get(Constants.BUNDLE_VERSION_ATTRIBUTE);
+        }
+        if ((thisVersion != null) && (version != null))
+        {
+            int cmp = thisVersion.compareTo(version);
+            if (cmp < 0)
+            {
+                return 1;
+            }
+            else if (cmp > 0)
+            {
+                return -1;
+            }
+            else
+            {
+                long thisId = Util.getBundleIdFromModuleId(m_module.getId());
+                long id = Util.getBundleIdFromModuleId(ps.m_module.getId());
+                if (thisId < id)
+                {
+                    return -1;
+                }
+                else if (thisId > id)
+                {
+                    return 1;
+                }
+                return 0;
+            }
+        }
+        else
+        {
+            return -1;
+        }
+    }
+
+    public int hashCode()
+    {
+        final int PRIME = 31;
+        int result = 1;
+        result = PRIME * result + ((m_capability == null) ? 0 : m_capability.hashCode());
+        result = PRIME * result + ((m_module == null) ? 0 : m_module.hashCode());
+        return result;
+    }
+
+    public boolean equals(Object o)
+    {
+        if (this == o)
+        {
+            return true;
+        }
+        if (o == null)
+        {
+            return false;
+        }
+        if (getClass() != o.getClass())
+        {
+            return false;
+        }
+        PackageSource ps = (PackageSource) o;
+        return m_module.equals(ps.m_module) && (m_capability == ps.m_capability);
+    }
+}