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 2006/01/27 14:01:25 UTC

svn commit: r372831 [2/2] - /incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/

Copied: incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/SearchPolicyImpl.java (from r370894, incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicy.java)
URL: http://svn.apache.org/viewcvs/incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/SearchPolicyImpl.java?p2=incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/SearchPolicyImpl.java&p1=incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicy.java&r1=370894&r2=372831&rev=372831&view=diff
==============================================================================
--- incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/R4SearchPolicy.java (original)
+++ incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/SearchPolicyImpl.java Fri Jan 27 05:01:17 2006
@@ -1,19 +1,3 @@
-/*
- *   Copyright 2005 The Apache Software Foundation
- *
- *   Licensed under the Apache License, Version 2.0 (the "License");
- *   you may not use this file except in compliance with the License.
- *   You may obtain a copy of the License at
- *
- *       http://www.apache.org/licenses/LICENSE-2.0
- *
- *   Unless required by applicable law or agreed to in writing, software
- *   distributed under the License is distributed on an "AS IS" BASIS,
- *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *   See the License for the specific language governing permissions and
- *   limitations under the License.
- *
- */
 package org.apache.felix.framework.searchpolicy;
 
 import java.net.URL;
@@ -22,10 +6,9 @@
 import org.apache.felix.framework.LogWrapper;
 import org.apache.felix.framework.util.SecurityManagerEx;
 import org.apache.felix.moduleloader.*;
-import org.apache.felix.moduleloader.search.ResolveException;
-import org.apache.felix.moduleloader.search.ResolveListener;
+import org.osgi.framework.Version;
 
-public class R4SearchPolicy implements SearchPolicy, ModuleListener
+public class SearchPolicyImpl implements ISearchPolicy, ModuleListener
 {
     // Array of R4Package.
     public static final String EXPORTS_ATTR = "exports";
@@ -39,7 +22,7 @@
     public static final String RESOLVED_ATTR = "resolved";
 
     private LogWrapper m_logger = null;
-    private ModuleManager m_mgr = null;
+    private IModuleFactory m_factory = null;
     private Map m_availPkgMap = new HashMap();
     private Map m_inUsePkgMap = new HashMap();
 
@@ -48,25 +31,31 @@
     private ResolveListener[] m_listeners = m_emptyListeners;
 
     // Reusable empty arrays.
-    public static final Module[] m_emptyModules = new Module[0];
-    public static final R4Package[] m_emptyPackages = new R4Package[0];
-    public static final R4Wire[] m_emptyWires = new R4Wire[0];
+    public static final IModule[] m_emptyModules = new IModule[0];
+    public static final IImport[] m_emptyImports= new IImport[0];
+    public static final IExport[] m_emptyExports= new IExport[0];
+    public static final IWire[] m_emptyWires = new IWire[0];
 
     // Re-usable security manager for accessing class context.
     private static SecurityManagerEx m_sm = new SecurityManagerEx();
 
-    public R4SearchPolicy(LogWrapper logger)
+    public SearchPolicyImpl(LogWrapper logger)
     {
         m_logger = logger;
     }
 
-    public void setModuleManager(ModuleManager mgr)
+    public IModuleFactory getModuleFactory()
+    {
+        return m_factory;
+    }
+
+    public void setModuleFactory(IModuleFactory factory)
         throws IllegalStateException
     {
-        if (m_mgr == null)
+        if (m_factory == null)
         {
-            m_mgr = mgr;
-            m_mgr.addModuleListener(this);
+            m_factory = factory;
+            m_factory.addModuleListener(this);
         }
         else
         {
@@ -74,27 +63,12 @@
         }
     }
 
-    public Object[] definePackage(Module module, String pkgName)
-    {
-        R4Package pkg = R4SearchPolicy.getExportPackage(module, pkgName);
-        if (pkg != null)
-        {
-            return new Object[]  {
-                pkgName, // Spec title.
-                pkg.getVersionLow().toString(), // Spec version.
-                "", // Spec vendor.
-                "", // Impl title.
-                "", // Impl version.
-                ""  // Impl vendor.
-            };
-        }
-        return null;
-    }
-
-    public Class findClassBeforeModule(ClassLoader parent, Module module, String name)
+    public Class findClass(IModule module, String name)
         throws ClassNotFoundException
     {
         // First, try to resolve the originating module.
+// TODO: Consider opimizing this call to resolve, since it is called
+// for each class load.
         try
         {
             resolve(module);
@@ -106,7 +80,7 @@
             // potentially leak internal module information.
             throw new ClassNotFoundException(
                 name + ": cannot resolve package "
-                + ex.getPackage() + ".");
+                + ex.getPackage());
         }
 
         // Get the package of the target class.
@@ -116,70 +90,89 @@
         // these packages cannot be provided by other bundles.
         if (pkgName.startsWith("java."))
         {
-            return (parent == null) ? null : parent.loadClass(name);
+            return this.getClass().getClassLoader().loadClass(name);
+        }
+
+        // Look in the module's imports.
+        Class clazz = findImportedClass(module, name, pkgName);
+
+        // If not found, try the module's own content.
+        if (clazz == null)
+        {
+            clazz = module.getContentLoader().getClass(name);
+
+            // If still not found, then try the module's dynamic imports.
+            if (clazz == null)
+            {
+                clazz = findDynamicallyImportedClass(module, name, pkgName);
+            }
+        }
+
+        if (clazz == null)
+        {
+            throw new ClassNotFoundException(name);
         }
 
+        return clazz;
+    }
+
+    private Class findImportedClass(IModule module, String name, String pkgName)
+        throws ClassNotFoundException
+    {
         // We delegate to the module's wires to find the class.
-        R4Wire[] wires = getWiringAttr(module);
+        IWire[] wires = (IWire[]) module.getAttribute(WIRING_ATTR, m_emptyWires);
         for (int i = 0; i < wires.length; i++)
         {
             // Only check when the package of the class is
-            // the same as the import package.
-            if (wires[i].m_pkg.getId().equals(pkgName))
+            // the same as the package for the wire.
+            if (wires[i].getExport().getName().equals(pkgName))
             {
-                // Before delegating to the module class loader to satisfy
-                // the class load, we must check the include/exclude filters
-                // from the target package to make sure that the class is
-                // actually visible. If the exporting module is the same as
-                // the requesting module, then filtering is not performed
-                // since a module has complete access to itself.
-// TODO: Determine if it is possible to modify Module Loader somehow
-// so that this check is done within the target module itself; it
-// doesn't totally make sense to do this check in the importing module.
-                if (wires[i].m_module != module)
-                {
-                    if (!wires[i].m_pkg.isIncluded(name))
-                    {
-                        throw new ClassNotFoundException(name);
-                    }
-                }
-
-                // Since the class is included, delegate to the exporting module.
-                try
+                // If we find the class, then return it. Otherwise,
+                // throw an exception since the provider of the
+                // package did not have the class.
+                Class clazz = wires[i].getClass(name);
+                if (clazz != null)
                 {
-                    Class clazz = wires[i].m_module.getClassLoader().loadClassFromModule(name);
-                    if (clazz != null)
-                    {
-                        return clazz;
-                    }
+                    return clazz;
                 }
-                catch (Throwable th)
+                else
                 {
-                    // Not much we can do here.
+                    throw new ClassNotFoundException(name);
                 }
-                throw new ClassNotFoundException(name);
             }
         }
 
         return null;
     }
 
-    public Class findClassAfterModule(ClassLoader parent, Module module, String name)
+    private Class findDynamicallyImportedClass(
+        IModule module, String name, String pkgName)
         throws ClassNotFoundException
     {
         // At this point, the module's imports were searched and so was the
-        // the module's own resources. Now we make an attempt to load the
+        // the module's content. Now we make an attempt to load the
         // class via a dynamic import, if possible.
-        String pkgName = Util.getClassPackage(name);
-        Module candidate = attemptDynamicImport(module, pkgName);
+        IWire wire = attemptDynamicImport(module, pkgName);
+
         // If the dynamic import was successful, then this initial
         // time we must directly return the result from dynamically
-        // selected candidate's class loader, but for subsequent
-        // requests for classes in the associated package will be
-        // processed as part of normal static imports.
-        if (candidate != null)
-        {
-            return candidate.getClassLoader().loadClass(name);
+        // created wire, but subsequent requests for classes in
+        // the associated package will be processed as part of
+        // normal static imports.
+        if (wire != null)
+        {
+            // If we find the class, then return it. Otherwise,
+            // throw an exception since the provider of the
+            // package did not have the class.
+            Class clazz = wire.getClass(name);
+            if (clazz != null)
+            {
+                return clazz;
+            }
+            else
+            {
+                throw new ClassNotFoundException(name);
+            }
         }
 
         // At this point, the class could not be found by the bundle's static
@@ -202,7 +195,7 @@
 
         // Get the class context to see the classes on the stack.
         Class[] classes = m_sm.getClassContext();
-        // Start from 1 to skip inner class.
+        // Start from 1 to skip security manager class.
         for (int i = 1; i < classes.length; i++)
         {
             // Find the first class on the call stack that is neither
@@ -214,9 +207,9 @@
                 // If the instigating class was not from a bundle, then
                 // delegate to the parent class loader. Otherwise, break
                 // out of loop and throw an exception.
-                if (!ModuleClassLoader.class.isInstance(classes[i].getClassLoader()))
+                if (!ContentClassLoader.class.isInstance(classes[i].getClassLoader()))
                 {
-                    return parent.loadClass(name);
+                    return this.getClass().getClassLoader().loadClass(name);
                 }
                 break;
             }
@@ -225,68 +218,163 @@
         throw new ClassNotFoundException(name);
     }
 
-    public URL findResource(ClassLoader parent, Module module, String name)
+    public URL findResource(IModule module, String name)
         throws ResourceNotFoundException
     {
         // First, try to resolve the originating module.
+// TODO: Consider opimizing this call to resolve, since it is called
+// for each class load.
         try
         {
             resolve(module);
         }
         catch (ResolveException ex)
         {
-            return null;
+            // We do not use the resolve exception as the
+            // cause of the exception, since this would
+            // potentially leak internal module information.
+            throw new ResourceNotFoundException(
+                name + ": cannot resolve package "
+                + ex.getPackage());
         }
 
-        // Get the package of the target resource.
+        // Get the package of the target class.
         String pkgName = Util.getResourcePackage(name);
 
-        // Load all "java.*" resources from parent class loader;
+        // Load all "java.*" classes from parent class loader;
         // these packages cannot be provided by other bundles.
         if (pkgName.startsWith("java."))
         {
-            return (parent == null) ? null : parent.getResource(name);
+            return this.getClass().getClassLoader().getResource(name);
         }
 
-        // We delegate to the module's wires to find the resource.
-        R4Wire[] wires = getWiringAttr(module);
-        for (int i = 0; i < wires.length; i++)
+        // Look in the module's imports.
+        URL url = findImportedResource(module, name, pkgName);
+
+        // If not found, try the module's own content.
+        if (url == null)
         {
-            // Only check when the package of the resource is
-            // the same as the import package.
-            if (wires[i].m_pkg.getId().equals(pkgName))
+            url = module.getContentLoader().getResource(name);
+
+            // If still not found, then try the module's dynamic imports.
+            if (url == null)
             {
-                try
+                url = findDynamicallyImportedResource(module, name, pkgName);
+            }
+        }
+
+        if (url == null)
+        {
+            throw new ResourceNotFoundException(name);
+        }
+
+        return url;
+    }
+
+    private URL findImportedResource(IModule module, String name, String pkgName)
+        throws ResourceNotFoundException
+    {
+        // We delegate to the module's wires to find the class.
+        IWire[] wires = (IWire[]) module.getAttribute(WIRING_ATTR, m_emptyWires);
+        for (int i = 0; i < wires.length; i++)
+        {
+            // Only check when the package of the class is
+            // the same as the package for the wire.
+            if (wires[i].getExport().getName().equals(pkgName))
+            {   
+                URL url = wires[i].getResource(name);
+                if (url != null)
                 {
-                    URL url = wires[i].m_module.getClassLoader().getResourceFromModule(name);
-                    if (url != null)
-                    {
-                        return url;
-                    }
+                    return url;
                 }
-                catch (Throwable th)
+                else
                 {
-                    // Not much we can do here.
+                    throw new ResourceNotFoundException(name);
                 }
-                throw new ResourceNotFoundException(name);
             }
         }
+    
+        return null;
+    }
 
-        // Check dynamic imports.
-// TODO: Dynamic imports should be searched after local sources.
-        Module candidate = attemptDynamicImport(module, pkgName);
-        // This initial time we must directly return the result from
-        // the candidate's class loaders, but since the candidate was
-        // added to the module's wiring attribute, subsequent class
-        // loads from the same package will be handled in the normal
-        // fashion for statically imported packaes.
-        return (candidate == null)
-            ? null : candidate.getClassLoader().getResource(name);
+    private URL findDynamicallyImportedResource(
+        IModule module, String name, String pkgName)
+        throws ResourceNotFoundException
+    {
+System.out.println("findDynamic " + name + " - " + pkgName);
+        // At this point, the module's imports were searched and so was the
+        // the module's content. Now we make an attempt to load the
+        // class via a dynamic import, if possible.
+        IWire wire = attemptDynamicImport(module, pkgName);
+        
+        // If the dynamic import was successful, then this initial
+        // time we must directly return the result from dynamically
+        // created wire, but subsequent requests for resources in
+        // the associated package will be processed as part of
+        // normal static imports.
+        if (wire != null)
+        {
+            // If we find the class, then return it. Otherwise,
+            // throw an exception since the provider of the
+            // package did not have the class.
+            URL url = wire.getResource(name);
+            if (url != null)
+            {
+                return url;
+            }
+            else
+            {
+                throw new ResourceNotFoundException(name);
+            }
+        }
+    
+        // At this point, the resource could not be found by the bundle's static
+        // or dynamic imports, nor its own resources. Before we throw
+        // an exception, we will try to determine if the instigator of the
+        // resource load was a class from a bundle or not. This is necessary
+        // because the specification mandates that classes on the class path
+        // should be hidden (except for java.*), but it does allow for these
+        // classes to be exposed by the system bundle as an export. However,
+        // in some situations classes on the class path make the faulty
+        // assumption that they can access everything on the class path from
+        // every other class loader that they come in contact with. This is
+        // not true if the class loader in question is from a bundle. Thus,
+        // this code tries to detect that situation. If the class
+        // instigating the resource load was NOT from a bundle, then we will
+        // make the assumption that the caller actually wanted to use the
+        // parent class loader and we will delegate to it. If the class was
+        // from a bundle, then we will enforce strict class loading rules
+        // for the bundle and throw a resource not found exception.
+    
+        // Get the class context to see the classes on the stack.
+        Class[] classes = m_sm.getClassContext();
+        // Start from 1 to skip security manager class.
+        for (int i = 1; i < classes.length; i++)
+        {
+            // Find the first class on the call stack that is neither
+            // a class loader or Class itself, because we want to ignore
+            // the calls to ClassLoader.loadClass() and Class.forName().
+            if (!ClassLoader.class.isAssignableFrom(classes[i]) &&
+                !Class.class.isAssignableFrom(classes[i]))
+            {
+                // If the instigating class was not from a bundle, then
+                // delegate to the parent class loader. Otherwise, break
+                // out of loop and throw an exception.
+                if (!ContentClassLoader.class.isInstance(classes[i].getClassLoader()))
+                {
+                    return this.getClass().getClassLoader().getResource(name);
+                }
+                break;
+            }
+        }
+        
+        throw new ResourceNotFoundException(name);
     }
 
-    private Module attemptDynamicImport(Module module, String pkgName)
+    private IWire attemptDynamicImport(IModule module, String pkgName)
     {
-        Module candidate = null;
+        IWire wire = null;
+        IModule candidate = null;
 
         // There is an overriding assumption here that a package is
         // never split across bundles. If a package can be split
@@ -296,27 +384,27 @@
         {
             // Check the dynamic import specs for a match of
             // the target package.
-            R4Package[] dynamics = getDynamicImportsAttr(module);
-            R4Package pkgMatch = null;
-            for (int i = 0; (pkgMatch == null) && (i < dynamics.length); i++)
+            IImport[] dynamics = (IImport[]) module.getAttribute(DYNAMICIMPORTS_ATTR, m_emptyImports);
+            IImport impMatch = null;
+            for (int i = 0; (impMatch == null) && (i < dynamics.length); i++)
             {
                 // Star matches everything.
-                if (dynamics[i].getId().equals("*"))
+                if (dynamics[i].getName().equals("*"))
                 {
                     // Create a package instance without wildcard.
-                    pkgMatch = new R4Package(
+                    impMatch = new ImportImpl(
                         pkgName,
                         dynamics[i].getDirectives(),
                         dynamics[i].getAttributes());
                 }
                 // Packages ending in ".*" must match starting strings.
-                else if (dynamics[i].getId().endsWith(".*"))
+                else if (dynamics[i].getName().endsWith(".*"))
                 {
                     if (pkgName.regionMatches(
-                        0, dynamics[i].getId(), 0, dynamics[i].getId().length() - 2))
+                        0, dynamics[i].getName(), 0, dynamics[i].getName().length() - 2))
                     {
                         // Create a package instance without wildcard.
-                        pkgMatch = new R4Package(
+                        impMatch = new ImportImpl(
                             pkgName,
                             dynamics[i].getDirectives(),
                             dynamics[i].getAttributes());
@@ -325,9 +413,9 @@
                 // Or we can have a precise match.
                 else
                 {
-                    if (pkgName.equals(dynamics[i].getId()))
+                    if (pkgName.equals(dynamics[i].getName()))
                     {
-                        pkgMatch = dynamics[i];
+                        impMatch = dynamics[i];
                     }
                 }
             }
@@ -336,7 +424,7 @@
             // packages or if the module is already wired for the target package,
             // then just return null. The module may be already wired to the target
             // package if the class being searched for does not actually exist.
-            if ((pkgMatch == null) || (getWire(module, pkgMatch.getId()) != null))
+            if ((impMatch == null) || (getWire(module, impMatch.getName()) != null))
             {
                 return null;
             }
@@ -347,14 +435,14 @@
             // of wires.
 
             // Lock module manager instance to ensure that nothing changes.
-            synchronized (m_mgr)
+            synchronized (m_factory)
             {
                 // Try to add a new entry to the module's import attribute.
                 // Select the first candidate that successfully resolves.
 
                 // First check already resolved exports for a match.
-                Module[] candidates = getCompatibleExporters(
-                    (Module[]) m_inUsePkgMap.get(pkgMatch.getId()), pkgMatch);
+                IModule[] candidates = getCompatibleExporters(
+                    (IModule[]) m_inUsePkgMap.get(impMatch.getName()), impMatch);
                 // If there is an "in use" candidate, just take the first one.
                 if (candidates.length > 0)
                 {
@@ -366,7 +454,7 @@
                 if (candidate == null)
                 {
                     candidates = getCompatibleExporters(
-                        (Module[]) m_availPkgMap.get(pkgMatch.getId()), pkgMatch);
+                        (IModule[]) m_availPkgMap.get(impMatch.getName()), impMatch);
                     for (int candIdx = 0;
                         (candidate == null) && (candIdx < candidates.length);
                         candIdx++)
@@ -386,14 +474,16 @@
                 // wiring attribute.
                 if (candidate != null)
                 {
-                    R4Wire[] wires = getWiringAttr(module);
-                    R4Wire[] newWires = new R4Wire[wires.length + 1];
+                    IWire[] wires = (IWire[]) module.getAttribute(WIRING_ATTR, m_emptyWires);
+                    IWire[] newWires = new IWire[wires.length + 1];
                     System.arraycopy(wires, 0, newWires, 0, wires.length);
                     // Find the candidate's export package object and
                     // use that for creating the wire; this is necessary
                     // since it contains "uses" dependency information.
-                    newWires[wires.length] = new R4Wire(
-                        getExportPackage(candidate, pkgMatch.getId()), candidate);
+                    wire = new WireImpl(
+                        module, candidate,
+                        getExportPackage(candidate, impMatch.getName()));
+                    newWires[wires.length] = wire;
                     module.setAttribute(WIRING_ATTR, newWires);
 m_logger.log(LogWrapper.LOG_DEBUG, "WIRE: [" + module + "] " + newWires[wires.length]);
                 }
@@ -404,45 +494,50 @@
             m_logger.log(LogWrapper.LOG_ERROR, "Unable to dynamically import package.", ex);
         }
 
-        return candidate;
+        return wire;
+    }
+
+    public URL createURL(IModule module, String path)
+    {
+        return null;
     }
 
-    public Module[] getAvailableExporters(R4Package pkg)
+    public IModule[] getAvailableExporters(IImport pkg)
     {
         // Synchronized on the module manager to make sure that no
         // modules are added, removed, or resolved.
-        synchronized (m_mgr)
+        synchronized (m_factory)
         {
-            return getCompatibleExporters((Module[]) m_availPkgMap.get(pkg.getId()), pkg);
+            return getCompatibleExporters((IModule[]) m_availPkgMap.get(pkg.getName()), pkg);
         }
     }
 
-    public Module[] getInUseExporters(R4Package pkg)
+    public IModule[] getInUseExporters(IImport pkg)
     {
         // Synchronized on the module manager to make sure that no
         // modules are added, removed, or resolved.
-        synchronized (m_mgr)
+        synchronized (m_factory)
         {
-            return getCompatibleExporters((Module[]) m_inUsePkgMap.get(pkg.getId()), pkg);
+            return getCompatibleExporters((IModule[]) m_inUsePkgMap.get(pkg.getName()), pkg);
         }
     }
 
-    public void resolve(Module rootModule)
+    public void resolve(IModule rootModule)
         throws ResolveException
     {
         // If the module is already resolved, then we can just return.
-        if (getResolvedAttr(rootModule).booleanValue())
+        if (((Boolean) rootModule.getAttribute(RESOLVED_ATTR, Boolean.FALSE)).booleanValue())
         {
             return;
         }
-
+    
         // This variable maps an unresolved module to a list of resolver
         // nodes, where there is one resolver node for each import that
         // must be resolved. A resolver node contains the potential
         // candidates to resolve the import and the current selected
         // candidate index.
         Map resolverMap = new HashMap();
-
+    
         // This map will be used to hold the final wires for all
         // resolved modules, which can then be used to fire resolved
         // events outside of the synchronized block.
@@ -451,7 +546,7 @@
         // Synchronize on the module manager, because we don't want
         // any modules being added or removed while we are in the
         // middle of this operation.
-        synchronized (m_mgr)
+        synchronized (m_factory)
         {
             // The first step is to populate the resolver map. This
             // will use the target module to populate the resolver map
@@ -466,8 +561,6 @@
             // transitive imports this implies.
             populateResolverMap(resolverMap, rootModule);
 
-//dumpResolverMap();
-
             // The next step is to use the resolver map to determine if
             // the class space for the root module is consistent. This
             // is an iterative process that transitively walks the "uses"
@@ -480,7 +573,7 @@
             // it exhausts all possible combinations and could not find a
             // consistent class space.
             findConsistentClassSpace(resolverMap, rootModule);
-
+    
             // The final step is to create the wires for the root module and
             // transitively all modules that are to be resolved from the
             // selected candidates for resolving the root module's imports.
@@ -490,7 +583,7 @@
             // The resolved module wire map maps a module to its array of
             // wires.
             resolvedModuleWireMap = createWires(resolverMap, rootModule);
-            
+        
 //dumpAvailablePackages();
 //dumpUsedPackages();
 
@@ -504,12 +597,12 @@
             Iterator iter = resolvedModuleWireMap.entrySet().iterator();
             while (iter.hasNext())
             {
-                fireModuleResolved((Module) ((Map.Entry) iter.next()).getKey());
+                fireModuleResolved((IModule) ((Map.Entry) iter.next()).getKey());
             }
         }
     }
 
-    private void populateResolverMap(Map resolverMap, Module module)
+    private void populateResolverMap(Map resolverMap, IModule module)
         throws ResolveException
     {
         // Detect cycles.
@@ -529,20 +622,20 @@
 
         // Loop through each import and calculate its resolving
         // set of candidates.
-        R4Package[] imports = getImportsAttr(module);
+        IImport[] imports = (IImport[]) module.getAttribute(IMPORTS_ATTR, m_emptyImports);
         for (int impIdx = 0; impIdx < imports.length; impIdx++)
         {
             // Get the candidates from the "in use" and "available"
             // package maps. Candidates "in use" have higher priority
             // than "available" ones, so put the "in use" candidates
             // at the front of the list of candidates.
-            Module[] inuse = getCompatibleExporters(
-                (Module[]) m_inUsePkgMap.get(
-                    imports[impIdx].getId()), imports[impIdx]);
-            Module[] available = getCompatibleExporters(
-                (Module[]) m_availPkgMap.get(
-                    imports[impIdx].getId()), imports[impIdx]);
-            Module[] candidates = new Module[inuse.length + available.length];
+            IModule[] inuse = getCompatibleExporters(
+                (IModule[]) m_inUsePkgMap.get(
+                    imports[impIdx].getName()), imports[impIdx]);
+            IModule[] available = getCompatibleExporters(
+                (IModule[]) m_availPkgMap.get(
+                    imports[impIdx].getName()), imports[impIdx]);
+            IModule[] candidates = new IModule[inuse.length + available.length];
             System.arraycopy(inuse, 0, candidates, 0, inuse.length);
             System.arraycopy(available, 0, candidates, inuse.length, available.length);
 
@@ -557,7 +650,7 @@
                     {
                         // Only populate the resolver map with modules that
                         // are not already resolved.
-                        if (!getResolvedAttr(candidates[candIdx]).booleanValue())
+                        if (!((Boolean) candidates[candIdx].getAttribute(RESOLVED_ATTR, Boolean.FALSE)).booleanValue())
                         {
                             populateResolverMap(resolverMap, candidates[candIdx]);
                         }
@@ -606,27 +699,70 @@
         }
     }
 
-    /**
-     * <p>
-     * This method searches the resolver solution space for a consistent
-     * set of modules to resolve all transitive imports that must be resolved
-     * as a result of resolving the root module. A consistent set of modules
-     * is one where the "uses" relationships of the exported packages for
-     * the selected provider modules do not conflict with each other. A
-     * conflict can occur when the constraints on two or more modules result
-     * in multiple providers for the same package in the same class space.
-     * </p>
-     * @param resolverMap a map containing all potential modules that may need
-     *        to be resolved and the candidates to resolve them.
-     * @param rootModule the module that is the root of the resolve operation.
-     * @throws ResolveException if no consistent set of candidates can be
-     *         found to resolve the root module.
-    **/
-    private void findConsistentClassSpace(Map resolverMap, Module rootModule)
+
+//  TODO: REMOVE THESE DEBUG METHODS.
+    private void dumpAvailablePackages()
+    {
+        synchronized (this)
+        {
+            System.out.println("AVAILABLE PACKAGES:");
+            for (Iterator i = m_availPkgMap.entrySet().iterator(); i.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) i.next();
+                System.out.println("  " + entry.getKey());
+                IModule[] modules = (IModule[]) entry.getValue();
+                for (int j = 0; j < modules.length; j++)
+                {
+                    System.out.println("    " + modules[j]);
+                }
+            }
+        }
+    }
+
+    private void dumpUsedPackages()
+    {
+        synchronized (this)
+        {
+            System.out.println("USED PACKAGES:");
+            for (Iterator i = m_inUsePkgMap.entrySet().iterator(); i.hasNext(); )
+            {
+                Map.Entry entry = (Map.Entry) i.next();
+                System.out.println("  " + entry.getKey());
+                IModule[] modules = (IModule[]) entry.getValue();
+                for (int j = 0; j < modules.length; j++)
+                {
+                    System.out.println("    " + modules[j]);
+                }
+            }
+        }
+    }
+
+    protected IModule[] getCompatibleExporters(IModule[] modules, IImport target)
+    {
+        // Create list of compatible exporters.
+        IModule[] candidates = null;
+        for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
+        {
+            // Get the modules export package for the target package.
+            IExport export = getExportPackage(modules[modIdx], target.getName());
+            // If compatible, then add the candidate to the list.
+            if ((export != null) && (target.isSatisfied(export)))
+            {
+                candidates = addModuleToArray(candidates, modules[modIdx]);
+            }
+        }
+        if (candidates == null)
+        {
+            return m_emptyModules;
+        }
+        return candidates;
+    }
+
+    private void findConsistentClassSpace(Map resolverMap, IModule rootModule)
         throws ResolveException
     {
         List resolverList = null;
-
+    
         // Test the current set of candidates to determine if they
         // are consistent. Keep looping until we find a consistent
         // set or an exception is thrown.
@@ -649,47 +785,47 @@
                     resolverList.add((List) ((Map.Entry) iter.next()).getValue());
                 }
             }
-
+    
             // Increment the candidate configuration so we can test again.
             incrementCandidateConfiguration(resolverList);
-
+    
             // Clear the cycle map.
             cycleMap.clear();
         }
     }
 
     private boolean isClassSpaceConsistent(
-        Map resolverMap, Module rootModule, Map cycleMap)
+        Map resolverMap, IModule rootModule, Map cycleMap)
     {
         // We do not need to verify that already resolved modules
         // have consistent class spaces because they should be
         // consistent by definition. Also, if the root module is
         // part of a cycle, then just assume it is true.
-        if (getResolvedAttr(rootModule).booleanValue() ||
+        if (((Boolean) rootModule.getAttribute(RESOLVED_ATTR, Boolean.FALSE)).booleanValue() ||
             (cycleMap.get(rootModule) != null))
         {
             return true;
         }
-
+    
         // Add to cycle map for future reference.
         cycleMap.put(rootModule, rootModule);
-
+    
         // Create an implicit "uses" constraint for every exported package
         // of the root module that is not also imported; uses constraints
         // for exported packages that are also imported will be taken
         // care of as part of normal import package processing.
-        R4Package[] exports = (R4Package[]) getExportsAttr(rootModule);
+        IExport[] exports = (IExport[]) rootModule.getAttribute(EXPORTS_ATTR, m_emptyExports);
         Map usesMap = new HashMap();
         for (int i = 0; i < exports.length; i++)
         {
             // Ignore exports that are also imported, since they
             // will be taken care of when verifying import constraints.
-            if (getImportPackage(rootModule, exports[i].getId()) == null)
+            if (getImportPackage(rootModule, exports[i].getName()) == null)
             {
-                usesMap.put(exports[i].getId(), rootModule);
+                usesMap.put(exports[i].getName(), rootModule);
             }
         }
-
+    
         // Loop through the current candidates for the module's imports
         // (available in the resolver node list of the resolver map) and
         // calculate the uses constraints for each of the currently
@@ -704,10 +840,10 @@
             // calculating the candidate's transitive "uses" constraints
             // for the provided package and testing whether they
             // overlap with existing constraints.
-
+    
             // First, get the resolver node.
             ResolverNode node = (ResolverNode) nodeList.get(nodeIdx);
-
+    
             // Verify that the current candidate itself has a consistent
             // class space.
             if (!isClassSpaceConsistent(
@@ -715,12 +851,12 @@
             {
                 return false;
             }
-
+    
             // Get the exported package from the current candidate that
             // will be used to resolve the root module's import.
-            R4Package candidatePkg = getExportPackage(
-                node.m_candidates[node.m_idx], node.m_pkg.getId());
-
+            IExport candidatePkg = getExportPackage(
+                node.m_candidates[node.m_idx], node.m_import.getName());
+    
             // Calculate the "uses" dependencies implied by the candidate's
             // exported package with respect to the currently selected
             // candidates in the resolver map.
@@ -749,38 +885,38 @@
                     return false;
                 }
             }
-
+    
             // Since the current candidate's uses constraints did not
             // conflict with existing constraints, merge all constraints
             // and keep testing the remaining candidates for the other
             // imports of the root module.
             usesMap.putAll(candUsesMap);
         }
-
+    
         return true;
     }
 
     private Map calculateUsesDependencies(
-        Map resolverMap, Module module, R4Package exportPkg, Map usesMap)
+        Map resolverMap, IModule module, IExport export, Map usesMap)
     {
 // TODO: CAN THIS BE OPTIMIZED?
 // TODO: IS THIS CYCLE CHECK CORRECT??
 // TODO: WHAT HAPPENS THERE ARE OVERLAPS WHEN CALCULATING USES??
 //       MAKE AN EXAMPLE WHERE TWO DEPENDENCIES PROVIDE SAME PACKAGE.
         // Make sure we are not in a cycle.
-        if (usesMap.get(exportPkg.getId()) != null)
+        if (usesMap.get(export.getName()) != null)
         {
             return usesMap;
         }
 
         // The target package at least uses itself,
         // so add it to the uses map.
-        usesMap.put(exportPkg.getId(), module);
+        usesMap.put(export.getName(), module);
 
         // Get the "uses" constraints for the target export
         // package and calculate the transitive uses constraints
         // of any used packages.
-        String[] uses = exportPkg.getUses();
+        String[] uses = export.getUses();
         List nodeList = (List) resolverMap.get(module);
 
         // We need to walk the transitive closure of "uses" relationships
@@ -803,19 +939,19 @@
                 // Get the actual exporter from the wire or if there
                 // is no wire, then get the export is from the module
                 // itself.
-                R4Wire wire = getWire(module, uses[usesIdx]);
+                IWire wire = getWire(module, uses[usesIdx]);
                 if (wire != null)
                 {
                     usesMap = calculateUsesDependencies(
-                        resolverMap, wire.m_module, wire.m_pkg, usesMap);
+                        resolverMap, wire.getExportingModule(), wire.getExport(), usesMap);
                 }
                 else
                 {
-                    exportPkg = getExportPackage(module, uses[usesIdx]);
-                    if (exportPkg != null)
+                    export = getExportPackage(module, uses[usesIdx]);
+                    if (export != null)
                     {
                         usesMap = calculateUsesDependencies(
-                            resolverMap, module, exportPkg, usesMap);
+                            resolverMap, module, export, usesMap);
                     }
                 }
             }
@@ -829,7 +965,7 @@
                     nodeIdx++)
                 {
                     node = (ResolverNode) nodeList.get(nodeIdx);
-                    if (!node.m_pkg.getId().equals(uses[usesIdx]))
+                    if (!node.m_import.getName().equals(uses[usesIdx]))
                     {
                         node = null;
                     }
@@ -844,7 +980,7 @@
                     usesMap = calculateUsesDependencies(
                         resolverMap,
                         node.m_candidates[node.m_idx],
-                        getExportPackage(node.m_candidates[node.m_idx], node.m_pkg.getId()),
+                        getExportPackage(node.m_candidates[node.m_idx], node.m_import.getName()),
                         usesMap);
                 }
                 // If there was no resolver node for the "used" package,
@@ -865,32 +1001,6 @@
         return usesMap;
     }
 
-    /**
-     * <p>
-     * This method <i>increments</i> the current candidate configuration
-     * in the specified resolver list, which contains resolver node lists
-     * for all of the candidates for all of the imports that need to be
-     * resolved. This method performs its function by treating the current
-     * candidate index variable in each resolver node as part of a big
-     * counter. In other words, it increments the least significant index.
-     * If the index overflows it sets it back to zero and carries the
-     * overflow to the next significant index and so on. Using this approach
-     * it checks every possible combination for a solution.
-     * </p>
-     * <p>
-     * This method is inefficient and a better approach is necessary. For
-     * example, it does not take into account which imports are actually
-     * being used, it just increments starting at the beginning of the list.
-     * This means that it could be modifying candidates that are not relevant
-     * to the current configuration and re-testing even though nothing has
-     * really changed. It needs to be smarter.
-     * </p>
-     * @param resolverList an ordered list of resolver node lists for all
-     *        the candidates of the potential imports that need to be
-     *        resolved.
-     * @throws ResolveException if the increment overflows the entire list,
-     *         signifying no consistent configurations exist.
-    **/
     private void incrementCandidateConfiguration(List resolverList)
         throws ResolveException
     {
@@ -920,7 +1030,7 @@
             "Unable to resolve due to constraint violation.", null, null);
     }
 
-    private Map createWires(Map resolverMap, Module rootModule)
+    private Map createWires(Map resolverMap, IModule rootModule)
     {
         Map resolvedModuleWireMap =
             populateWireMap(resolverMap, rootModule, new HashMap());
@@ -928,9 +1038,9 @@
         while (iter.hasNext())
         {
             Map.Entry entry = (Map.Entry) iter.next();
-            Module module = (Module) entry.getKey();
-            R4Wire[] wires = (R4Wire[]) entry.getValue();
-
+            IModule module = (IModule) entry.getKey();
+            IWire[] wires = (IWire[]) entry.getValue();
+    
             // Set the module's resolved and wiring attribute.
             module.setAttribute(RESOLVED_ATTR, Boolean.TRUE);
             // Only add wires attribute if some exist; export
@@ -939,7 +1049,7 @@
             {
                 module.setAttribute(WIRING_ATTR, wires);
             }
-
+    
             // Remove the wire's exporting module from the "available"
             // package map and put it into the "in use" package map;
             // these steps may be a no-op.
@@ -949,10 +1059,10 @@
             {
 m_logger.log(LogWrapper.LOG_DEBUG, "WIRE: [" + module + "] " + wires[wireIdx]);
                 // First remove the wire module from "available" package map.
-                Module[] modules = (Module[]) m_availPkgMap.get(wires[wireIdx].m_pkg.getId());
-                modules = removeModuleFromArray(modules, wires[wireIdx].m_module);
-                m_availPkgMap.put(wires[wireIdx].m_pkg.getId(), modules);
-
+                IModule[] modules = (IModule[]) m_availPkgMap.get(wires[wireIdx].getExport().getName());
+                modules = removeModuleFromArray(modules, wires[wireIdx].getExportingModule());
+                m_availPkgMap.put(wires[wireIdx].getExport().getName(), modules);
+    
                 // Also remove any exported packages from the "available"
                 // package map that are from the module associated with
                 // the current wires where the exported packages were not
@@ -961,291 +1071,81 @@
                 // to a different module. If the exported package is not
                 // actually exported, then we just want to remove it
                 // completely, since it cannot be used.
-                if (wires[wireIdx].m_module != module)
+                if (wires[wireIdx].getExportingModule() != module)
                 {
-                    modules = (Module[]) m_availPkgMap.get(wires[wireIdx].m_pkg.getId());
+                    modules = (IModule[]) m_availPkgMap.get(wires[wireIdx].getExport().getName());
                     modules = removeModuleFromArray(modules, module);
-                    m_availPkgMap.put(wires[wireIdx].m_pkg.getId(), modules);
+                    m_availPkgMap.put(wires[wireIdx].getExport().getName(), modules);
                 }
-
+    
                 // Add the module of the wire to the "in use" package map.
-                modules = (Module[]) m_inUsePkgMap.get(wires[wireIdx].m_pkg.getId());
-                modules = addModuleToArray(modules, wires[wireIdx].m_module);
-                m_inUsePkgMap.put(wires[wireIdx].m_pkg.getId(), modules);
+                modules = (IModule[]) m_inUsePkgMap.get(wires[wireIdx].getExport().getName());
+                modules = addModuleToArray(modules, wires[wireIdx].getExportingModule());
+                m_inUsePkgMap.put(wires[wireIdx].getExport().getName(), modules);
             }
         }
         return resolvedModuleWireMap;
     }
 
-    private Map populateWireMap(Map resolverMap, Module module, Map wireMap)
+    private Map populateWireMap(Map resolverMap, IModule module, Map wireMap)
     {
         // If the module is already resolved or it is part of
         // a cycle, then just return the wire map.
-        if (getResolvedAttr(module).booleanValue() ||
+        if (((Boolean) module.getAttribute(RESOLVED_ATTR, Boolean.FALSE)).booleanValue() ||
             (wireMap.get(module) != null))
         {
             return wireMap;
         }
-
+    
         List nodeList = (List) resolverMap.get(module);
-        R4Wire[] wires = new R4Wire[nodeList.size()];
-
+        IWire[] wires = new IWire[nodeList.size()];
+    
         // Put the module in the wireMap with an empty wire array;
         // we do this early so we can use it to detect cycles.
         wireMap.put(module, wires);
-
+    
         // Loop through each resolver node and create a wire
         // for the selected candidate for the associated import.
         for (int nodeIdx = 0; nodeIdx < nodeList.size(); nodeIdx++)
         {
             // Get the import's associated resolver node.
             ResolverNode node = (ResolverNode) nodeList.get(nodeIdx);
-
+    
             // Add the candidate to the list of wires.
-            R4Package exportPkg =
-                getExportPackage(node.m_candidates[node.m_idx], node.m_pkg.getId());
-            wires[nodeIdx] = new R4Wire(exportPkg, node.m_candidates[node.m_idx]);
-
+            IExport export =
+                getExportPackage(node.m_candidates[node.m_idx], node.m_import.getName());
+            wires[nodeIdx] = new WireImpl(module, node.m_candidates[node.m_idx], export);
+    
             // Create the wires for the selected candidate module.
             wireMap = populateWireMap(resolverMap, node.m_candidates[node.m_idx], wireMap);
         }
-
+    
         return wireMap;
     }
 
-// TODO: REMOVE THESE DEBUG METHODS.
-    private void dumpResolverMap(Map resolverMap)
+    //
+    // Event handling methods for validation events.
+    //
+
+    /**
+     * Adds a resolver listener to the search policy. Resolver
+     * listeners are notified when a module is resolve and/or unresolved
+     * by the search policy.
+     * @param l the resolver listener to add.
+    **/
+    public void addResolverListener(ResolveListener l)
     {
-        Iterator iter = resolverMap.entrySet().iterator();
-        while (iter.hasNext())
+        // Verify listener.
+        if (l == null)
         {
-            Map.Entry entry = (Map.Entry) iter.next();
-            ResolverNode node = (ResolverNode) entry.getValue();
-            System.out.println("MODULE " + node.m_module + " IMPORT " + node.m_pkg);
-            for (int i = 0; i < node.m_candidates.length; i++)
-            {
-                System.out.println("--> " + node.m_candidates[i]);
-            }
+            throw new IllegalArgumentException("Listener is null");
         }
-    }
 
-    private void dumpAvailablePackages()
-    {
-        synchronized (m_mgr)
+        // Use the m_noListeners object as a lock.
+        synchronized (m_emptyListeners)
         {
-            System.out.println("AVAILABLE PACKAGES:");
-            for (Iterator i = m_availPkgMap.entrySet().iterator(); i.hasNext(); )
-            {
-                Map.Entry entry = (Map.Entry) i.next();
-                System.out.println("  " + entry.getKey());
-                Module[] modules = (Module[]) entry.getValue();
-                for (int j = 0; j < modules.length; j++)
-                {
-                    System.out.println("    " + modules[j]);
-                }
-            }
-        }
-    }
-
-    private void dumpUsedPackages()
-    {
-        synchronized (m_mgr)
-        {
-            System.out.println("USED PACKAGES:");
-            for (Iterator i = m_inUsePkgMap.entrySet().iterator(); i.hasNext(); )
-            {
-                Map.Entry entry = (Map.Entry) i.next();
-                System.out.println("  " + entry.getKey());
-                Module[] modules = (Module[]) entry.getValue();
-                for (int j = 0; j < modules.length; j++)
-                {
-                    System.out.println("    " + modules[j]);
-                }
-            }
-        }
-    }
-
-    /**
-     * This method returns a list of modules that have an export
-     * that is compatible with the given import identifier and version.
-     * @param pkgMap a map of export packages to exporting modules.
-     * @param target the target import package.
-     * @return an array of modules that have compatible exports or <tt>null</tt>
-     *         if none are found.
-    **/
-    protected Module[] getCompatibleExporters(Module[] modules, R4Package target)
-    {
-        // Create list of compatible exporters.
-        Module[] candidates = null;
-        for (int modIdx = 0; (modules != null) && (modIdx < modules.length); modIdx++)
-        {
-            // Get the modules export package for the target package.
-            R4Package exportPkg = getExportPackage(modules[modIdx], target.getId());
-            // If compatible, then add the candidate to the list.
-            if ((exportPkg != null) && (exportPkg.doesSatisfy(target)))
-            {
-                candidates = addModuleToArray(candidates, modules[modIdx]);
-            }
-        }
-
-        if (candidates == null)
-        {
-            return m_emptyModules;
-        }
-
-        return candidates;
-    }
-
-    public void moduleAdded(ModuleEvent event)
-    {
-        // When a module is added to the system, we need to initialize
-        // its resolved and wiring attributes and add its exports to
-        // the map of available exports.
-
-        // Synchronize on the module manager, since we don't want any
-        // bundles to be installed or removed.
-        synchronized (m_mgr)
-        {
-            // Add wiring attribute.
-            event.getModule().setAttribute(WIRING_ATTR, null);
-            // Add resolved attribute.
-            event.getModule().setAttribute(RESOLVED_ATTR, Boolean.FALSE);
-            // Add exports to available package map.
-            R4Package[] exports = getExportsAttr(event.getModule());
-            for (int i = 0; i < exports.length; i++)
-            {
-                Module[] modules = (Module[]) m_availPkgMap.get(exports[i].getId());
-
-                // We want to add the module into the list of available
-                // exporters in sorted order (descending version and
-                // ascending bundle identifier). Insert using a simple
-                // binary search algorithm.
-                if (modules == null)
-                {
-                    modules = new Module[] { event.getModule() };
-                }
-                else
-                {
-                    int top = 0, bottom = modules.length - 1, middle = 0;
-                    R4Version middleVersion = null;
-                    while (top <= bottom)
-                    {
-                        middle = (bottom - top) / 2 + top;
-                        middleVersion = getExportPackage(
-                            modules[middle], exports[i].getId()).getVersionLow();
-                        // Sort in reverse version order.
-                        int cmp = middleVersion.compareTo(exports[i].getVersionLow());
-                        if (cmp < 0)
-                        {
-                            bottom = middle - 1;
-                        }
-                        else if (cmp == 0)
-                        {
-                            // Sort further by ascending bundle ID.
-                            long middleId = getBundleIdFromModuleId(modules[middle].getId());
-                            long exportId = getBundleIdFromModuleId(event.getModule().getId());
-                            if (middleId < exportId)
-                            {
-                                top = middle + 1;
-                            }
-                            else
-                            {
-                                bottom = middle - 1;
-                            }
-                        }
-                        else
-                        {
-                            top = middle + 1;
-                        }
-                    }
-
-                    Module[] newMods = new Module[modules.length + 1];
-                    System.arraycopy(modules, 0, newMods, 0, top);
-                    System.arraycopy(modules, top, newMods, top + 1, modules.length - top);
-                    newMods[top] = event.getModule();
-                    modules = newMods;
-                }
-
-                m_availPkgMap.put(exports[i].getId(), modules);
-            }
-        }
-    }
-
-    public void moduleReset(ModuleEvent event)
-    {
-        moduleRemoved(event);
-    }
-
-    public void moduleRemoved(ModuleEvent event)
-    {
-        // When a module is removed from the system, we need remove
-        // its exports from the "in use" and "available" package maps.
-
-        // Synchronize on the module manager, since we don't want any
-        // bundles to be installed or removed.
-        synchronized (m_mgr)
-        {
-            // Remove exports from package maps.
-            R4Package[] pkgs = getExportsAttr(event.getModule());
-            for (int i = 0; i < pkgs.length; i++)
-            {
-                // Remove from "available" package map.
-                Module[] modules = (Module[]) m_availPkgMap.get(pkgs[i].getId());
-                if (modules != null)
-                {
-                    modules = removeModuleFromArray(modules, event.getModule());
-                    m_availPkgMap.put(pkgs[i].getId(), modules);
-                }
-                // Remove from "in use" package map.
-                modules = (Module[]) m_inUsePkgMap.get(pkgs[i].getId());
-                if (modules != null)
-                {
-                    modules = removeModuleFromArray(modules, event.getModule());
-                    m_inUsePkgMap.put(pkgs[i].getId(), modules);
-                }
-            }
-        }
-    }
-
-    // This is duplicated from BundleInfo and probably shouldn't be,
-    // but its functionality is needed by the moduleAdded() callback.
-    protected static long getBundleIdFromModuleId(String id)
-    {
-        try
-        {
-            String bundleId = (id.indexOf('.') >= 0)
-                ? id.substring(0, id.indexOf('.')) : id;
-            return Long.parseLong(bundleId);
-        }
-        catch (NumberFormatException ex)
-        {
-            return -1;
-        }
-    }
-
-    //
-    // Event handling methods for validation events.
-    //
-
-    /**
-     * Adds a resolver listener to the search policy. Resolver
-     * listeners are notified when a module is resolve and/or unresolved
-     * by the search policy.
-     * @param l the resolver listener to add.
-    **/
-    public void addResolverListener(ResolveListener l)
-    {
-        // Verify listener.
-        if (l == null)
-        {
-            throw new IllegalArgumentException("Listener is null");
-        }
-
-        // Use the m_noListeners object as a lock.
-        synchronized (m_emptyListeners)
-        {
-            // If we have no listeners, then just add the new listener.
-            if (m_listeners == m_emptyListeners)
+            // If we have no listeners, then just add the new listener.
+            if (m_listeners == m_emptyListeners)
             {
                 m_listeners = new ResolveListener[] { l };
             }
@@ -1322,7 +1222,7 @@
      * Fires a validation event for the specified module.
      * @param module the module that was resolved.
     **/
-    protected void fireModuleResolved(Module module)
+    protected void fireModuleResolved(IModule module)
     {
         // Event holder.
         ModuleEvent event = null;
@@ -1337,7 +1237,7 @@
             // Lazily create event.
             if (event == null)
             {
-                event = new ModuleEvent(m_mgr, module);
+                event = new ModuleEvent(m_factory, module);
             }
             listeners[i].moduleResolved(event);
         }
@@ -1347,7 +1247,7 @@
      * Fires an unresolved event for the specified module.
      * @param module the module that was unresolved.
     **/
-    protected void fireModuleUnresolved(Module module)
+    protected void fireModuleUnresolved(IModule module)
     {
         // Event holder.
         ModuleEvent event = null;
@@ -1362,60 +1262,145 @@
             // Lazily create event.
             if (event == null)
             {
-                event = new ModuleEvent(m_mgr, module);
+                event = new ModuleEvent(m_factory, module);
             }
             listeners[i].moduleUnresolved(event);
         }
     }
 
-    //
-    // Static utility methods.
-    //
+    public void moduleAdded(ModuleEvent event)
+    {
+        // When a module is added to the system, we need to initialize
+        // its resolved and wiring attributes and add its exports to
+        // the map of available exports.
+
+        // Synchronize on the module manager, since we don't want any
+        // bundles to be installed or removed.
+        synchronized (m_factory)
+        {
+            // Add wiring attribute.
+            event.getModule().setAttribute(WIRING_ATTR, null);
+            // Add resolved attribute.
+            event.getModule().setAttribute(RESOLVED_ATTR, Boolean.FALSE);
+            // Add exports to available package map.
+            IExport[] exports = (IExport[]) event.getModule().getAttribute(EXPORTS_ATTR, m_emptyExports);
+            for (int i = 0; i < exports.length; i++)
+            {
+                IModule[] modules = (IModule[]) m_availPkgMap.get(exports[i].getName());
+
+                // We want to add the module into the list of available
+                // exporters in sorted order (descending version and
+                // ascending bundle identifier). Insert using a simple
+                // binary search algorithm.
+                if (modules == null)
+                {
+                    modules = new IModule[] { event.getModule() };
+                }
+                else
+                {
+                    int top = 0, bottom = modules.length - 1, middle = 0;
+                    Version middleVersion = null;
+                    while (top <= bottom)
+                    {
+                        middle = (bottom - top) / 2 + top;
+                        middleVersion = getExportPackage(
+                            modules[middle], exports[i].getName()).getVersion();
+                        // Sort in reverse version order.
+                        int cmp = middleVersion.compareTo(exports[i].getVersion());
+                        if (cmp < 0)
+                        {
+                            bottom = middle - 1;
+                        }
+                        else if (cmp == 0)
+                        {
+                            // Sort further by ascending bundle ID.
+                            long middleId = getBundleIdFromModuleId(modules[middle].getId());
+                            long exportId = getBundleIdFromModuleId(event.getModule().getId());
+                            if (middleId < exportId)
+                            {
+                                top = middle + 1;
+                            }
+                            else
+                            {
+                                bottom = middle - 1;
+                            }
+                        }
+                        else
+                        {
+                            top = middle + 1;
+                        }
+                    }
+
+                    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] = event.getModule();
+                    modules = newMods;
+                }
+
+                m_availPkgMap.put(exports[i].getName(), modules);
+            }
+        }
+    }
 
-    public static Boolean getResolvedAttr(Module m)
+    // This is duplicated from BundleInfo and probably shouldn't be,
+    // but its functionality is needed by the moduleAdded() callback.
+    protected static long getBundleIdFromModuleId(String id)
     {
-        Boolean b =
-            (Boolean) m.getAttribute(RESOLVED_ATTR);
-        if (b == null)
+        try
         {
-            b = Boolean.FALSE;
+            String bundleId = (id.indexOf('.') >= 0)
+                ? id.substring(0, id.indexOf('.')) : id;
+            return Long.parseLong(bundleId);
+        }
+        catch (NumberFormatException ex)
+        {
+            return -1;
         }
-        return b;
     }
 
-    public static R4Package[] getExportsAttr(Module m)
+    public void moduleReset(ModuleEvent event)
     {
-        R4Package[] attr =
-            (R4Package[]) m.getAttribute(EXPORTS_ATTR);
-        return (attr == null) ? m_emptyPackages : attr;
+        moduleRemoved(event);
     }
 
-    public static R4Package getExportPackage(Module m, String id)
+    public void moduleRemoved(ModuleEvent event)
     {
-        R4Package[] pkgs = getExportsAttr(m);
-        for (int i = 0; (pkgs != null) && (i < pkgs.length); i++)
+        // When a module is removed from the system, we need remove
+        // its exports from the "in use" and "available" package maps.
+
+        // Synchronize on the module manager, since we don't want any
+        // bundles to be installed or removed.
+        synchronized (m_factory)
         {
-            if (pkgs[i].getId().equals(id))
+            // Remove exports from package maps.
+            IExport[] exports = (IExport[]) event.getModule().getAttribute(EXPORTS_ATTR, m_emptyExports);
+            for (int i = 0; i < exports.length; i++)
             {
-                return pkgs[i];
+                // Remove from "available" package map.
+                IModule[] modules = (IModule[]) m_availPkgMap.get(exports[i].getName());
+                if (modules != null)
+                {
+                    modules = removeModuleFromArray(modules, event.getModule());
+                    m_availPkgMap.put(exports[i].getName(), modules);
+                }
+                // Remove from "in use" package map.
+                modules = (IModule[]) m_inUsePkgMap.get(exports[i].getName());
+                if (modules != null)
+                {
+                    modules = removeModuleFromArray(modules, event.getModule());
+                    m_inUsePkgMap.put(exports[i].getName(), modules);
+                }
             }
         }
-        return null;
     }
 
-    public static R4Package[] getImportsAttr(Module m)
+    public static IExport getExportPackage(IModule m, String name)
     {
-        R4Package[] attr =
-            (R4Package[]) m.getAttribute(IMPORTS_ATTR);
-        return (attr == null) ? m_emptyPackages: attr;
-    }
-
-    public static R4Package getImportPackage(Module m, String id)
-    {
-        R4Package[] pkgs = getImportsAttr(m);
+        IExport[] pkgs = (IExport[]) m.getAttribute(EXPORTS_ATTR, m_emptyExports);
         for (int i = 0; (pkgs != null) && (i < pkgs.length); i++)
         {
-            if (pkgs[i].getId().equals(id))
+            if (pkgs[i].getName().equals(name))
             {
                 return pkgs[i];
             }
@@ -1423,19 +1408,12 @@
         return null;
     }
 
-    public static R4Package[] getDynamicImportsAttr(Module m)
-    {
-        R4Package[] attr =
-            (R4Package[]) m.getAttribute(DYNAMICIMPORTS_ATTR);
-        return (attr == null) ? m_emptyPackages: attr;
-    }
-
-    public static R4Package getDynamicImportPackage(Module m, String id)
+    public static IImport getImportPackage(IModule m, String name)
     {
-        R4Package[] pkgs = getDynamicImportsAttr(m);
+        IImport[] pkgs = (IImport[]) m.getAttribute(IMPORTS_ATTR, m_emptyImports);
         for (int i = 0; (pkgs != null) && (i < pkgs.length); i++)
         {
-            if (pkgs[i].getId().equals(id))
+            if (pkgs[i].getName().equals(name))
             {
                 return pkgs[i];
             }
@@ -1443,23 +1421,12 @@
         return null;
     }
 
-    public static R4Wire[] getWiringAttr(Module m)
+    public static IWire getWire(IModule m, String name)
     {
-        R4Wire[] attr =
-            (R4Wire[]) m.getAttribute(WIRING_ATTR);
-        if (attr == null)
-        {
-            attr = m_emptyWires;
-        }
-        return attr;
-    }
-
-    public static R4Wire getWire(Module m, String id)
-    {
-        R4Wire[] wires = getWiringAttr(m);
+        IWire[] wires = (IWire[]) m.getAttribute(WIRING_ATTR, m_emptyWires);
         for (int i = 0; (wires != null) && (i < wires.length); i++)
         {
-            if (wires[i].m_pkg.getId().equals(id))
+            if (wires[i].getExport().getName().equals(name))
             {
                 return wires[i];
             }
@@ -1467,21 +1434,7 @@
         return null;
     }
 
-    public static boolean isModuleInArray(Module[] modules, Module m)
-    {
-        // Verify that the module is not already in the array.
-        for (int i = 0; (modules != null) && (i < modules.length); i++)
-        {
-            if (modules[i] == m)
-            {
-                return true;
-            }
-        }
-        
-        return false;
-    }
-
-    public static Module[] addModuleToArray(Module[] modules, Module m)
+    public static IModule[] addModuleToArray(IModule[] modules, IModule m)
     {
         // Verify that the module is not already in the array.
         for (int i = 0; (modules != null) && (i < modules.length); i++)
@@ -1494,20 +1447,20 @@
 
         if (modules != null)
         {
-            Module[] newModules = new Module[modules.length + 1];
+            IModule[] newModules = new IModule[modules.length + 1];
             System.arraycopy(modules, 0, newModules, 0, modules.length);
             newModules[modules.length] = m;
             modules = newModules;
         }
         else
         {
-            modules = new Module[] { m };
+            modules = new IModule[] { m };
         }
 
         return modules;
     }
 
-    public static Module[] removeModuleFromArray(Module[] modules, Module m)
+    public static IModule[] removeModuleFromArray(IModule[] modules, IModule m)
     {
         if (modules == null)
         {
@@ -1534,7 +1487,7 @@
             // Otherwise, we need to do some array copying.
             else
             {
-                Module[] newModules= new Module[modules.length - 1];
+                IModule[] newModules= new IModule[modules.length - 1];
                 System.arraycopy(modules, 0, newModules, 0, idx);
                 if (idx < newModules.length)
                 {
@@ -1547,41 +1500,7 @@
         return modules;
     }
 
-// TODO: INVESTIGATE GENERIC ARRAY GROWING/SHRINKING.
-    private static R4Wire[] shrinkWireArray(R4Wire[] wires)
-    {
-        if (wires == null)
-        {
-            return m_emptyWires;
-        }
-
-        int count = 0;
-        for (int i = 0; i < wires.length; i++)
-        {
-            if (wires[i] == null)
-            {
-                count++;
-            }
-        }
-
-        if (count > 0)
-        {
-            R4Wire[] newWires = new R4Wire[wires.length - count];
-            count = 0;
-            for (int i = 0; i < wires.length; i++)
-            {
-                if (wires[i] != null)
-                {
-                    newWires[count++] = wires[i];
-                }
-            }
-            wires = newWires;
-        }
-
-        return wires;
-    }
-
-    private static Module[] shrinkModuleArray(Module[] modules)
+    private static IModule[] shrinkModuleArray(IModule[] modules)
     {
         if (modules == null)
         {
@@ -1599,7 +1518,7 @@
 
         if (count > 0)
         {
-            Module[] newModules = new Module[modules.length - count];
+            IModule[] newModules = new IModule[modules.length - count];
             count = 0;
             for (int i = 0; i < modules.length; i++)
             {
@@ -1616,17 +1535,17 @@
 
     private static class ResolverNode
     {
-        public Module m_module = null;
-        public R4Package m_pkg = null;
-        public Module[] m_candidates = null;
+        public IModule m_module = null;
+        public IImport m_import = null;
+        public IModule[] m_candidates = null;
         public int m_idx = 0;
         public boolean m_visited = false;
-        public ResolverNode(Module module, R4Package pkg, Module[] candidates)
+        public ResolverNode(IModule module, IImport imp, IModule[] candidates)
         {
             m_module = module;
-            m_pkg = pkg;
+            m_import = imp;
             m_candidates = candidates;
-            if (getResolvedAttr(m_module).booleanValue())
+            if (((Boolean) m_module.getAttribute(RESOLVED_ATTR, Boolean.FALSE)).booleanValue())
             {
                 m_visited = true;
             }

Added: incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/URLPolicyImpl.java
URL: http://svn.apache.org/viewcvs/incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/URLPolicyImpl.java?rev=372831&view=auto
==============================================================================
--- incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/URLPolicyImpl.java (added)
+++ incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/URLPolicyImpl.java Fri Jan 27 05:01:17 2006
@@ -0,0 +1,50 @@
+package org.apache.felix.framework.searchpolicy;
+
+import java.net.*;
+import java.security.AccessController;
+
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.moduleloader.IModule;
+import org.apache.felix.moduleloader.IURLPolicy;
+
+public class URLPolicyImpl implements IURLPolicy
+{
+    private URLStreamHandler m_streamHandler = null;
+
+    public URLPolicyImpl(URLStreamHandler streamHandler)
+    {
+        m_streamHandler = streamHandler;
+    }
+
+    public URL createURL(IModule module, String path)
+    {
+         // Add a slash if there is one already, otherwise
+         // the is no slash separating the host from the file
+         // in the resulting URL.
+         if (!path.startsWith("/"))
+         {
+             path = "/" + path;
+         }
+
+// TODO: FIX CREATE PRIVILEGED URL, SEE OSGiURLPolicy:93.
+         try
+         {
+//             if (System.getSecurityManager() != null)
+//             {
+//                 return (URL) AccessController.doPrivileged(
+//                     new CreateURLPrivileged(module.getId(), rsIdx, name));
+//             }
+//             else
+             {
+                 return new URL(FelixConstants.BUNDLE_URL_PROTOCOL,
+                         module.getId(), -1, path, m_streamHandler);
+             }
+         }
+         catch (MalformedURLException ex)
+         {
+// TODO: FIX LOGGING.
+             ex.printStackTrace();
+         }
+         return null;
+    }
+}
\ No newline at end of file

Propchange: incubator/felix/sandbox/rickhall/framework-branch/src/main/java/org/apache/felix/framework/searchpolicy/URLPolicyImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native