Renamed packages to the new package structure and update source code to match.


git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@233548 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/org/apache/felix/moduleloader/DefaultURLPolicy.java b/src/org/apache/felix/moduleloader/DefaultURLPolicy.java
new file mode 100644
index 0000000..0aa9d8d
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/DefaultURLPolicy.java
@@ -0,0 +1,97 @@
+/*
+ *   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.moduleloader;
+
+import java.net.URL;
+
+/**
+ * <p>
+ * This class implements a simple <tt>URLPolicy</tt> that the <tt>ModuleManager</tt>
+ * uses if the application does not specify one. This implementation always returns
+ * <tt>null</tt> for <tt>CodeSource</tt> <tt>URL</tt>s, which means that security
+ * is simply ignored. For resource <tt>URL</tt>s, it returns an <tt>URL</tt> in the
+ * form of:
+ * </p>
+ * <pre>
+ *     module://&lt;module-id&gt;/&lt;resource-path&gt;
+ * </pre>
+ * <p>
+ * In order to properly handle the "<tt>module:</tt>" protocol, this policy
+ * also defines a custom <tt>java.net.URLStreamHandler</tt> that it assigns
+ * to each <tt>URL</tt> as it is created. This custom handler is used to
+ * return a custom <tt>java.net.URLConnection</tt> that will correctly parse
+ * the above <tt>URL</tt> and retrieve the associated resource bytes using
+ * methods from <tt>ModuleManager</tt> and <tt>Module</tt>.
+ * </p>
+ * @see org.apache.felix.moduleloader.ModuleManager
+ * @see org.apache.felix.moduleloader.Module
+ * @see org.apache.felix.moduleloader.URLPolicy
+**/
+public class DefaultURLPolicy implements URLPolicy
+{
+    private ModuleURLStreamHandler m_handler = null;
+
+    /**
+     * <p>
+     * This method is a stub and always returns <tt>null</tt>.
+     * </p>
+     * @param mgr the <tt>ModuleManager</tt> of the module.
+     * @param module the module for which the <tt>URL</tt> is to be created.
+     * @return <tt>null</tt>.
+    **/
+    public URL createCodeSourceURL(ModuleManager mgr, Module module)
+    {
+        return null;
+    }
+
+    /**
+     * <p>
+     * This method returns a <tt>URL</tt> that is suitable
+     * for accessing the bytes of the specified resource.
+     * </p>
+     * @param mgr the <tt>ModuleManager</tt> of the module.
+     * @param module the module for which the resource is being loaded.
+     * @param rsIdx the index of the <tt>ResourceSource</tt> containing the resource.
+     * @param name the name of the resource being loaded.
+     * @return an <tt>URL</tt> for retrieving the resource.
+    **/
+    public URL createResourceURL(ModuleManager mgr, Module module, int rsIdx, String name)
+    {
+        if (m_handler == null)
+        {
+            m_handler = new ModuleURLStreamHandler(mgr);
+        }
+
+        // Add a slash if there is one already, otherwise
+        // the is no slash separating the host from the file
+        // in the resulting URL.
+        if (!name.startsWith("/"))
+        {
+            name = "/" + name;
+        }
+
+        try
+        {
+            return new URL("module", module.getId(), -1, "/" + rsIdx + name, m_handler);
+        }
+        catch (Exception ex)
+        {
+            System.err.println("DefaultResourceURLPolicy: " + ex);
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/JarResourceSource.java b/src/org/apache/felix/moduleloader/JarResourceSource.java
new file mode 100644
index 0000000..0f3fdbf
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/JarResourceSource.java
@@ -0,0 +1,211 @@
+/*
+ *   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.moduleloader;
+
+import java.io.*;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+/**
+ * <p>
+ * This class implements a <tt>ResourceSource</tt> for retrieving resources
+ * from a JAR file. The approach used by this implementation is to defer
+ * opening the JAR file until a request for a resource is made.
+ * </p>
+ * @see org.apache.felix.moduleloader.ResourceSource
+**/
+public class JarResourceSource implements ResourceSource
+{
+    private static final int BUFSIZE = 4096;
+
+    private File m_file = null;
+    private JarFile m_jarFile = null;
+    private boolean m_opened = false;
+
+    /**
+     * <p>
+     * Constructs an instance using the specified file name as the source
+     * of the JAR file.
+     * </p>
+     * @param fileName the name of the JAR file to be used as the source.
+    **/
+    public JarResourceSource(String fileName)
+    {
+        m_file = new File(fileName);
+    }
+
+    /**
+     * <p>
+     * Constructs an instance using the specified file as the source
+     * of the JAR file.
+     * </p>
+     * @param file the JAR file to be used as the source.
+    **/
+    public JarResourceSource(File file)
+    {
+        m_file = file;
+    }
+
+    /**
+     * <p>
+     * Closes the JAR file if it has not already been closed.
+     * <p>
+    **/
+    protected void finalize()
+    {
+        if (m_jarFile != null)
+        {
+            try {
+                m_jarFile.close();
+            } catch (IOException ex) {
+                // Not much we can do, so ignore it.
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * This method initializes the resource source. Since opening
+     * the JAR file is deferred until a request for a resource is
+     * actually made, this method really only sets a flag indicating
+     * that the resource source has been initialized.
+     * <p>
+    **/
+    public void open()
+    {
+        m_opened = true;
+    }
+
+    /**
+     * <p>
+     * This method deinitializes the resource source by closing
+     * the associated JAR file if it is open.
+     * <p>
+    **/
+    public synchronized void close()
+    {
+        try {
+            if (m_jarFile != null)
+            {
+                m_jarFile.close();
+            }
+        } catch (Exception ex) {
+            System.err.println("JarResourceSource: " + ex);
+        }
+
+        m_jarFile = null;
+        m_opened = false;
+    }
+
+    // JavaDoc comments are copied from ResourceSource.
+    public synchronized boolean hasResource(String name) throws IllegalStateException
+    {
+        if (!m_opened)
+        {
+            throw new IllegalStateException("JarResourceSource is not open");
+        }
+
+        // Open JAR file if not already opened.
+        if (m_jarFile == null)
+        {
+            try {
+                openJarFile();
+            } catch (IOException ex) {
+                System.err.println("JarResourceSource: " + ex);
+                return false;
+            }
+        }
+
+        try {
+            ZipEntry ze = m_jarFile.getEntry(name);
+            return ze != null;
+        } catch (Exception ex) {
+            return false;
+        } finally {
+        }
+    }
+
+    // JavaDoc comments are copied from ResourceSource.
+    public synchronized byte[] getBytes(String name) throws IllegalStateException
+    {
+        if (!m_opened)
+        {
+            throw new IllegalStateException("JarResourceSource is not open");
+        }
+
+        // Open JAR file if not already opened.
+        if (m_jarFile == null)
+        {
+            try {
+                openJarFile();
+            } catch (IOException ex) {
+                System.err.println("JarResourceSource: " + ex);
+                return null;
+            }
+        }
+
+        // Get the embedded resource.
+        InputStream is = null;
+        ByteArrayOutputStream baos = null;
+
+        try {
+            ZipEntry ze = m_jarFile.getEntry(name);
+            if (ze == null)
+            {
+                return null;
+            }
+            is = m_jarFile.getInputStream(ze);
+            if (is == null)
+            {
+                return null;
+            }
+            baos = new ByteArrayOutputStream(BUFSIZE);
+            byte[] buf = new byte[BUFSIZE];
+            int n = 0;
+            while ((n = is.read(buf, 0, buf.length)) >= 0)
+            {
+                baos.write(buf, 0, n);
+            }
+            return baos.toByteArray();
+
+        } catch (Exception ex) {
+            return null;
+        } finally {
+            try {
+                if (baos != null) baos.close();
+            } catch (Exception ex) {
+            }
+            try {
+                if (is != null) is.close();
+            } catch (Exception ex) {
+            }
+        }
+    }
+
+    private void openJarFile() throws IOException
+    {
+        if (m_jarFile == null)
+        {
+            m_jarFile = new JarFile(m_file);
+        }
+    }
+
+    public String toString()
+    {
+        return "JAR " + m_file.getPath();
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/LibrarySource.java b/src/org/apache/felix/moduleloader/LibrarySource.java
new file mode 100644
index 0000000..a0538ae
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/LibrarySource.java
@@ -0,0 +1,67 @@
+/*
+ *   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.moduleloader;
+
+/**
+ * <p>
+ * This interface represents a source for obtaining native libraries for a
+ * given module via the module's class loader. The main goal of a library
+ * source is to map a library name to a path in the file system.
+ * </p>
+ * <p>
+ * All library sources are initialized before first usage via a call
+ * to the <a href="#open()"><tt>LibrarySource.open()</tt></a> method and
+ * are also deinitialized via a call to
+ * <a href="#open()"><tt>LibrarySource.close()</tt></a>. Library sources
+ * should be implemented such that they can be opened, closed, and then
+ * re-opened.
+ * </p>
+ * @see org.apache.felix.moduleloader.Module
+ * @see org.apache.felix.moduleloader.ModuleClassLoader
+**/
+public interface LibrarySource
+{
+    /**
+     * <p>
+     * This method initializes the library source. It is called when
+     * the associated module is added to the <tt>ModuleManager</tt>. It
+     * is acceptable for implementations to ignore duplicate calls to this
+     * method if the library source is already opened.
+     * </p>
+    **/
+    public void open();
+
+    /**
+     * <p>
+     * This method de-initializes the library source. It is called when
+     * the associated module is removed from the <tt>ModuleManager</tt> or
+     * when the module is reset by the <tt>ModuleManager</tt>.
+     * </p>
+    **/
+    public void close();
+
+    /**
+     * <p>
+     * Returns a file system path to the specified library.
+     * </p>
+     * @param name the name of the library that is being requested.
+     * @return a file system path to the specified library.
+     * @throws java.lang.IllegalStateException if the resource source has not
+     *         been opened.
+    **/
+    public String getPath(String name) throws IllegalStateException;
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/Module.java b/src/org/apache/felix/moduleloader/Module.java
new file mode 100644
index 0000000..66b64a2
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/Module.java
@@ -0,0 +1,357 @@
+/*
+ *   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.moduleloader;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+
+/**
+ * <p>
+ * The <tt>Module</tt> class is a grouping mechanism for application classes
+ * and resources. Conceptually, most applications are grouped into
+ * entities such as JAR files (containing classes and resources) and native
+ * libraries. In some cases, these entities are core application classes and
+ * resources, while in other cases, these entities are ancillary, such as
+ * dynamically loaded plug-ins. Applications place some level of semantics
+ * onto these types of entities or <i>modules</i>, but for the <tt>ModuleLoader</tt>,
+ * no particular semantics are attached to modules (other than they are a grouping
+ * mechanism for classes and resources). This means that the application
+ * is free to map itself into modules any way that is appropriate.
+ * </p>
+ * <p>
+ * A module has the following features:
+ * </p>
+ * <ul>
+ *   <li>A unique identifier within the scope of its <tt>ModuleManager</tt>.
+ *   </li>
+ *   <li>A set of key-value attribute pairs.
+ *   </li>
+ *   <li>A set of resource sources from which it is possible to
+ *       retrieve classes and resources.
+ *   </li>
+ *   <li>A set of native library sources from which it is possible
+ *       to retrieve native libraries.
+ *   </li>
+ * </ul>
+ * <p>
+ * A module's identifier must be unique within the scope of its
+ * <tt>ModuleManager</tt>, but there is no meaning associated with it. The
+ * set of attribute-value pairs attached to the module have no meaning to
+ * the <tt>ModuleManager</tt>, nor does it consult them at all. The point
+ * of these attributes is to attach meta-data for use by
+ * <a href="SearchPolicy.html"><tt>SearchPolicy</tt></a> implementations.
+ * Attributes are represented as an array of <tt>Object</tt>
+ * arrays, i.e., <tt>Object[][]</tt>. Each element in the attribute array is
+ * a two-element <tt>Object</tt> array, where <tt>Module.KEY_IDX</tt> is the attribute's
+ * key and <tt>Module.VALUE_IDX</tt> is the attribute's value.
+ * </p>
+ * <p>
+ * The actual contents of a module is contained in two sets of sources
+ * for its resources and native libraries,
+ * <a href="ResourceSource.html"><tt>ResourceSource</tt></a>s
+ * and <a href="LibrarySource.html"><tt>LibrarySource</tt></a>s, respectively.
+ * Each module also has a <a href="ModuleClassLoader.html"><tt>ModuleClassLoader</tt></a>
+ * associated with it. The <tt>ModuleClassLoader</tt> consults these two types
+ * of sources to find classes, resources, and native libraries.
+ * </p>
+ * @see org.apache.felix.moduleloader.ModuleManager
+ * @see org.apache.felix.moduleloader.ModuleClassLoader
+ * @see org.apache.felix.moduleloader.ResourceSource
+ * @see org.apache.felix.moduleloader.LibrarySource
+**/
+public class Module
+{
+    /**
+     * This is the index used to retrieve the key of an attribute;
+     * an attribute is represented as an <tt>Object[]</tt> instance.
+    **/
+    public static final int KEY_IDX = 0;
+    /**
+     * This is the index used to retrieve the value of an attribute;
+     * an attribute is represented as an <tt>Object[]</tt> instance.
+    **/
+    public static final int VALUE_IDX = 1;
+
+    private ModuleManager m_mgr = null;
+    private String m_id = null;
+    private boolean m_useParentSource = false;
+    private Map m_attributeMap = new HashMap();
+    private ResourceSource[] m_resSources = null;
+    private LibrarySource[] m_libSources = null;
+    private ModuleClassLoader m_loader = null;
+
+    /**
+     * <p>
+     * Constructs a <tt>Module</tt> instance that will be associated with
+     * the specified <tt>ModuleManager</tt> and will have the specified
+     * identifier, attributes, resource sources, and library sources. In general,
+     * modules should not be created directly, but should be created by making
+     * a call to <tt>ModuleManager.addModule()</tt>.
+     * </p>
+     * @param mgr the <tt>ModuleManager</tt> that will be associated to
+     *       the instance.
+     * @param id the identifier of the instance.
+     * @param attributes the set of attributes associated with the instance.
+     * @param resSources the set of <tt>ResourceSource</tt>s associated with
+     *        the instance.
+     * @param libSources the set of <tt>LibrarySource</tt>s associated with
+     *        the instance.
+     * @param useParentSource a flag indicating whether or not the parent
+     *        class loader should be used as a resource source; this is an
+     *        ugly hack to allow a module to masquerade as the system
+     *        class loader.
+     * @see org.apache.felix.moduleloader.ModuleManager
+     * @see org.apache.felix.moduleloader.ResourceSource
+     * @see org.apache.felix.moduleloader.LibrarySource
+    **/
+    public Module(
+        ModuleManager mgr, String id, Object[][] attributes,
+        ResourceSource[] resSources, LibrarySource[] libSources,
+        boolean useParentSource)
+    {
+        m_mgr = mgr;
+        m_id = id;
+        m_useParentSource = useParentSource;
+        initialize(attributes, resSources, libSources);
+    }
+
+    /**
+     * <p>
+     * Returns the identifier of the module.
+     * </p>
+     * @return the identifier of the module.
+    **/
+    public String getId()
+    {
+        return m_id;
+    }
+
+    /**
+     * <p>
+     * Returns the attribute set associated with this module. Attributes
+     * are represented as an array of <tt>Object</tt> arrays, i.e.,
+     * <tt>Object[][]</tt>. Each element in the attribute array is
+     * two-element <tt>Object</tt> array, where <tt>Module.KEY_IDX</tt>
+     * is the index to the attribute key and <tt>Module.VALUE_IDX</tt>
+     * is the index to the attribute value. The returned array is a
+     * copy and may be freely modified.
+     * </p>
+     * @return the attribute set associated with this module.
+    **/
+    public synchronized Object[][] getAttributes()
+    {
+        Set s = m_attributeMap.entrySet();
+        Object[][] attributes = new Object[s.size()][];
+        Iterator iter = s.iterator();
+        for (int i = 0; iter.hasNext(); i++)
+        {
+            Map.Entry entry = (Map.Entry) iter.next();
+            attributes[i] = new Object[] { entry.getKey(), entry.getValue() };
+        }
+        return attributes;
+    }
+
+    /**
+     * <p>
+     * Returns the attribute value associated with the specified key.
+     * </p>
+     * @param key the key of the attribute whose value is to be retrieved.
+     * @return the attribute's value or <tt>null</tt>.
+    **/
+    public synchronized Object getAttribute(String key)
+    {
+        return m_attributeMap.get(key);
+    }
+
+    /**
+     * <p>
+     * Sets the attribute value associated with the specified key. The
+     * attribute will be added if it does not currently exist.
+     * </p>
+     * @param key the key of the attribute whose value is to be set.
+     * @param value the new value to be associated with the attribute key.
+    **/
+    public synchronized void setAttribute(String key, Object value)
+    {
+        m_attributeMap.put(key, value);
+    }
+
+    /**
+     * <p>
+     * Returns the array of <tt>ResourceSource</tt>s associated with
+     * the module. The returned array is not a copy and therefore should
+     * not be modified.
+     * </p>
+     * @return the array of <tt>ResourceSource</tt>s associated with
+     *         the module.
+     * @see org.apache.felix.moduleloader.ResourceSource
+    **/
+    public ResourceSource[] getResourceSources()
+    {
+        return m_resSources;
+    }
+
+    /**
+     * <p>
+     * Returns the array of <tt>LibrarySource</tt>s associated with
+     * the module. The returned array is not a copy and therefore should
+     * not be modified.
+     * </p>
+     * @return the array of <tt>LibrarySource</tt>s associated with
+     *         the module.
+     * @see org.apache.felix.moduleloader.LibrarySource
+    **/
+    public LibrarySource[] getLibrarySources()
+    {
+        return m_libSources;
+    }
+
+    /**
+     * <p>
+     * Returns the <tt>ModuleClassLoader</tt> associated with this module.
+     * If a security manager is installed, then this method uses a privileged
+     * action to avoid a security exception being thrown to the caller.
+     * </p>
+     * @return the <tt>ModuleClassLoader</tt> associated with this module.
+     * @see org.apache.felix.moduleloader.ModuleClassLoader
+    **/
+    public synchronized ModuleClassLoader getClassLoader()
+    {
+        if (m_loader == null)
+        {
+            if (System.getSecurityManager() != null)
+            {
+                m_loader = (ModuleClassLoader) AccessController.doPrivileged(
+                    new GetClassLoaderPrivileged(m_mgr, this, m_useParentSource));
+            }
+            else
+            {
+                m_loader = new ModuleClassLoader(m_mgr, this, m_useParentSource);
+            }
+        }
+
+        return m_loader;
+    }
+
+    /**
+     * <p>
+     * Returns the module's identifier.
+     * </p>
+     * @return the module's identifier.
+    **/
+    public String toString()
+    {
+        return m_id;
+    }
+
+    /**
+     * <p>
+     * Resets the module by throwing away its associated class loader and
+     * re-initializing its attributes, resource sources, and library sources
+     * with the specified values.
+     * </p>
+     * @param attributes the new attributes to be associated with the module.
+     * @param resSources the new resource sources to be associated with the module.
+     * @param libSources the new library sources to be associated with the module.
+     * @see org.apache.felix.moduleloader.ResourceSource
+     * @see org.apache.felix.moduleloader.LibrarySource
+    **/
+    protected synchronized void reset(
+        Object[][] attributes, ResourceSource[] resSources,
+        LibrarySource[] libSources)
+    {
+        // Throw away class loader.
+        m_loader = null;
+        // Clear attribute map.
+        m_attributeMap.clear();
+        // Close all sources.
+        dispose();
+        // Re-initialize.
+        initialize(attributes, resSources, libSources);
+    }
+
+    /**
+     * <p>
+     * Disposes the module by closing all resource and library sources.
+     * </p>
+    **/
+    protected synchronized void dispose()
+    {
+        // Close sources.
+        for (int i = 0; (m_resSources != null) && (i < m_resSources.length); i++)
+        {
+            m_resSources[i].close();
+        }
+        for (int i = 0; (m_libSources != null) && (i < m_libSources.length); i++)
+        {
+            m_libSources[i].close();
+        }
+    }
+
+    /**
+     * <p>
+     * Initializes the module by copying the specified attribute array into
+     * a map and opening all resource and library sources.
+     * </p>
+     * @param attributes the attributes to be put into a map.
+     * @param resSources the resource sources to be opened.
+     * @param libSources the library sources to be opened.
+     * @see org.apache.felix.moduleloader.ResourceSource
+     * @see org.apache.felix.moduleloader.LibrarySource
+    **/
+    private void initialize(
+        Object[][] attributes, ResourceSource[] resSources, LibrarySource[] libSources)
+    {
+        for (int i = 0; (attributes != null) && (i < attributes.length); i++)
+        {
+            m_attributeMap.put(attributes[i][KEY_IDX], attributes[i][VALUE_IDX]);
+        }
+
+        m_resSources = resSources;
+        m_libSources = libSources;
+
+        // Open sources.
+        for (int i = 0; (m_resSources != null) && (i < m_resSources.length); i++)
+        {
+            m_resSources[i].open();
+        }
+        for (int i = 0; (m_libSources != null) && (i < m_libSources.length); i++)
+        {
+            m_libSources[i].open();
+        }
+    }
+
+    private static class GetClassLoaderPrivileged implements PrivilegedAction
+    {
+        private ModuleManager m_mgr = null;
+        private Module m_module = null;
+        private boolean m_useParentSource = false;
+
+        public GetClassLoaderPrivileged(ModuleManager mgr, Module module, boolean useParentSource)
+        {
+            m_mgr = mgr;
+            m_module = module;
+            m_useParentSource = useParentSource;
+        }
+
+        public Object run()
+        {
+            return new ModuleClassLoader(m_mgr, m_module, m_useParentSource);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/ModuleClassLoader.java b/src/org/apache/felix/moduleloader/ModuleClassLoader.java
new file mode 100644
index 0000000..262128d
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/ModuleClassLoader.java
@@ -0,0 +1,473 @@
+/*
+ *   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.moduleloader;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.SecureClassLoader;
+import java.security.cert.Certificate;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * <p>
+ * Each module that is managed by a <tt>ModuleManager</tt> has a
+ * <tt>ModuleClassLoader</tt> associated with it. The <tt>ModuleClassLoader</tt>
+ * is responsible for loading all classes, resources, and native libraries
+ * for its module. The <tt>ModuleClassLoader</tt> of a module
+ * is accessed using the <tt>Module.getClassLoader()</tt> method. The
+ * <tt>ModuleClassLoader</tt> uses its module's
+ * <a href="ResourceSource.html"><tt>ResourceSource</tt></a>s
+ * and <a href="LibrarySource.html"><tt>LibrarySource</tt></a>s
+ * to perform its function.
+ * </p>
+ * <p>
+ * When loading a class or resource, the <tt>ModuleClassLoader</tt> does
+ * not immediately search its module's <tt>ResourceSource</tt>s, instead
+ * it first delegates the request to the
+ * <a href="SearchPolicy.html"><tt>SearchPolicy</tt></a> of the
+ * <tt>ModuleManager</tt>; this allows applications to inject specific
+ * class/resource loading policies. When the <tt>ModuleClassLoader</tt> delegates
+ * to the search policy, the search policy uses application-specific behavior
+ * to typically service the request from the <tt>ResourceSource</tt>s of
+ * other modules. If the search policy returns a result, then this result is
+ * returned immediately by the <tt>ModuleClassLoader</tt>; otherwise, it searches
+ * the <tt>ResourceSource</tt>s its module in an attempt to satisfy the
+ * original request.
+ * </p>
+ * <p>
+ * <b><i>Important:</i></b> The search policy <i>searches</i> modules in
+ * some application-specific manner in order to find a class or resource.
+ * This <i>search</i> is instigated, either directly or indirectly, by calls
+ * to <tt>ModuleClassLoader.loadClass()</tt> and <tt>ModuleClassLoader.getResource()</tt>,
+ * respectively. In order for the search policy to load a class or resource,
+ * it must <b>not</b> use <tt>ModuleClassLoader.loadClass()</tt> or
+ * <tt>ModuleClassLoader.getResource()</tt> again, because this would result
+ * in an infinite loop. Instead, the <tt>ModuleClassLoader</tt> offers the
+ * the methods <tt>ModuleClassLoader.loadClassFromModule()</tt> and
+ * <tt>ModuleClassLoader.getResourceFromModule()</tt> to search a given module
+ * and to avoid an infinite loop. As an example, consider the following
+ * snippet of code that implements an "exhaustive" search policy:
+ * </p>
+ * <pre>
+ *     ...
+ *     public Class findClass(Module module, String name)
+ *     {
+ *         Module[] modules = m_mgr.getModules();
+ *         for (int i = 0; i < modules.length; i++)
+ *         {
+ *             try {
+ *                 Class clazz = modules[i].getClassLoader().loadClassFromModule(name);
+ *                 if (clazz != null)
+ *                 {
+ *                     return clazz;
+ *                 }
+ *             } catch (Throwable th) {
+ *             }
+ *         }
+ *
+ *         return null;
+ *     }
+ *     ...
+ * </pre>
+ * <p>
+ * In the above code, the search policy "exhaustively" searches every module in the
+ * <tt>ModuleManager</tt> to find the requested resource. Note that this policy
+ * will also search the module that originated the request, which is not totally
+ * necessary since returning <tt>null</tt> will cause the <tt>ModuleClassLoader</tt>
+ * to search the originating module's <tt>ResourceSource</tt>s.
+ * </p>
+**/
+public class ModuleClassLoader extends SecureClassLoader
+{
+    private ModuleManager m_mgr = null;
+    private Module m_module = null;
+    private boolean m_useParentSource = false;
+
+    /**
+     * <p>
+     * Constructs an instance using the specified <tt>ModuleManager</tt>, for
+     * the specified <tt>Module</tt>. This constructor is protected so that
+     * it cannot be created publicly.
+     * </p>
+     * @param mgr the <tt>ModuleManager</tt> of the <tt>Module</tt>.
+     * @param module the <tt>Module</tt> instance associated with the class loader.
+    **/
+    protected ModuleClassLoader(ModuleManager mgr, Module module, boolean useParentSource)
+    {
+        super(ModuleClassLoader.class.getClassLoader());
+        m_mgr = mgr;
+        m_module = module;
+        m_useParentSource = useParentSource;
+    }
+
+    /**
+     * <p>
+     * This method is nearly an exact copy of the ClassLoader.loadClass()
+     * method. The main difference is that it delegates to its associated
+     * <tt>ModuleManager</tt>'s search policy before calling the
+     * <tt>ClassLoader.findClass()</tt> method. Additionally, the synchronized
+     * modifier was removed from the superclass method; this change was necessary
+     * because ClassLoader class assumes a tree of class loaders, but the class
+     * loading structure in the <tt>ModuleManager</tt> might actually be a graph
+     * of class loaders; thus, it was necessary to loosen the concurrency locking
+     * to allow for cycles.
+     * </p>
+     * @param name the class to be loaded.
+     * @param resolve flag indicating whether the class should be resolved or not.
+     * @return the loaded class.
+     * @throws java.lang.ClassNotFoundException if the class could not be loaded.
+    **/
+    protected Class loadClass(String name, boolean resolve)
+        throws ClassNotFoundException
+    {
+        // Make sure the class was not already loaded.
+        Class c = findLoadedClass(name);
+        // Ask the search policy for the clas before consulting the module.
+        c = m_mgr.getSearchPolicy().findClassBeforeModule(getParent(), m_module, name);
+        // If the search policy didn't find it, then consult the module.
+        if (c == null)
+        {
+            c = findClass(name);
+        }
+        // If the module didn't find it, then consult the search policy
+        // one more time.
+        if (c == null)
+        {
+            c = m_mgr.getSearchPolicy().findClassAfterModule(getParent(), m_module, name);
+        }
+        // If still not found, then throw an exception.
+        if (c == null)
+        {
+            throw new ClassNotFoundException(name);
+        }
+        // Otherwise resolve the class.
+        if (resolve)
+        {
+            resolveClass(c);
+        }
+        return c;
+    }
+
+    /**
+     * <p>
+     * This method overriden from from <tt>ClassLoader</tt>.
+     * It is implemented such that it loads classes from the set of
+     * <tt>ResourceSource</tt>s from its associated module.
+     * </p>
+     * @param name the name of the resource to load.
+     * @return the loaded <tt>Class</tt> object.
+     * @throws java.lang.ClassNotFoundException if the class could not be loaded.
+    **/
+    protected Class findClass(String name) throws ClassNotFoundException
+    {
+        Class clazz = findLoadedClass(name);
+
+        // If the parent is used as a source, try to
+        // load the class from it.
+        // TODO: This is really a hack and should be generalized somehow.
+        if (m_useParentSource)
+        {
+            clazz = (getParent() == null) ? null : getParent().loadClass(name);
+        }
+
+        // Otherwise search for class in resource sources.
+        if (clazz == null)
+        {
+            String actual = name.replace('.', '/') + ".class";
+            ResourceSource[] sources = m_module.getResourceSources();
+            for (int i = 0;
+                (clazz == null) && (sources != null) && (i < sources.length);
+                i++)
+            {
+                byte[] bytes = sources[i].getBytes(actual);
+                if (bytes != null)
+                {
+                    // We need to try to define a Package object for the class
+                    // before we call defineClass(). Get the package name and
+                    // see if we have already created the package.
+                    String pkgName = Util.getClassPackage(name);
+                    if (pkgName.length() > 0)
+                    {
+                        if (getPackage(pkgName) == null)
+                        {
+                            Object[] params =
+                                m_mgr.getSearchPolicy().definePackage(m_module, pkgName);
+                            if (params != null)
+                            {
+                                definePackage(
+                                    pkgName,
+                                    (String) params[0],
+                                    (String) params[1],
+                                    (String) params[2],
+                                    (String) params[3],
+                                    (String) params[4],
+                                    (String) params[5],
+                                    null);
+                            }
+                        }
+                    }
+
+                    // Get the code source URL for this class. For concurrency
+                    // purposes, we are performing this call outside of the
+                    // synchronized block below since we call out to application
+                    // code, which might in turn need to call back into the
+                    // module loader code. Because of this, it is better to
+                    // not be holding any locks before making the call.
+                    URL url = m_mgr.getURLPolicy().createCodeSourceURL(
+                        m_mgr, m_module);
+
+                    // If we have a valid code source URL, then use it to
+                    // define the class for security purposes, otherwise
+                    // define the class without a code source.
+                    if (url != null)
+                    {
+                        CodeSource cs = new CodeSource(url, (Certificate[]) null);
+                        clazz = defineClass(name, bytes, 0, bytes.length, cs);
+                    }
+                    else
+                    {
+                        clazz = defineClass(name, bytes, 0, bytes.length);
+                    }
+                }
+            }
+        }
+
+        if (clazz != null)
+        {
+            return clazz;
+        }
+
+        return null;
+    }
+
+    /**
+     * <p>
+     * This method is used by <tt>SearchPolicy</tt> instances when they want
+     * to load a class from a module. The search policy is initially invoked when
+     * <tt>ModuleClassLoader.loadClass()</tt> delegates a class loading
+     * request to it. In general, the ultimate goal of the search policy is to
+     * return a class from another module if possible. Unfortunately, if a search
+     * policy tries to directly load a class from another module's class loader, an
+     * infinite loop will result because the module's class loader will delegate the
+     * request back to the search policy. To avoid this situation, search policies
+     * must use this method when trying to load a class from a module.
+     * </p>
+     * @param name the name of the class to load.
+     * @return the loaded class or <tt>null</tt>.
+    **/
+    public Class loadClassFromModule(String name)
+    {
+        try
+        {
+            return findClass(name);
+        } catch (Throwable th) {
+            // Not much we can do.
+// TODO: Do something with this error message.
+//            System.err.println("ModuleClassLoader: " + th.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * <p>
+     * This method is nearly an exact copy of the ClassLoader.getResource()
+     * method. The main difference is that it delegates to its associated
+     * <tt>ModuleManager</tt>'s search policy before calling the
+     * <tt>ClassLoader.findResource()</tt> method.
+     * </p>
+     * @param name the class to be loaded.
+     * @return a URL to the resource or <tt>null</tt> if the resource was not found.
+    **/
+    public URL getResource(String name)
+    {
+        URL url = null;
+
+        // Ask the search policy for the resource.
+        if (m_mgr.getSearchPolicy() != null)
+        {
+            try
+            {
+                url = m_mgr.getSearchPolicy().findResource(getParent(), m_module, name);
+            }
+            catch (ResourceNotFoundException ex)
+            {
+                // We return null here because if SearchPolicy.findResource()
+                // throws an exception we interpret that to mean that the
+                // search should be stopped.
+                return null;
+            }
+        }
+
+        // If not found, then search locally.
+        if (url == null)
+        {
+            url = findResource(name);
+        }
+
+        return url;
+    }
+
+    /**
+     * <p>
+     * This method overriden from from <tt>ClassLoader</tt>.
+     * It is implemented such that it loads resources from the set of
+     * <tt>ResourceSource</tt>s from its associated module.
+     * </p>
+     * @param name the name of the resource to load.
+     * @return the <tt>URL</tt> associated with the resource or <tt>null</tt>.
+    **/
+    protected URL findResource(String name)
+    {
+        URL url = null;
+
+        // If the parent is used as a source, try to
+        // load the class from it.
+        if (m_useParentSource)
+        {
+            url = (getParent() == null) ? null : getParent().getResource(name);
+        }
+
+        // Try to load the resource from the module's resource
+        // sources.
+        if (url == null)
+        {
+            // Remove leading slash, if present.
+            if (name.startsWith("/"))
+            {
+                name = name.substring(1);
+            }
+
+            ResourceSource[] sources = m_module.getResourceSources();
+            for (int i = 0;
+                (url == null) && (sources != null) && (i < sources.length);
+                i++)
+            {
+                if (sources[i].hasResource(name))
+                {
+                    url = m_mgr.getURLPolicy().createResourceURL(m_mgr, m_module, i, name);
+                }
+            }
+        }
+
+        return url;
+    }
+
+    /**
+     * <p>
+     * This method is used by <tt>SearchPolicy</tt> instances when they want
+     * to load a resource from a module. The search policy is initially invoked when
+     * <tt>ModuleClassLoader.loadClass()</tt> delegates a resource loading
+     * request to it. In general, the ultimate goal of the search policy is to
+     * return a resource from another module if possible. Unfortunately, if a search
+     * policy tries to directly load a resource from another module's class loader, an
+     * infinite loop will result because the module's class loader will delegate the
+     * request back to the search policy. To avoid this situation, search policies
+     * must use this method when trying to load a resource from a module.
+     * </p>
+     * @param name the name of the resource to load.
+     * @return a URL to the resource or <tt>null</tt>.
+    **/
+    public URL getResourceFromModule(String name)
+    {
+        try
+        {
+            return findResource(name);
+        }
+        catch (Throwable th)
+        {
+            // Ignore and just return null.
+        }
+        return null;
+    }
+
+    protected Enumeration findResources(String name)
+    {
+        Vector v = new Vector();
+        // If the parent is used as a source, try to
+        // load the class from it.
+        if (m_useParentSource)
+        {
+            try
+            {
+                Enumeration e = (getParent() == null)
+                    ? null : getParent().getResources(name);
+                while ((e != null) && e.hasMoreElements())
+                {
+                    v.addElement(e.nextElement());
+                }
+            }
+            catch (IOException ex)
+            {
+                // What can we do?
+            }
+        }
+
+        // Remove leading slash, if present.
+        if (name.startsWith("/"))
+        {
+            name = name.substring(1);
+        }
+
+        // Try to load the resource from the module's resource
+        // sources.
+
+        ResourceSource[] sources = m_module.getResourceSources();
+        for (int i = 0; (sources != null) && (i < sources.length); i++)
+        {
+            if (sources[i].hasResource(name))
+            {
+                v.addElement(m_mgr.getURLPolicy().createResourceURL(m_mgr, m_module, i, name));
+            }
+        }
+
+        return v.elements();
+    }
+
+    /**
+     * <p>
+     * This method overriden from from <tt>ClassLoader</tt>. It maps a library
+     * name to a library path by consulting the <tt>LibrarySource</tt>s of the
+     * class loader's module.
+     * </p>
+     * @param name the name of the library to find.
+     * @return the file system path of library or <tt>null</tt>
+    **/
+    protected String findLibrary(String name)
+    {
+        // Remove leading slash, if present.
+        if (name.startsWith("/"))
+        {
+            name = name.substring(1);
+        }
+
+        LibrarySource[] sources = m_module.getLibrarySources();
+        for (int i = 0;
+            (sources != null) && (i < sources.length);
+            i++)
+        {
+            String path = sources[i].getPath(name);
+            if (path != null)
+            {
+                return path;
+            }
+        }
+
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/ModuleEvent.java b/src/org/apache/felix/moduleloader/ModuleEvent.java
new file mode 100644
index 0000000..ec15d49
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/ModuleEvent.java
@@ -0,0 +1,61 @@
+/*
+ *   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.moduleloader;
+
+import java.util.EventObject;
+
+/**
+ * <p>
+ * This is an event class that is used by the <tt>ModuleManager</tt> to
+ * indicate when modules are added, removed, or reset. To receive these
+ * events, a <tt>ModuleListener</tt> must be added to the <tt>ModuleManager</tt>
+ * instance.
+ * </p>
+ * @see org.apache.felix.moduleloader.ModuleManager
+ * @see org.apache.felix.moduleloader.Module
+ * @see org.apache.felix.moduleloader.ModuleListener
+**/
+public class ModuleEvent extends EventObject
+{
+    private Module m_module = null;
+
+    /**
+     * <p>
+     * Constructs a module event with the specified <tt>ModuleManager</tt>
+     * as the event source and the specified module as the subject of
+     * the event.
+     * </p>
+     * @param mgr the source of the event.
+     * @param module the subject of the event.
+    **/
+    public ModuleEvent(ModuleManager mgr, Module module)
+    {
+        super(mgr);
+        m_module = module;
+    }
+
+    /**
+     * <p>
+     * Returns the module that is the subject of the event.
+     * </p>
+     * @return the module that is the subject of the event.
+    **/
+    public Module getModule()
+    {
+        return m_module;
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/ModuleListener.java b/src/org/apache/felix/moduleloader/ModuleListener.java
new file mode 100644
index 0000000..4a583d9
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/ModuleListener.java
@@ -0,0 +1,58 @@
+/*
+ *   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.moduleloader;
+
+import java.util.EventListener;
+
+/**
+ * <p>
+ * This interface is an event listener for <tt>ModuleEvent</tt> events.
+ * To receive events, an implementation of this listener must be added
+ * to the <tt>ModuleManager</tt> instance.
+ * </p>
+ * @see org.apache.felix.moduleloader.ModuleManager
+ * @see org.apache.felix.moduleloader.ModuleEvent
+**/
+public interface ModuleListener extends EventListener
+{
+    /**
+     * <p>
+     * This method is called after a module is added to the
+     * <tt>ModuleManager</tt>.
+     * </p>
+     * @param event the event object containing the event details.
+    **/
+    public void moduleAdded(ModuleEvent event);
+
+    /**
+     * <p>
+     * This method is called after a module has been reset by the
+     * <tt>ModuleManager</tt>.
+     * </p>
+     * @param event the event object containing the event details.
+    **/
+    public void moduleReset(ModuleEvent event);
+
+    /**
+     * <p>
+     * This method is called after a module is remove from the
+     * <tt>ModuleManager</tt>.
+     * </p>
+     * @param event the event object containing the event details.
+    **/
+    public void moduleRemoved(ModuleEvent event);
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/ModuleManager.java b/src/org/apache/felix/moduleloader/ModuleManager.java
new file mode 100644
index 0000000..7640479
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/ModuleManager.java
@@ -0,0 +1,524 @@
+/*
+ *   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.moduleloader;
+
+import java.util.*;
+
+/**
+ * <p>
+ * The <tt>ModuleManager</tt> class is the core facility for defining a
+ * re-usable, policy-driven class loader for applications that require
+ * flexible class loading mechanisms. The <tt>ModuleManager</tt> is not
+ * class loader itself, but it supports the concept of a
+ * <a href="Module.html"><tt>Module</tt></a>,
+ * which is a unit of organization for application classes and resources.
+ * The <tt>ModuleManager</tt> has only a handful of methods that allow
+ * an application to add, remove, reset, and query modules; the intent
+ * is to place as few assumptions in the <tt>ModuleManager</tt> as possible.
+ * </p>
+ * <p>
+ * The idea is simple, allow the application to map itself into modules
+ * however it sees fit and let the <tt>ModuleManager</tt> assume the
+ * responsibility of managing the modules and loading classes and resources
+ * from them as necessary via <a href="ModuleClassLoader.html"><tt>ModuleClassLoader</tt></a>s
+ * that are associated with each module. In order to achieve this goal, though, the
+ * <tt>ModuleManager</tt> must make at least one assumption on behalf of
+ * the application. This assumption is that the loading of classes and resources
+ * from the available modules must happen using a search algorithm
+ * that is particular to the application itself. As a result of this assumption,
+ * the <tt>ModuleManager</tt> requires that the application provide a concrete
+ * implementation of the <a href="SearchPolicy.html"><tt>SearchPolicy</tt></a>
+ * interface.
+ * </p>
+ * <p>
+ * The search policy allows the <tt>ModuleLoader</tt> to let applications inject
+ * their own particular class loading policies, without dictating strict or
+ * constraining base assumptions. Of course, it is likely that many applications
+ * will use the same or very similar search policies. Because of this, another
+ * goal of the <tt>ModuleLoader</tt> approach is to foster a common library of
+ * search policies that applications are free to use or customize as they see
+ * fit. These common search policies are analagous to patterns, where each search
+ * policy is viewable as a <i>class loading pattern</i>. Some initial search
+ * policies included with the <tt>ModuleLoader</tt> are
+ * <a href="search/ExhaustiveSearchPolicy.html"><tt>ExhaustiveSearchPolicy</tt></a>,
+ * <a href="search/SelfContainedSearchPolicy.html"><tt>SelfContainedSearchPolicy</tt></a>, and
+ * <a href="search/ImportSearchPolicy.html"><tt>ImportSearchPolicy</tt></a>.
+ * </p>
+ * <p>
+ * Due to the fact that class loaders are tied to security and resource loading,
+ * the search policy alone is not sufficient for the <tt>ModuleLoader</tt> to
+ * perform its function. To fulfill these other purposes, the <tt>ModuleLoader</tt>
+ * introduces another policy interface, called the <a href="URLPolicy.html"><tt>URLPolicy</tt></a>.
+ * The <tt>URLPolicy</tt> allows the application to inject its particular policy
+ * for to purposes:
+ * </p>
+ * <ol>
+ *   <li>Creating the <tt>URL</tt> associated with loading a resource, such as
+ *       the <tt>URL</tt> returned from a call to <tt>Class.getResource()</tt>.
+ *   </li>
+ *   <li>Creating the <tt>URL</tt> that will be associated with a class's
+ *       <tt>CodeSource</tt> when defining the class for purposes of security
+ *       and assigning permissions.
+ *   </li>
+ * </ol>
+ * <p>
+ * The <tt>ModuleLoader</tt> defines a default <tt>URLPolicy</tt>, called
+ * <a href="DefaultURLPolicy.html"><tt>DefaultURLPolicy</tt></a>, that provides
+ * a simple <tt>URLStreamHandler</tt> for accessing resources inside of modules
+ * and that returns <tt>null</tt> for the <tt>CodeSource</tt> <tt>URL</tt>.
+ * Applications only need to supply their own <tt>URLPolicy</tt> if the default
+ * one does not provide the appropriate behavior.
+ * </p>
+ * <p>
+ * It is possible for an application to create multiple instances of the
+ * <tt>ModuleManager</tt> within a single JVM, but it is not possible to
+ * share modules across multiple <tt>ModuleManager</tt>s. A given <tt>ModuleManager</tt>
+ * can only have one <tt>SelectionPolicy</tt> and one <tt>URLPolicy</tt>.
+ * </p>
+ * @see org.apache.felix.moduleloader.Module
+ * @see org.apache.felix.moduleloader.ModuleClassLoader
+ * @see org.apache.felix.moduleloader.SearchPolicy
+ * @see org.apache.felix.moduleloader.URLPolicy
+ * @see org.apache.felix.moduleloader.DefaultURLPolicy
+**/
+public class ModuleManager
+{
+    private List m_moduleList = new ArrayList();
+    private Map m_moduleMap = new HashMap();
+    private SearchPolicy m_searchPolicy = null;
+    private URLPolicy m_urlPolicy = null;
+    private ModuleListener[] m_listeners = null;
+    private static final ModuleListener[] m_noListeners = new ModuleListener[0];
+
+    /**
+     * <p>
+     * Constructs a <tt>ModuleManager</tt> instance using the specified
+     * search policy and the default <tt>URL</tt> policy.
+     * </p>
+     * @param searchPolicy the search policy that the instance should use.
+     * @see org.apache.felix.moduleloader.SearchPolicy
+    **/
+    public ModuleManager(SearchPolicy searchPolicy)
+    {
+        this(searchPolicy, null);
+    }
+
+    /**
+     * <p>
+     * Constructs a <tt>ModuleManager</tt> instance using the specified
+     * search policy and the specified <tt>URL</tt> policy.
+     * </p>
+     * @param searchPolicy the search policy that the instance should use.
+     * @param urlPolicy the <tt>URL</tt> policy that the instance should use.
+     * @see org.apache.felix.moduleloader.SearchPolicy
+     * @see org.apache.felix.moduleloader.URLPolicy
+    **/
+    public ModuleManager(SearchPolicy searchPolicy, URLPolicy urlPolicy)
+    {
+        m_listeners = m_noListeners;
+        m_searchPolicy = searchPolicy;
+        m_searchPolicy.setModuleManager(this);
+
+        if (urlPolicy == null)
+        {
+            m_urlPolicy = new DefaultURLPolicy();
+        }
+        else
+        {
+            m_urlPolicy = urlPolicy;
+        }
+    }
+
+    /**
+     * <p>
+     * Returns the <tt>URL</tt> policy used by this instance.
+     * </p>
+     * @return the <tt>URL</tt> policy used by this instance.
+     * @see org.apache.felix.moduleloader.URLPolicy
+    **/
+    public URLPolicy getURLPolicy()
+    {
+        return m_urlPolicy;
+    }
+
+    /**
+     * <p>
+     * Returns the search policy used by this instance.
+     * </p>
+     * @return the search policy used by this instance.
+     * @see org.apache.felix.moduleloader.SearchPolicy
+    **/
+    public SearchPolicy getSearchPolicy()
+    {
+        return m_searchPolicy;
+    }
+
+    /**
+     * <p>
+     * Returns an array of all modules being managed by the
+     * <tt>ModuleManager</tt> instance. The array contains a snapshot of
+     * all modules in the <tt>ModuleManager</tt> at the time when this
+     * method was called.
+     * </p>
+     * @return an array of all modules being managed by the <tt>ModuleManager</tt>
+     *         instance.
+     * @see org.apache.felix.moduleloader.Module
+    **/
+    public synchronized Module[] getModules()
+    {
+        Module[] modules = new Module[m_moduleList.size()];
+        return (Module[]) m_moduleList.toArray(modules);
+    }
+
+    /**
+     * <p>
+     * Returns a module associated with the specified identifier.
+     * </p>
+     * @param id the identifier for the module to be retrieved.
+     * @return the module associated with the identifier or <tt>null</tt>.
+     * @see org.apache.felix.moduleloader.Module
+    **/
+    public synchronized Module getModule(String id)
+    {
+        return (Module) m_moduleMap.get(id);
+    }
+
+    /**
+     * <p>
+     * Adds a module to the module manager. The module will have the specified
+     * unique identifier, with the associated attributes, resource sources, and
+     * library sources. If the identifier is not unique, then an exception is
+     * thrown.
+     * </p>
+     * @param id the unique identifier of the new module.
+     * @param attributes an array of key-value attribute pairs to
+     *        associate with the module.
+     * @param resSources an array of <tt>ResourceSource</tt>s to associate
+     *        with the module.
+     * @param libSources an array of <tt>LibrarySource</tt>s to associate
+     *        with the module.
+     * @return the newly created module.
+     * @throws java.lang.IllegalArgumentException if the module identifier
+     *         is not unique.
+     * @see org.apache.felix.moduleloader.Module
+     * @see org.apache.felix.moduleloader.ResourceSource
+     * @see org.apache.felix.moduleloader.LibrarySource
+    **/
+    public Module addModule(String id, Object[][] attributes,
+        ResourceSource[] resSources, LibrarySource[] libSources)
+    {
+        return addModule(id, attributes, resSources, libSources, false);
+    }
+
+    public Module addModule(String id, Object[][] attributes,
+        ResourceSource[] resSources, LibrarySource[] libSources,
+        boolean useParentSource)
+    {
+        Module module = null;
+
+        // Use a synchronized block instead of synchronizing the
+        // method, so we can fire our event outside of the block.
+        synchronized (this)
+        {
+            if (m_moduleMap.get(id) == null)
+            {
+                module = new Module(this, id, attributes, resSources, libSources, useParentSource);
+                m_moduleList.add(module);
+                m_moduleMap.put(id, module);
+            }
+            else
+            {
+                throw new IllegalArgumentException("Module ID must be unique.");
+            }
+        }
+
+        // Fire event here instead of inside synchronized block.
+        fireModuleAdded(module);
+
+        return module;
+    }
+
+    /**
+     * <p>
+     * Resets a given module. In resetting a module, the module's associated
+     * class loader is thrown away; it is the application's responsibility to
+     * determine when and how that application code stops using classes (and
+     * subsequent instances) from the class loader of the reset module.
+     * This method allows the associated elements of the module (i.e.,
+     * attributes, resource sources, and library sources) to be changed also;
+     * if these elements have not changed then they simply need to be passed
+     * back in from the existing module. This method is useful in situations
+     * where the underlying module needs to be changed at run time, such as
+     * might be necessary if a module was updated.
+     * </p>
+     * <p>
+     * The same effect could be achieved by first removing and then re-adding
+     * a module, but with one subtle different. By removing and then re-adding
+     * a module, a new module is created and, thus, all existing references
+     * become invalid. By explicitly having this method, the <tt>ModuleManager</tt>
+     * maintains the integrity of the module reference, which is more intuitive
+     * in the case where an updated module is intended to be the same module,
+     * only updated.
+     * </p>
+     * @param module the module reset.
+     * @param attributes an array of key-value attribute pairs to
+     *        associate with the module.
+     * @param resSources an array of <tt>ResourceSource</tt>s to associate
+     *        with the module.
+     * @param libSources an array of <tt>LibrarySource</tt>s to associate
+     *        with the module.
+     * @see org.apache.felix.moduleloader.Module
+     * @see org.apache.felix.moduleloader.ResourceSource
+     * @see org.apache.felix.moduleloader.LibrarySource
+    **/
+    public void resetModule(
+        Module module, Object[][] attributes,
+        ResourceSource[] resSources, LibrarySource[] libSources)
+    {
+        // Use a synchronized block instead of synchronizing the
+        // method, so we can fire our event outside of the block.
+        synchronized (this)
+        {
+            module = (Module) m_moduleMap.get(module.getId());
+            if (module != null)
+            {
+                module.reset(attributes, resSources, libSources);
+            }
+            else
+            {
+                // Don't fire event.
+                return;
+            }
+        }
+
+        // Fire event here instead of inside synchronized block.
+        fireModuleReset(module);
+    }
+
+    /**
+     * <p>
+     * Removes the specified module from the <tt>ModuleManager</tt>. Removing
+     * a module only removed the module from the <tt>ModuleManager</tt>. It is
+     * the application's responsibility to determine when and how application code
+     * stop using classes (and subsequent instances) that were loaded from
+     * the class loader of the removed module.
+     * </p>
+     * @param module the module to remove.
+    **/
+    public void removeModule(Module module)
+    {
+        // Use a synchronized block instead of synchronizing the
+        // method, so we can fire our event outside of the block.
+        synchronized (this)
+        {
+            if (m_moduleMap.get(module.getId()) != null)
+            {
+                // Remove from data structures.
+                m_moduleList.remove(module);
+                m_moduleMap.remove(module.getId());
+
+                // Dispose of the module.
+                module.dispose();
+            }
+            else
+            {
+                // Don't fire event.
+                return;
+            }
+        }
+
+        // Fire event here instead of inside synchronized block.
+        fireModuleRemoved(module);
+    }
+
+    /**
+     * <p>
+     * Adds a listener to the <tt>ModuleManager</tt> to listen for
+     * module added, reset, and removed events.
+     * </p>
+     * @param l the <tt>ModuleListener</tt> to add.
+    **/
+    public void addModuleListener(ModuleListener l)
+    {
+        // Verify listener.
+        if (l == null)
+        {
+            throw new IllegalArgumentException("Listener is null");
+        }
+
+        // Use the m_noListeners object as a lock.
+        synchronized (m_noListeners)
+        {
+            // If we have no listeners, then just add the new listener.
+            if (m_listeners == m_noListeners)
+            {
+                m_listeners = new ModuleListener[] { l };
+            }
+            // Otherwise, we need to do some array copying.
+            // Notice, the old array is always valid, so if
+            // the dispatch thread is in the middle of a dispatch,
+            // then it has a reference to the old listener array
+            // and is not affected by the new value.
+            else
+            {
+                ModuleListener[] newList = new ModuleListener[m_listeners.length + 1];
+                System.arraycopy(m_listeners, 0, newList, 0, m_listeners.length);
+                newList[m_listeners.length] = l;
+                m_listeners = newList;
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Removes a listener from the <tt>ModuleManager</tt>.
+     * </p>
+     * @param l the <tt>ModuleListener</tt> to remove.
+    **/
+    public void removeModuleListener(ModuleListener l)
+    {
+        // Verify listener.
+        if (l == null)
+        {
+            throw new IllegalArgumentException("Listener is null");
+        }
+
+        // Use the m_noListeners object as a lock.
+        synchronized (m_noListeners)
+        {
+            // Try to find the instance in our list.
+            int idx = -1;
+            for (int i = 0; i < m_listeners.length; i++)
+            {
+                if (m_listeners[i].equals(l))
+                {
+                    idx = i;
+                    break;
+                }
+            }
+
+            // If we have the instance, then remove it.
+            if (idx >= 0)
+            {
+                // If this is the last listener, then point to empty list.
+                if (m_listeners.length == 1)
+                {
+                    m_listeners = m_noListeners;
+                }
+                // Otherwise, we need to do some array copying.
+                // Notice, the old array is always valid, so if
+                // the dispatch thread is in the middle of a dispatch,
+                // then it has a reference to the old listener array
+                // and is not affected by the new value.
+                else
+                {
+                    ModuleListener[] newList = new ModuleListener[m_listeners.length - 1];
+                    System.arraycopy(m_listeners, 0, newList, 0, idx);
+                    if (idx < newList.length)
+                    {
+                        System.arraycopy(m_listeners, idx + 1, newList, idx,
+                            newList.length - idx);
+                    }
+                    m_listeners = newList;
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Fires an event indicating that the specified module was added
+     * to the <tt>ModuleManager</tt>.
+     * </p>
+     * @param module the module that was added.
+    **/
+    protected void fireModuleAdded(Module module)
+    {
+        // Event holder.
+        ModuleEvent event = null;
+
+        // Get a copy of the listener array, which is guaranteed
+        // to not be null.
+        ModuleListener[] listeners = m_listeners;
+
+        // Loop through listeners and fire events.
+        for (int i = 0; i < listeners.length; i++)
+        {
+            // Lazily create event.
+            if (event == null)
+            {
+                event = new ModuleEvent(this, module);
+            }
+            listeners[i].moduleAdded(event);
+        }
+    }
+
+    /**
+     * <p>
+     * Fires an event indicating that the specified module was reset.
+     * </p>
+     * @param module the module that was reset.
+    **/
+    protected void fireModuleReset(Module module)
+    {
+        // Event holder.
+        ModuleEvent event = null;
+
+        // Get a copy of the listener array, which is guaranteed
+        // to not be null.
+        ModuleListener[] listeners = m_listeners;
+
+        // Loop through listeners and fire events.
+        for (int i = 0; i < listeners.length; i++)
+        {
+            // Lazily create event.
+            if (event == null)
+            {
+                event = new ModuleEvent(this, module);
+            }
+            listeners[i].moduleReset(event);
+        }
+    }
+
+    /**
+     * <p>
+     * Fires an event indicating that the specified module was removed
+     * from the <tt>ModuleManager</tt>.
+     * </p>
+     * @param module the module that was removed.
+    **/
+    protected void fireModuleRemoved(Module module)
+    {
+        // Event holder.
+        ModuleEvent event = null;
+
+        // Get a copy of the listener array, which is guaranteed
+        // to not be null.
+        ModuleListener[] listeners = m_listeners;
+
+        // Loop through listeners and fire events.
+        for (int i = 0; i < listeners.length; i++)
+        {
+            // Lazily create event.
+            if (event == null)
+            {
+                event = new ModuleEvent(this, module);
+            }
+            listeners[i].moduleRemoved(event);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/ModuleURLConnection.java b/src/org/apache/felix/moduleloader/ModuleURLConnection.java
new file mode 100644
index 0000000..9015bbf
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/ModuleURLConnection.java
@@ -0,0 +1,159 @@
+/*
+ *   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.moduleloader;
+
+import java.io.*;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.Permission;
+
+class ModuleURLConnection extends URLConnection
+{
+    private ModuleManager m_mgr = null;
+    private int m_contentLength;
+    private long m_contentTime;
+    private String m_contentType;
+    private InputStream m_is;
+
+    public ModuleURLConnection(ModuleManager mgr, URL url)
+    {
+        super(url);
+        m_mgr = mgr;
+    }
+
+    public void connect() throws IOException
+    {
+        if (!connected)
+        {
+            // The URL is constructed like this:
+            // module://<module-id>/<source-idx>/<resource-path>
+            Module module = m_mgr.getModule(url.getHost());
+            if (module == null)
+            {
+                throw new IOException("Unable to find bundle's module.");
+            }
+
+            String resource = url.getFile();
+            if (resource == null)
+            {
+                throw new IOException("Unable to find resource: " + url.toString());
+            }
+            if (resource.startsWith("/"))
+            {
+                resource = resource.substring(1);
+            }
+            int rsIdx = -1;
+            try
+            {
+                rsIdx = Integer.parseInt(resource.substring(0, resource.indexOf("/")));
+            }
+            catch (NumberFormatException ex)
+            {
+                new IOException("Error parsing resource index.");
+            }
+            resource = resource.substring(resource.indexOf("/") + 1);
+
+            // Get the resource bytes from the resource source.
+            byte[] bytes = null;
+            ResourceSource[] resSources = module.getResourceSources();
+            if ((resSources != null) && (rsIdx < resSources.length))
+            {
+                if (resSources[rsIdx].hasResource(resource))
+                {
+                    bytes = resSources[rsIdx].getBytes(resource);
+                }
+            }
+
+            if (bytes == null)
+            {
+                throw new IOException("Unable to find resource: " + url.toString());
+            }
+
+            m_is = new ByteArrayInputStream(bytes);
+            m_contentLength = bytes.length;
+            m_contentTime = 0L;  // TODO: Change this.
+            m_contentType = URLConnection.guessContentTypeFromName(resource);
+            connected = true;
+        }
+    }
+
+    public InputStream getInputStream()
+        throws IOException
+    {
+        if (!connected)
+        {
+            connect();
+        }
+        return m_is;
+    }
+
+    public int getContentLength()
+    {
+        if (!connected)
+        {
+            try {
+                connect();
+            } catch(IOException ex) {
+                return -1;
+            }
+        }
+        return m_contentLength;
+    }
+
+    public long getLastModified()
+    {
+        if (!connected)
+        {
+            try {
+                connect();
+            } catch(IOException ex) {
+                return 0;
+            }
+        }
+        if (m_contentTime != -1L)
+        {
+            return m_contentTime;
+        }
+        else
+        {
+            return 0L;
+        }
+    }
+
+    public String getContentType()
+    {
+        if (!connected)
+        {
+            try {
+                connect();
+            } catch(IOException ex) {
+                return null;
+            }
+        }
+        return m_contentType;
+    }
+
+    public Permission getPermission()
+    {
+        // TODO: This should probably return a FilePermission
+        // to access the bundle JAR file, but we don't have the
+        // necessary information here to construct the absolute
+        // path of the JAR file...so it would take some
+        // re-arranging to get this to work.
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/ModuleURLStreamHandler.java b/src/org/apache/felix/moduleloader/ModuleURLStreamHandler.java
new file mode 100644
index 0000000..24595ec
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/ModuleURLStreamHandler.java
@@ -0,0 +1,35 @@
+/*
+ *   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.moduleloader;
+
+import java.io.IOException;
+import java.net.*;
+
+class ModuleURLStreamHandler extends URLStreamHandler
+{
+    private ModuleManager m_mgr = null;
+
+    public ModuleURLStreamHandler(ModuleManager mgr)
+    {
+        m_mgr = mgr;
+    }
+
+    protected URLConnection openConnection(URL url) throws IOException
+    {
+        return new ModuleURLConnection(m_mgr, url);
+    }
+}
diff --git a/src/org/apache/felix/moduleloader/ResourceNotFoundException.java b/src/org/apache/felix/moduleloader/ResourceNotFoundException.java
new file mode 100644
index 0000000..23be347
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/ResourceNotFoundException.java
@@ -0,0 +1,25 @@
+/*
+ *   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.moduleloader;
+
+public class ResourceNotFoundException extends Exception
+{
+    public ResourceNotFoundException(String msg)
+    {
+        super(msg);
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/ResourceSource.java b/src/org/apache/felix/moduleloader/ResourceSource.java
new file mode 100644
index 0000000..78d1ee1
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/ResourceSource.java
@@ -0,0 +1,84 @@
+/*
+ *   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.moduleloader;
+
+/**
+ * <p>
+ * This interface represents a source for obtaining resources for a
+ * given module via the module's class loader. A resource source is used
+ * for retrieving both classes and resources; at this level, classes are
+ * treated in an identical manner as an ordinary resource. Resource sources
+ * are completely arbitrary and implementations may load resources from a JAR
+ * file, the network, a database, or anywhere.
+ * </p>
+ * <p>
+ * All resource sources are initialized before first usage via a call
+ * to the <a href="#open()"><tt>ResourceSource.open()</tt></a> method and
+ * are also deinitialized via a call to
+ * <a href="#open()"><tt>ResourceSource.close()</tt></a>. Resource sources
+ * should be implemented such that they can be opened, closed, and then
+ * re-opened.
+ * </p>
+ * @see org.apache.felix.moduleloader.Module
+ * @see org.apache.felix.moduleloader.ModuleClassLoader
+**/
+public interface ResourceSource
+{
+    /**
+     * <p>
+     * This method initializes the resource source. It is called when
+     * the associated module is added to the <tt>ModuleManager</tt>. It
+     * is acceptable for implementations to ignore duplicate calls to this
+     * method if the resource source is already opened.
+     * </p>
+    **/
+    public void open();
+
+    /**
+     * <p>
+     * This method de-initializes the resource source. It is called when
+     * the associated module is removed from the <tt>ModuleManager</tt> or
+     * when the module is reset by the <tt>ModuleManager</tt>.
+     * </p>
+    **/
+    public void close();
+
+    /**
+     * <p>
+     * This method returns a boolean indicating whether the resource source
+     * contains the specified resource.
+     * </p>
+     * @param name the name of the resource whose existence is being checked.
+     * @param <tt>true</tt> if the resource source has the resource, <tt>false</tt>
+     *        otherwise.
+     * @throws java.lang.IllegalStateException if the resource source has not
+     *         been opened.
+    **/
+    public boolean hasResource(String name) throws IllegalStateException;
+
+    /**
+     * <p>
+     * This method returns a byte array of the specified resource's contents.
+     * </p>
+     * @param name the name of the resource to retrieve.
+     * @param a byte array of the resource's contents or <tt>null</tt>
+     *        if the resource was not found.
+     * @throws java.lang.IllegalStateException if the resource source has not
+     *         been opened.
+    **/
+    public byte[] getBytes(String name) throws IllegalStateException;
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/SearchPolicy.java b/src/org/apache/felix/moduleloader/SearchPolicy.java
new file mode 100644
index 0000000..dfa51f2
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/SearchPolicy.java
@@ -0,0 +1,234 @@
+/*
+ *   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.moduleloader;
+
+import java.net.URL;
+
+/**
+ * <p>
+ * This interface represents a policy to define the most basic behavior
+ * of how classes, resources, and native libraries within a specific instance
+ * of <tt>ModuleManager</tt> are found. A <tt>ModuleManager</tt> manages a set of
+ * <tt>Module</tt>s, each of which is a potential source of classes, resources,
+ * and native libraries. The search policy makes it possible to consult these
+ * sources without hard-coding assumptions about application behavior
+ * or structure. Applicaitons inject their own specific class loading policy
+ * by creating a custom search policy or by selecting a pre-existing search
+ * policy that matches their needs.
+ * </p>
+ * <p>
+ * The search policy is used by <tt>ModuleClassLoader</tt>, of which, there
+ * is one per <tt>Module</tt> within a given <tt>ModuleManager</tt> instance.
+ * The search policy is consulted by the <tt>ModuleClassLoader</tt> whenever
+ * there is a request for a class, resource, or native library. The search
+ * policy will generally search other modules in an application-specific
+ * way in order to find the requested item; for example, an application may
+ * use a policy where module's may import from one another. If the search
+ * policy provides an answer, then the <tt>ModuleClassLoader</tt> will use
+ * this to answer the originating request.
+ * </p>
+ * <p>
+ * <b><i>Important:</i></b> The search policy <i>searches</i> modules in
+ * some application-specific manner in order to find a class or resource.
+ * This <i>search</i> is instigated, either directly or indirectly, by calls
+ * to <tt>ModuleClassLoader.loadClass()</tt> and <tt>ModuleClassLoader.getResource()</tt>,
+ * respectively. In order for the search policy to load a class or resource,
+ * it must <b>not</b> use <tt>ModuleClassLoader.loadClass()</tt> or
+ * <tt>ModuleClassLoader.getResource()</tt> again, because this would result
+ * in an infinite loop. Instead, the <tt>ModuleClassLoader</tt> offers the
+ * the methods <tt>ModuleClassLoader.loadClassFromModule()</tt> and
+ * <tt>ModuleClassLoader.getResourceFromModule()</tt> to search a given module
+ * and to avoid an infinite loop.
+ * </p>
+ * <pre>
+ *     ...
+ *     public Class findClass(Module module, String name)
+ *     {
+ *         Module[] modules = m_mgr.getModules();
+ *         for (int i = 0; i < modules.length; i++)
+ *         {
+ *             try {
+ *                 Class clazz = modules[i].getClassLoader().loadClassFromModule(name);
+ *                 if (clazz != null)
+ *                 {
+ *                     return clazz;
+ *                 }
+ *             } catch (Throwable th) {
+ *             }
+ *         }
+ *
+ *         return null;
+ *     }
+ *     ...
+ * </pre>
+ * <p>
+ * In the above code, the search policy "exhaustively" searches every module in the
+ * <tt>ModuleManager</tt> to find the requested resource. Note that this policy
+ * will also search the module that originated the request, which is not totally
+ * necessary since returning <tt>null</tt> will cause the <tt>ModuleClassLoader</tt>
+ * to search the originating module's <tt>ResourceSource</tt>s.
+ * </p>
+**/
+public interface SearchPolicy
+{
+    /**
+     * <p>
+     * This method is called once by the <tt>ModuleManager</tt> to
+     * give the search policy instance a reference to its associated
+     * module manager. This method should be implemented such that
+     * it cannot be called twice; calling this method a second time
+     * should produce an illegal state exception.
+     * </p>
+     * @param mgr the module manager associated with this search policy.
+     * @throws java.lang.IllegalStateException if the method is called
+     *         more than once.
+    **/
+    public void setModuleManager(ModuleManager mgr)
+        throws IllegalStateException;
+
+    /**
+     * <p>
+     * The <tt>ModuleClassLoader</tt> calls this method before performing
+     * the call to <tt>ClassLoader.defineClass()</tt> to give the search policy
+     * an opportunity to define the <tt>Package</tt> object for the specified
+     * package. The method should return an array of <tt>String</tt> values for
+     * each of the following: specTitle, specVersion, specVendor, implTitle,
+     * implVersion, and implVendor. See <tt>ClassLoader.definePackage()</tt>
+     * for more details. The returned array may contain <tt>null</tt>s, but
+     * the return array must have six elements.
+     * </p>
+     * @param module the module requesting a class from the package.
+     * @param pkgName the package name of the class being requested.
+     * @return an array containing values for creating the <tt>Package</tt>
+     *         object for the specified package.
+    **/
+    public Object[] definePackage(Module module, String pkgName);
+
+    /**
+     * <p>
+     * When a module instigates a class load operation, this method is called
+     * to find the desired class for the instigating module. This method is
+     * called <b>before</b> searching the module's resource sources for the class.
+     * How the class is found is dependent upon the search policy implementation.
+     * </p>
+     * <p>
+     * This method may return <tt>null</tt> or throw an exception if the
+     * specified class is not found. Whether a specific search policy
+     * implementation should do one or the other depends on the details
+     * of the specific search policy. The <tt>ModuleClassLoader</tt>
+     * first delegates to this method, then to the local resources
+     * sources of the module, and then finally to then the
+     * <tt>SearchPolicy.findClassAfterModule()</tt> method. If this method
+     * returns null, then the search for the class will continue to these
+     * latter two steps. On the other hand, if this method returns a class
+     * or throws an exception, then the latter two steps will not be searched.
+     * </p>
+     * <p>
+     * <b>Important:</b> If the implementation of this method delegates
+     * the class loading to a <tt>ModuleClassLoader</tt> of another module,
+     * then it should <b>not</b> use the method <tt>ModuleClassLoader.loadClass()</tt>
+     * to load the class; it should use <tt>ModuleClassLoader.loadClassFromModule()</tt>
+     * instead. This is necessary to eliminate an infinite loop that would
+     * occur otherwise. Also, with respect to the <tt>ModuleLoader</tt> framework,
+     * this method will only be called by a single thread at a time and is only
+     * intended to be called by <tt>ModuleClassLoader.loadClass()</tt>.
+     * </p>
+     * @param parent the parent class loader of the delegating class loader.
+     * @param module the target module that is loading the class.
+     * @param name the name of the class being loaded.
+     * @return the class if found, <tt>null</tt> otherwise.
+     * @throws java.lang.ClassNotFoundException if the class could not be
+     *         found and the entire search operation should fail.
+    **/
+    public Class findClassBeforeModule(ClassLoader parent, Module module, String name)
+        throws ClassNotFoundException;
+
+    /**
+     * <p>
+     * When a module instigates a class load operation, this method is called
+     * to find the desired class for the instigating module. This method is
+     * called <b>after</b> searching the module's resource sources for the class.
+     * How the class is found is dependent upon the search policy implementation.
+     * </p>
+     * <p>
+     * The <tt>ModuleClassLoader</tt> first delegates to the
+     * <tt>SearchPolicy.findClassBeforeModule() method, then to the local
+     * resources sources of the module, and then finally to this method.
+     * This method is the last attempt to find the class and if it fails
+     * (by either return <tt>null</tt> or throwing an exception), then the
+     * result of the entire class load will fail.
+     * </p>
+     * <p>
+     * <b>Important:</b> If the implementation of this method delegates
+     * the class loading to a <tt>ModuleClassLoader</tt> of another module,
+     * then it should <b>not</b> use the method <tt>ModuleClassLoader.loadClass()</tt>
+     * to load the class; it should use <tt>ModuleClassLoader.loadClassFromModule()</tt>
+     * instead. This is necessary to eliminate an infinite loop that would
+     * occur otherwise. Also, with respect to the <tt>ModuleLoader</tt> framework,
+     * this method will only be called by a single thread at a time and is only
+     * intended to be called by <tt>ModuleClassLoader.loadClass()</tt>.
+     * </p>
+     * @param parent the parent class loader of the delegating class loader.
+     * @param module the target module that is loading the class.
+     * @param name the name of the class being loaded.
+     * @return the class if found, <tt>null</tt> otherwise.
+     * @throws java.lang.ClassNotFoundException if the class could not be
+     *         found and the entire search operation should fail.
+    **/
+    public Class findClassAfterModule(ClassLoader parent, Module module, String name)
+        throws ClassNotFoundException;
+
+    /**
+     * <p>
+     * This method tries to find the specified resource for the specified
+     * module. How the resource is found or whether it is actually retrieved
+     * from the specified module is dependent upon the implementation. The
+     * default <tt>ModuleClassLoader.getResource()</tt> method does not do
+     * any searching on its own.
+     * </p>
+     * <p>
+     * This method may return <tt>null</tt> or throw an exception if the
+     * specified resource is not found. Whether a specific search policy
+     * implementation should do one or the other depends on the details
+     * of the specific search policy. The <tt>ModuleClassLoader</tt>
+     * first delegates to this method and then to the local resource
+     * sources of the module. If this method returns null, then the local
+     * resource sources will be searched. On the other hand, if this method
+     * throws an exception, then the local resource sources will not be
+     * searched.
+     * </p>
+     * <p>
+     * <b>Important:</b> If the implementation of this method delegates
+     * the resource loading to a <tt>ModuleClassLoader</tt> of another module,
+     * then it should not use the method <tt>ModuleClassLoader.getResource()</tt>
+     * to get the resource; it should use <tt>ModuleClassLoader.getResourceFromModule()</tt>
+     * instead. This is necessary to eliminate an infinite loop that would
+     * occur otherwise. Also, with respect to the <tt>ModuleLoader</tt> framework,
+     * this method will only be called by a single thread at a time and is not
+     * intended to be called directly.
+     * </p>
+     * @param parent the parent class loader of the delegating class loader.
+     * @param module the target module that is loading the resource.
+     * @param name the name of the resource being loaded.
+     * @return a <tt>URL</tt> to the resource if found, <tt>null</tt> otherwise.
+     * @throws org.apache.felix.moduleloader.ResourceNotFoundException if the
+     *         resource could not be found and the entire search operation
+     *         should fail.
+    **/
+    public URL findResource(ClassLoader parent, Module module, String name)
+        throws ResourceNotFoundException;
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/URLPolicy.java b/src/org/apache/felix/moduleloader/URLPolicy.java
new file mode 100644
index 0000000..452aebc
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/URLPolicy.java
@@ -0,0 +1,74 @@
+/*
+ *   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.moduleloader;
+
+import java.net.URL;
+
+/**
+ * <p>
+ * This interface represents the <tt>ModuleLoader</tt>'s policy for creating
+ * <tt>URL</tt> for resource loading and security purposes. Java requires the
+ * use of <tt>URL</tt>s for resource loading and security. For resource loading,
+ * <tt>URL</tt>s are returned for requested resources. Subsequently, the resource
+ * <tt>URL</tt> is used to create an input stream for reading the resources
+ * bytes. With respect to security, <tt>URL</tt>s are used when defining a
+ * class in order to determine where the code came from, this concept is called
+ * a <tt>CodeSource</tt>. This approach enables Java to assign permissions to
+ * code that originates from particular locations.
+ * </p>
+ * <p>
+ * The <tt>ModuleManager</tt> requires a concrete implementation of this
+ * interface in order to function. Whenever the <tt>ModuleManager</tt> requires
+ * a <tt>URL</tt> for either resource loading or security, it delegates to
+ * the policy implementation. A default implementation is provided,
+ * called <a href="DefaultURLPolicy.html"><tt>DefaultURLPolicy</tt></a>, but
+ * it only supports resource loading, not security.
+ * </p>
+ * @see org.apache.felix.moduleloader.ModuleManager
+ * @see org.apache.felix.moduleloader.DefaultURLPolicy
+**/
+public interface URLPolicy
+{
+    /**
+     * <p>
+     * This method should return a <tt>URL</tt> that represents the
+     * location from which the module originated. This <tt>URL</tt>
+     * can be used when assigning permissions to the module, such as
+     * is done in the Java permissions policy file.
+     * </p>
+     * @param mgr the <tt>ModuleManager</tt> of the module.
+     * @param module the module for which the <tt>URL</tt> is to be created.
+     * @return an <tt>URL</tt> to associate with the module.
+    **/
+    public URL createCodeSourceURL(ModuleManager mgr, Module module);
+
+    /**
+     * <p>
+     * This method should return a <tt>URL</tt> that is suitable
+     * for accessing the bytes of the specified resource. It must be possible
+     * open a connection to this <tt>URL</tt>, which may require that
+     * the implementer of this method also introduce a custom
+     * <tt>java.net.URLStreamHander</tt> when creating the <tt>URL</tt>.
+     * </p>
+     * @param mgr the <tt>ModuleManager</tt> of the module.
+     * @param module the module for which the resource is being loaded.
+     * @param rsIdx the index of the <tt>ResourceSource</tt> containing the resource.
+     * @param name the name of the resource being loaded.
+     * @return an <tt>URL</tt> for retrieving the resource.
+    **/
+    public URL createResourceURL(ModuleManager mgr, Module module, int rsIdx, String name);
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/Util.java b/src/org/apache/felix/moduleloader/Util.java
new file mode 100755
index 0000000..34b16d7
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/Util.java
@@ -0,0 +1,64 @@
+/*
+ *   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.moduleloader;
+
+public class Util
+{
+    public static String getClassName(String className)
+    {
+        if (className == null)
+        {
+            className = "";
+        }
+        return (className.lastIndexOf('.') < 0)
+            ? "" : className.substring(className.lastIndexOf('.') + 1);
+    }
+
+    public static String getClassPackage(String className)
+    {
+        if (className == null)
+        {
+            className = "";
+        }
+        return (className.lastIndexOf('.') < 0)
+            ? "" : className.substring(0, className.lastIndexOf('.'));
+    }
+
+    public static String getResourcePackage(String resource)
+    {
+        if (resource == null)
+        {
+            resource = "";
+        }
+        // NOTE: The package of a resource is tricky to determine since
+        // resources do not follow the same naming conventions as classes.
+        // This code is pessimistic and assumes that the package of a
+        // resource is everything up to the last '/' character. By making
+        // this choice, it will not be possible to load resources from
+        // imports using relative resource names. For example, if a
+        // bundle exports "foo" and an importer of "foo" tries to load
+        // "/foo/bar/myresource.txt", this will not be found in the exporter
+        // because the following algorithm assumes the package name is
+        // "foo.bar", not just "foo". This only affects imported resources,
+        // local resources will work as expected.
+        String pkgName = (resource.startsWith("/")) ? resource.substring(1) : resource;
+        pkgName = (pkgName.lastIndexOf('/') < 0)
+            ? "" : pkgName.substring(0, pkgName.lastIndexOf('/'));
+        pkgName = pkgName.replace('/', '.');
+        return pkgName;
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/CompatibilityPolicy.java b/src/org/apache/felix/moduleloader/search/CompatibilityPolicy.java
new file mode 100644
index 0000000..86b7efd
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/CompatibilityPolicy.java
@@ -0,0 +1,62 @@
+/*
+ *   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.moduleloader.search;
+
+/**
+ * <p>
+ * This interface represents the naming and version numbering policy of
+ * import and export identifiers for the <tt>ImportSearchPolicy</tt>. A concrete
+ * implementation of this interface is required to create an instance
+ * of <tt>ImportSearchPolicy</tt>. The sole purpose of this interface
+ * is to allow the <tt>ImportSearchPolicy</tt> to determine if one
+ * import/export identifier and version is compatible with another.
+ * </p>
+ * @see org.apache.felix.moduleloader.search.ImportSearchPolicy
+**/
+public interface CompatibilityPolicy
+{
+    /**
+     * Compares two import/export identifiers.
+     * @param leftId the identifier to test for compatibility.
+     * @param leftVersion the version number to test for compatibility.
+     * @param rightId the identifier used as the compatibility base line.
+     * @param rightVersion the version used as the compatibility base line.
+     * @return <tt>0</tt> if the identifiers are equal, <tt>-1</tt> if the
+     *         left identifier is less then the right identifier, and <tt>1</tt>
+     *         if the left identifier is greater than the right identifier.
+     * @throws java.lang.IllegalArgumentException if the two identifiers
+     *         are not comparable, i.e., they refer to intrinsically different
+     *         entities.
+    **/
+    public int compare(
+        Object leftId, Object leftVersion,
+        Object rightId, Object rightVersion);
+
+    /**
+     * Returns whether the first import/export identifer is compatible
+     * with the second; this method should not throw any exceptions.
+     * @param leftId the identifier to test for compatibility.
+     * @param leftVersion the version number to test for compatibility.
+     * @param rightId the identifier used as the compatibility base line.
+     * @param rightVersion the version used as the compatibility base line.
+     * @return <tt>true</tt> if the left version number object is compatible
+     *         with the right version number object, otherwise <tt>false</tt>.
+    **/
+    public boolean isCompatible(
+        Object leftId, Object leftVersion,
+        Object rightId, Object rightVersion);
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/ExhaustiveSearchPolicy.java b/src/org/apache/felix/moduleloader/search/ExhaustiveSearchPolicy.java
new file mode 100644
index 0000000..d98d8e6
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/ExhaustiveSearchPolicy.java
@@ -0,0 +1,162 @@
+/*
+ *   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.moduleloader.search;
+
+import java.net.URL;
+
+import org.apache.felix.moduleloader.*;
+
+/**
+ * <p>
+ * This class implements a <tt>ModuleLoader</tt> search policy that
+ * exhaustively and linearly searches all modules when trying to load
+ * a particular class or resource. As a result of this algorithm, every class loader
+ * for every module is essentially identical, meaning that each will
+ * load a given class or resource from the same class loader. This search policy
+ * provides behavior similar to the standard <tt>CLASSPATH</tt> environment
+ * variable approach. The main difference is that modules can be added
+ * to the module manager at run time; thus, the class path is dynamically
+ * extended. This search policy is not fully dynamic, since it does not
+ * support the removal of modules at run time; if a module is removed from
+ * the module manager at run time, there is no attempt to clean up its
+ * loaded classes.
+ * </p>
+ * @see org.apache.felix.moduleloader.SearchPolicy
+ * @see org.apache.felix.moduleloader.Module
+ * @see org.apache.felix.moduleloader.ModuleClassLoader
+ * @see org.apache.felix.moduleloader.ModuleManager
+**/
+public class ExhaustiveSearchPolicy implements SearchPolicy
+{
+    private ModuleManager m_mgr = null;
+
+    /**
+     * This method is part of the <tt>SearchPolicy</tt> interface.
+     * This method is called by the <tt>ModuleManager</tt> once to
+     * give the search policy instance a reference to its associated
+     * module manager. This method should be implemented such that
+     * it cannot be called twice; calling this method a second time
+     * should produce an illegal state exception.
+     * @param mgr the module manager associated with this search policy.
+     * @throws java.lang.IllegalStateException if the method is called
+     *         more than once.
+    **/
+    public void setModuleManager(ModuleManager mgr)
+        throws IllegalStateException
+    {
+        if (m_mgr == null)
+        {
+            m_mgr = mgr;
+        }
+        else
+        {
+            throw new IllegalStateException("Module manager is already initialized");
+        }
+    }
+
+    public Object[] definePackage(Module module, String pkgName)
+    {
+        return null;
+    }
+
+    /**
+     * This method finds the specified class for the specified module. It
+     * finds the class by linearly asking each module in the module manager
+     * for the specific class. As soon as the class is found, it is returned.
+     * @param parent the parent class loader of the delegating class loader.
+     * @param module the target module that is loading the class.
+     * @param name the name of the class being loaded.
+     * @return the class if found, <tt>null</tt> otherwise.
+    **/
+    public Class findClassBeforeModule(ClassLoader parent, Module module, String name)
+    {
+        // First, try to load from parent.
+        if (parent != null)
+        {
+            try
+            {
+                Class c = parent.loadClass(name);
+                if (c != null)
+                {
+                    return c;
+                }
+            }
+            catch (ClassNotFoundException ex)
+            {
+                // Ignore and search modules.
+            }
+        }
+
+        Module[] modules = m_mgr.getModules();
+        for (int i = 0; i < modules.length; i++)
+        {
+            try {
+                Class clazz = modules[i].getClassLoader().loadClassFromModule(name);
+                if (clazz != null)
+                {
+                    return clazz;
+                }
+            } catch (Throwable th) {
+            }
+        }
+
+        return null;
+    }
+
+    public Class findClassAfterModule(ClassLoader parent, Module module, String name)
+    {
+        return null;
+    }
+
+    /**
+     * This method finds the specified resource for the specified module. It
+     * finds the resource by linearly asking each module in the module manager
+     * for specific resource. As soon as the resource is found, a <tt>URL</tt>
+     * to it is returned.
+     * @param parent the parent class loader of the delegating class loader.
+     * @param module the target module that is loading the resource.
+     * @param name the name of the resource being loaded.
+     * @return a <tt>URL</tt> to the resource if found, <tt>null</tt> otherwise.
+    **/
+    public URL findResource(ClassLoader parent, Module module, String name)
+    {
+        // First, try to load from parent.
+        if (parent != null)
+        {
+            URL url = parent.getResource(name);
+            if (url != null)
+            {
+                return url;
+            }
+        }
+
+        Module[] modules = m_mgr.getModules();
+        for (int i = 0; i < modules.length; i++)
+        {
+            try {
+                URL url = modules[i].getClassLoader().getResourceFromModule(name);
+                if (url != null)
+                {
+                    return url;
+                }
+            } catch (Throwable th) {
+            }
+        }
+
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/ImportSearchPolicy.java b/src/org/apache/felix/moduleloader/search/ImportSearchPolicy.java
new file mode 100644
index 0000000..3fefc24
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/ImportSearchPolicy.java
@@ -0,0 +1,1322 @@
+/*
+ *   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.moduleloader.search;
+
+import java.net.URL;
+import java.util.*;
+
+import org.apache.felix.moduleloader.*;
+
+/**
+ * <p>
+ * This class implements a <tt>ModuleLoader</tt> search policy to support
+ * modules that import and export classes and resources from/to one another.
+ * Modules import from other modules by specifying a set of import identifiers
+ * and associated version numbers. Modules export their classes and
+ * resources by specifying a set of export identifiers and associated
+ * versions. Exports for a given module are also treated as imports for that module,
+ * meaning that it is possible for a module that exports classes to not use
+ * the classes it exports, but to actually use classes that are exported from
+ * another module. This search policy requires the following meta-data
+ * attributes be attached to each module:
+ * </p>
+ * <ul>
+ *   <li><tt>ImportSearchPolicy.EXPORTS_ATTR</tt> - the "<tt>exports</tt>"
+ *       meta-data attribute is used to declare the module's exports,
+ *   </li>
+ *   <li><tt>ImportSearchPolicy.IMPORTS_ATTR</tt> - the "<tt>imports</tt>"
+ *       meta-data attribute is used to declare the module's imports,
+ *   </li>
+ *   <li><tt>ImportSearchPolicy.PROPAGATES_ATTR</tt> - the "<tt>propagates</tt>"
+ *       meta-data attribute is used to declare which imports are exposed or
+ *       "propagated" to clients of the module's exports, and
+ *   </li>
+ *   <li><tt>ImportSearchPolicy.VALID_ATTR</tt> - the "<tt>valid</tt>"
+ *       meta-data attribute signifies the current <i>validation</i> status
+ *       of the module (this will be defined more fully below).
+ *   </li>
+ * </ul>
+ * <p>
+ * The value of the <tt>ImportSearchPolicy.EXPORTS_ATTR</tt> attribute is
+ * an array of <tt>Object</tt> arrays, i.e., <tt>Object[][]</tt>. Each element
+ * in the array signifies a particular export that is offered by this
+ * associated module. Each element is an array triple of
+ * <tt>Object</tt>, where the index into this triple is:
+ * </p>
+ * <ul>
+ *   <li><tt>ImportSearchPolicy.IDENTIFIER_IDX</tt> - the first element
+ *       is the export identifier object, used to identify the
+ *       export target. The export identifier does not have any special
+ *       meaning to the search policy and any value is allowed. A
+ *       typical identifier might be the package name of the exported classes,
+ *       such as <tt>javax.servlet</tt>.
+ *   </li>
+ *   <li><tt>ImportSearchPolicy.VERSION_IDX</tt> - the second element
+ *       is the export version number. The version number does not have
+ *       any special meaning to the search policy and any value is allowed.
+ *       A typical version number might be major, minor, and release number.
+ *   </li>
+ *   <li><tt>ImportSearchPolicy.RESOLVING_MODULE_IDX</tt> - the third element
+ *       is the resolving module for this export; since exports are treated like
+ *       imports, it is possible that the resolving module will not be the
+ *       exporting module itself. This value is filled in automatically by the
+ *       search policy and is initially <tt>null</tt>.
+ *   </li>
+ * </ul>
+ * </p>
+ * <p>
+ * The value of the <tt>ImportSearchPolicy.IMPORTS_ATTR</tt> attribute is
+ * essentially the same as the <tt>ImportSearchPolicy.EXPORTS_ATTR</tt> defined
+ * above; the only difference is that the array of versioned identifiers denote
+ * import targets rather than exports.
+ * </p>
+ * <p>
+ * The value of the <tt>ImportSearchPolicy.PROPAGATES_ATTR</tt> attribute is
+ * an array of <tt>Object</tt>s, i.e., <tt>Object[]</tt>. Each element in the
+ * array is an identifier of a propagated import target from the
+ * <tt>ImportSearchPolicy.IMPORTS_ATTR</tt> attribute. Only identifiers for
+ * import targets are candidates for inclusion and the version number is
+ * unnecessary since it is assumed from the corresponding import target.
+ * </p>
+ * <p>
+ * The value of the <tt>ImportSearchPolicy.VALID_ATTR</tt> attribute is a
+ * <tt>Boolean</tt>. The value is initially set to <tt>Boolean.FALSE</tt>
+ * and indicates that the module has not yet been validated. After the module
+ * is validated, the value is set to <tt>Boolean.TRUE</tt>. The search policy
+ * automatically adds this attribute to all modules and maintains its value.
+ * </p>
+ * <p>
+ * These meta-data attributes help the search policy enforce consistency
+ * using a process called <i>validation</i>; validation ensures that classes
+ * and resources are only loaded from a module whose imports are satisfied.
+ * Therefore, a <i>valid</i> module is a module whose imports are satisfied and
+ * an <i>invalid</i> module is a module whose imports are not yet satisfied.
+ * An invalid module may be invalid for two reasons:
+ * </p>
+ * <p>
+ * <ol>
+ *   <li>Its imports are not available or</li>
+ *   <li>It has not yet been validated.</li>
+ * </ol>
+ * </p>
+ * <p>
+ * These two possibilities arise due to the fact that module validation
+ * is not performed until it is necessary (i.e., lazy evaluation). A module
+ * is automatically validated when an attempt is made to get classes or
+ * resources from it, although it is possible to manually validate a module.
+ * For a given module, called <tt>M</tt>, the validation process attempts to
+ * find an exporting module for every import target of <tt>M</tt>. If an
+ * exporter is not found for a specific import target, then the validation of
+ * module <tt>M</tt> fails. If an exporting module is found, then this module
+ * is also validated, if it is not already. As a result, the validation of
+ * module <tt>M</tt> depends on the validation of the transitive closure of
+ * all modules on which <tt>M</tt> depends. It is also possible for modules
+ * to exhibit dependency cycles; circular dependencies are allowed.
+ * Initially, a module's <tt>VALID_ATTR</tt> is set to <tt>Boolean.FALSE</tt>,
+ * but after the module is successfully validated, this attribute is set to
+ * <tt>Boolean.TRUE</tt>.
+ * </p>
+ * <p>
+ * Besides ensuring that every import target is resolved to an appropriate
+ * exporting module, the validation process also attempts to maintain
+ * consistency along "propagation" chains. Propagation occurs when a module
+ * imports classes that are also visible from its own exports; for example,
+ * an HTTP server implementation may import classes from <tt>javax.servlet</tt>
+ * and export classes that have methods that use the type <tt>javax.servlet.Servlet</tt>
+ * in their signatures. Monitoring these types of occurences is important
+ * to uncover import source and version conflicts when multiple sources or
+ * versions of an import target are available within one virtual machine. When
+ * a module <tt>M</tt> is validated, the propagation information of each
+ * module that resolves the imports of <tt>M</tt> is checked to ensure
+ * that they do not propagate conflicting sources of <tt>M</tt>'s
+ * imports; specifically, it is verified that all propagators of a
+ * particular import target have the same source module for that import
+ * target.
+ * </p>
+ * <p>
+ * To facilitate applicability in as many scenarios as possible, this search
+ * policy delegates some decisions via additional policy interfaces. The following
+ * two policy interfaces must be specified by the code that instantiates the
+ * <tt>ImportSearchPolicy</tt> object:
+ * </p>
+ * <ul>
+ *   <li><tt>CompatibilityPolicy</tt> - this policy is used to determine
+ *       whether import/export version numbers are compatible.
+ *   </li>
+ *   <li><tt>SelectionPolicy</tt> - this policy is used to resolve a specific
+ *       import target when multiple candidate exporting modules exist.
+ *   </li>
+ * </ul>
+ * <p>
+ * Once an instance is created with definitions of the above policy interfaces,
+ * this search policy will operate largely self-contained. There are a few utility
+ * methods for manually validating modules, adding validation listeners, and
+ * access meta-data attributes, but for the most part these are not necessary
+ * except for implementing more sophisticated infrastructure.
+ * </p>
+ * <p>
+ * The follow snippet of code illustrates a typical usage scenario for
+ * this search policy:
+ * </p>
+ * <pre>
+ *     ...
+ *     ImportSearchPolicy searchPolicy =
+ *         new ImportSearchPolicy(
+ *             new MyCompatibilityPolicy(), new MySelectionPolicy());
+ *     ModuleManager mgr = new ModuleManager(searchPolicy);
+ *     ...
+ *     Object[][] exports = new Object[][] {
+ *         { "org.apache.jasper", "2.1.0", null }
+ *     };
+ *     Object[][] imports = new Object[][] {
+ *         { "javax.servlet", "2.3.1", null }
+ *     };
+ *     Object[][] attributes = new Object[][] {
+ *         new Object[] { ImportSearchPolicy.EXPORTS_ATTR, exports },
+ *         new Object[] { ImportSearchPolicy.IMPORTS_ATTR, imports },
+ *         new Object[] { ImportSearchPolicy.PROPAGATES_ATTR, new Object[] { "javax.servlet" } }
+ *      };
+ *     ResourceSource[] resSources = new ResourceSource[] {
+ *         new JarResourceSource(file1)
+ *         new JarResourceSource(file2)
+ *     };
+ *     Module module = mgr.addModule(id, attributes, resSources, null);
+ *     ClassLoader loader = module.getClassLoader();
+ *     // Assuming that all imports are satisfied...
+ *     Class clazz = loader.loadClass("org.foo.MyClass");
+ *     ...
+ * </pre>
+ * <p>
+ * The above code snippet illustrates creating a module with one export and one
+ * import, where the import is also propagated via the module's export. The module
+ * has multiple resource sources, but no library sources.
+ * </p>
+ * @see org.apache.felix.moduleloader.SearchPolicy
+ * @see org.apache.felix.moduleloader.Module
+ * @see org.apache.felix.moduleloader.ModuleClassLoader
+ * @see org.apache.felix.moduleloader.ModuleManager
+**/
+public class ImportSearchPolicy implements SearchPolicy, ModuleListener
+{
+    /**
+     * This is the name of the "exports" meta-data attribute that
+     * should be attached to each module. The value of this attribute
+     * is of type <tt>Object[][]</tt> and is described in the overview
+     * documentation for this class.
+    **/
+    public static final String EXPORTS_ATTR = "exports";
+    /**
+     * This is the name of the "imports" meta-data attribute that
+     * should be attached to each module. The value of this attribute
+     * is of type <tt>Object[][]</tt> and is described in the overview
+     * documentation for this class.
+    **/
+    public static final String IMPORTS_ATTR = "imports";
+    /**
+     * This is the name of the "propagates" meta-data attribute that
+     * should be attached to each module. The value of this attribute
+     * is of type <tt>Object[]</tt> and is described in the overview
+     * documentation for this class.
+    **/
+    public static final String PROPAGATES_ATTR = "propagates";
+    /**
+     * This is the name of the "valid" meta-data attribute that is
+     * automatically attached to each module. The value of this attribute
+     * is of type <tt>Boolean</tt> and is described in the overview
+     * documentation for this class.
+    **/
+    public static final String VALID_ATTR = "valid";
+
+    /**
+     * This is the index used to retrieve the import or export identifier
+     * from a given element of the <tt>EXPORTS_ATTR</tt> or the <tt>IMPORTS_ATTR</tt>
+     * attribute.
+    **/
+    public static final int IDENTIFIER_IDX = 0;
+    /**
+     * This is the index used to retrieve the import or export version number
+     * from a given element of the <tt>EXPORTS_ATTR</tt> or the <tt>IMPORTS_ATTR</tt>
+     * attribute.
+    **/
+    public static final int VERSION_IDX = 1;
+    /**
+     * This is the index used to retrieve the resolving module for an import
+     * or export target from a given element of the <tt>EXPORTS_ATTR</tt> or
+     * the <tt>IMPORTS_ATTR</tt> attribute.
+    **/
+    public static final int RESOLVING_MODULE_IDX = 2;
+
+    private ModuleManager m_mgr = null;
+    private CompatibilityPolicy m_compatPolicy = null;
+    private SelectionPolicy m_selectPolicy = null;
+    private ValidationListener[] m_listeners = null;
+    private String[] m_searchAttrs = { IMPORTS_ATTR, EXPORTS_ATTR };
+    private static final ValidationListener[] m_noListeners = new ValidationListener[0];
+
+    /**
+     * Constructs an import search policy instance with the supplied
+     * compatibility and selection policies.
+     * @param compatPolicy the compatibility policy implementation to be used
+     *        by the search policy.
+     * @param selectPolicy the selection policy implementation to be used
+     *        by the search policy.
+    **/
+    public ImportSearchPolicy(
+        CompatibilityPolicy compatPolicy,
+        SelectionPolicy selectPolicy)
+    {
+        m_compatPolicy = compatPolicy;
+        m_selectPolicy = selectPolicy;
+        m_listeners = m_noListeners;
+    }
+
+    /**
+     * Returns the compatibility policy used by this import search policy instance.
+     * @return the compatibility policy of this import search policy instance.
+    **/
+    public CompatibilityPolicy getCompatibilityPolicy()
+    {
+        return m_compatPolicy;
+    }
+
+    /**
+     * Returns the selection policy used by this import search policy instance.
+     * @return the selection policy of this import search policy instance.
+    **/
+    public SelectionPolicy getSelectionPolicy()
+    {
+        return m_selectPolicy;
+    }
+
+    // JavaDoc comment copied from SearchPolicy.
+    public void setModuleManager(ModuleManager mgr)
+        throws IllegalStateException
+    {
+        if (m_mgr == null)
+        {
+            m_mgr = mgr;
+            m_mgr.addModuleListener(this);
+        }
+        else
+        {
+            throw new IllegalStateException("Module manager is already initialized");
+        }
+    }
+
+    public Object[] definePackage(Module module, String pkgName)
+    {
+        return null;
+    }
+
+    /**
+     * This method is part of the <tt>SearchPolicy</tt> interface; it
+     * should not be called directly. This method finds a class
+     * based on the import/export meta-data attached to the module.
+     * It first attempts to validate the target module, if it cannot
+     * be validated, then a <tt>ClassNotFoundException</tt> is thrown.
+     * Once the module is validated, the module's imports are searched
+     * for the target class, then the module's exports are searched.
+     * If the class is found in either place, then it is returned;
+     * otherwise, <tt>null</tt> is returned.
+     * @param parent the parent class loader of the delegating class loader.
+     * @param module the target module that is loading the class.
+     * @param name the name of the class being loaded.
+     * @return the class if found, <tt>null</tt> otherwise.
+     * @throws java.lang.ClassNotFoundException if the target module
+     *         could not be validated.
+    **/
+    public Class findClassBeforeModule(ClassLoader parent, Module module, String name)
+        throws ClassNotFoundException
+    {
+        // First, try to validate the originating module.
+        try {
+            validate(module);
+        } catch (ValidationException ex) {
+            throw new ClassNotFoundException(name);
+        }
+
+        // Try to load from parent.
+        if (parent != null)
+        {
+            try
+            {
+                Class c = parent.loadClass(name);
+                if (c != null)
+                {
+                    return c;
+                }
+            }
+            catch (ClassNotFoundException ex)
+            {
+                // Ignore and search imports/exports.
+            }
+        }
+
+        // Get the package of the target class.
+        String pkgName = Util.getClassPackage(name);
+
+        // We delegate to the module's imports for finding the
+        // desired class first, then we delegate to the module's
+        // exports for finding the desired class. We do this because
+        // implicitly a module imports everything that it exports.
+        // To avoid code duplication, we use a simple array of
+        // attribute names to loop through both of the imports
+        // and exports meta-data searching for the desired class.
+        for (int attrIdx = 0; attrIdx < m_searchAttrs.length; attrIdx++)
+        {
+            Object[][] imports = getImportsOrExports(module, m_searchAttrs[attrIdx]);
+
+            // If the module doesn't import anything, then just
+            // return null.
+            if ((imports != null) && (imports.length > 0))
+            {
+                for (int i = 0; i < imports.length; i++)
+                {
+                    // Only check when the package of the class is
+                    // the same as the import package.
+                    if (imports[i][IDENTIFIER_IDX].equals(pkgName))
+                    {
+                        Module resolvingModule = (Module) imports[i][RESOLVING_MODULE_IDX];
+                        try {
+                            Class clazz =
+                                resolvingModule.getClassLoader().loadClassFromModule(name);
+                            if (clazz != null)
+                            {
+                                return clazz;
+                            }
+                        } catch (Throwable th) {
+                            // Not much we can do.
+                            System.err.println("ImportSearchPolicy: " + th.getMessage());
+                        }
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public Class findClassAfterModule(ClassLoader parent, Module module, String name)
+    {
+        return null;
+    }
+
+    /**
+     * This method is part of the <tt>SearchPolicy</tt> interface; it
+     * should not be called directly. This method finds a resource
+     * based on the import/export meta-data attached to the module.
+     * It first attempts to validate the target module, if it cannot
+     * be validated, then it returns <tt>null</tt>. Once the module is
+     * validated, the module's imports are searched for the target
+     * resource, then the module's exports are searched. If the resource
+     * is found in either place, then a <tt>URL</tt> to is is returned;
+     * otherwise, <tt>null</tt> is returned.
+     * @param parent the parent class loader of the delegating class loader.
+     * @param module the target module that is loading the resource.
+     * @param name the name of the resource being loaded.
+     * @return a <tt>URL</tt> to the resource if found, <tt>null</tt> otherwise.
+    **/
+    public URL findResource(ClassLoader parent, Module module, String name)
+    {
+        // First, try to validate the originating module.
+        try
+        {
+            validate(module);
+        }
+        catch (ValidationException ex)
+        {
+            return null;
+        }
+
+        // Try to load from parent.
+        if (parent != null)
+        {
+            URL url = parent.getResource(name);
+            if (url != null)
+            {
+                return url;
+            }
+        }
+
+        // Get the package of the target resource.
+        String pkgName = Util.getResourcePackage(name);
+
+        // We delegate to the module's imports for finding the
+        // desired class first, then we delegate to the module's
+        // exports for finding the desired class. We do this because
+        // implicitly a module imports everything that it exports.
+        // To avoid code duplication, we use a simple array of
+        // attribute names to loop through both of the imports
+        // and exports meta-data searching for the desired class.
+        for (int attrIdx = 0; attrIdx < m_searchAttrs.length; attrIdx++)
+        {
+            Object[][] imports = getImportsOrExports(module, m_searchAttrs[attrIdx]);
+
+            // If the module doesn't import or export anything,
+            // then just return null.
+            if ((imports != null) && (imports.length > 0))
+            {
+                for (int i = 0; i < imports.length; i++)
+                {
+                    // Only check when the package of the resource is
+                    // the same as the import package.
+                    if (imports[i][IDENTIFIER_IDX].equals(pkgName))
+                    {
+                        Module resolvingModule = (Module) imports[i][RESOLVING_MODULE_IDX];
+                        try {
+                            URL url =
+                                resolvingModule.getClassLoader().getResourceFromModule(name);
+                            if (url != null)
+                            {
+                                return url;
+                            }
+                        } catch (Throwable th) {
+                        }
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private Map m_validateMap = new HashMap();
+    private Module m_rootModule = null;
+
+    /**
+     * This method validates the specified target module. If the module
+     * is already validated, then this method returns immediately. This
+     * method synchronizes on the associated module manager to ensure that
+     * modules are not added or removed while the validation is occuring.
+     * Each import and export for the target module are resolved by first
+     * using the compatibility policy to create a list of candidate export
+     * modules, then using the selection policy to choose among the
+     * candidates. Each selected candidate is also recursively validated;
+     * this process validates a transitive closure of all dependent modules.
+     * After the selected candidate is validated, its propagated imports
+     * are checked to make sure that they do not conflict with any existing
+     * propagated imports. If no validation errors occur, then all dependent
+     * modules are marked as validated, if they are not already validated.
+     * If an error occurs, the valid state of all modules remains unchanged.
+     * @param module the module to validate.
+     * @throws org.apache.felix.moduleloader.search.ValidationException if
+     *         the module or any dependent modules could not be validated.
+    **/
+    public void validate(Module module)
+        throws ValidationException
+    {
+        if (getValidAttribute(module).booleanValue())
+        {
+            return;
+        }
+
+        // Flag to indicate whether the bundle is valid or not.
+        boolean isValid = true;
+
+        // This list will be used to remember which bundles
+        // were validated so that the validation events can
+        // be fired outside of the synchronized block.
+        List fireValidatedList = null;
+
+        // Will hold the exception to be thrown or rethrown.
+        ValidationException invalidException = null;
+
+        // Synchronize on the module manager, because we don't want
+        // anything to change while we are in the middle of this
+        // operation.
+        synchronized (m_mgr)
+        {
+            // If we are already validating this module, then
+            // just return; this is necessary for cycles.
+            if (m_validateMap.get(module) != null)
+            {
+                return;
+            }
+
+            // Add the module to the validation map; this
+            // is necessary for cycles.
+            m_validateMap.put(module, module);
+
+            // Keep track of the root module that started
+            // the validation request; this is necessary
+            // for cycles.
+            if (m_rootModule == null)
+            {
+                m_rootModule = module;
+            }
+
+            // Now perform the validation algorithm.
+            Map propagateMap = new HashMap();
+  
+            // Validation binds the module's imports to a specific exporting
+            // module. A module also implicitly imports whatever it exports,
+            // so exports are validated in the same fashion as imports. It
+            // is possible, given the selection policy that a given export
+            // may actually be satisfied by a different module (i.e., a
+            // module is not guaranteed to be bound to what it exports). Since
+            // the imports and exports meta-data are validated in the same
+            // fashion, we will use the follow attribute array to loop and
+            // validate both imports and exports using the same code.
+            for (int attrIdx = 0; (isValid) && (attrIdx < m_searchAttrs.length); attrIdx++)
+            {
+                // Get the imports (exports are treated as imports to)
+                // for the current module.
+                Object[][] imports = getImportsOrExports(module, m_searchAttrs[attrIdx]);
+                // See if each import has available exporters.
+                for (int impIdx = 0; impIdx < imports.length; impIdx++)
+                {
+                    // Get all exporter candidates.
+                    Module[] candidates =
+                        getCompatibleModules(
+                            imports[impIdx][IDENTIFIER_IDX], imports[impIdx][VERSION_IDX]);
+                    // If there are no candidates, then prepare a
+                    // validation exception.
+                    if (candidates == null)
+                    {
+                        isValid = false;
+                        invalidException =
+                            new ValidationException(
+                                "Unable to validate module",
+                                module,
+                                imports[impIdx][IDENTIFIER_IDX],
+                                imports[impIdx][VERSION_IDX],
+                                false);
+                        break;
+                    }
+
+                    // Use selection policy to choose a single export candidate.
+                    Module exportingModule = m_selectPolicy.select(
+                        module, imports[impIdx][IDENTIFIER_IDX],
+                        imports[impIdx][VERSION_IDX], candidates, m_compatPolicy);
+                    // If there is no export module chosen, then prepare
+                    // a validation exception.
+                    if (exportingModule == null)
+                    {
+                        isValid = false;
+                        invalidException =
+                            new ValidationException(
+                                "Unable to validate module",
+                                module,
+                                imports[impIdx][IDENTIFIER_IDX],
+                                imports[impIdx][VERSION_IDX],
+                                false);
+                        break;
+                    }
+
+                    // Make sure that the export module is
+                    // also validated.
+                    try
+                    {
+                        validate(exportingModule);
+                    }
+                    catch (ValidationException ex)
+                    {
+                        // Prepare to rethrow the exception if
+                        // the exporter could not be validated.
+                        isValid = false;
+                        invalidException = ex;
+                        break;
+                    }
+
+                    // Keep track of all propagations from each module that this
+                    // module imports from. Verify that any given import always
+                    // comes form the same export module, otherwise there will be
+                    // class cast exceptions.
+                    Object[] propagates = getPropagatesAttribute(exportingModule);
+                    for (int propIdx = 0; propIdx < propagates.length; propIdx++)
+                    {
+                        // If the module does not import the propagated target,
+                        // then it can be safely ignored.
+                        if (doesImport(module, propagates[propIdx]))
+                        {
+                            Module sourceModule =
+                                (Module) propagateMap.get(propagates[propIdx]);
+
+                            // If the propagation source module has not already been
+                            // found, then remember the resolving module of the
+                            // exporting module as the source of the propagated
+                            // target.
+                            if (sourceModule == null)
+                            {
+                                propagateMap.put(
+                                    propagates[propIdx],
+                                    getImportResolvingModule(
+                                        exportingModule, propagates[propIdx]));
+                            }
+                            // If the propagation source module is found, then check to
+                            // see if it is propagating the import target from the same
+                            // module as previously determined for this module. If not,
+                            // then this is a propagation conflict.
+                            else if (sourceModule !=
+                                getImportResolvingModule(
+                                    exportingModule, propagates[propIdx]))
+                            {
+                                isValid = false;
+                                invalidException =
+                                    new ValidationException(
+                                        "Unable to validate module",
+                                        exportingModule,
+                                        propagates[propIdx],
+                                        null,
+                                        true);
+                                break;
+                            }
+                        }
+                    }
+
+                    // Set the chosen exporting module for the module
+                    // being validated.
+                    imports[impIdx][RESOLVING_MODULE_IDX] = exportingModule;
+                }
+            }
+
+            // Since this method is recursive, check to see it we are
+            // back at the root module that started the request, which
+            // would indicate that the request is finished.
+            if (m_rootModule == module)
+            {
+                // If the result is valid, then we have validated successfully.
+                if (isValid)
+                {
+                    // Loop through all modules in validate map
+                    // and mark them as valid.
+                    Iterator iter = m_validateMap.keySet().iterator();
+                    while (iter.hasNext())
+                    {
+                        Module m = (Module) iter.next();
+                        if (!getValidAttribute(m).booleanValue())
+                        {
+                            m.setAttribute(VALID_ATTR, Boolean.TRUE);
+                            if (fireValidatedList == null)
+                            {
+                                fireValidatedList = new ArrayList();
+                            }
+                            fireValidatedList.add(m);
+                        }
+                    }
+                }
+                // If we are here, then the validate failed, so we
+                // need to reset any partially validated modules.
+                else
+                {
+                    Iterator iter = m_validateMap.keySet().iterator();
+                    while (iter.hasNext())
+                    {
+                        Module m = (Module) iter.next();
+                        invalidate(
+                            m,
+                            m.getAttributes(),
+                            m.getResourceSources(),
+                            m.getLibrarySources());
+                    }
+                }
+
+                // Clear the root module and validation map
+                // before leaving the synchronized block.
+                m_rootModule = null;
+                m_validateMap.clear();
+            }
+        }
+
+        // (Re)throw the exception if invalid, otherwise
+        // fire validation events if the validated event
+        // list is not null.
+        if (!isValid)
+        {
+            throw invalidException;
+        }
+        else if (fireValidatedList != null)
+        {
+            for (int i = 0; i < fireValidatedList.size(); i++)
+            {
+                fireModuleValidated((Module) fireValidatedList.get(i));
+            }
+        }
+    }
+
+    /**
+     * This method returns a list of modules that have an export
+     * that is compatible with the given import identifier and version.
+     * @param identifier the import identifier.
+     * @param version the version of the import identifier.
+     * @return an array of modules that have compatible exports or <tt>null</tt>
+     *         if none are found.
+    **/
+    protected Module[] getCompatibleModules(Object identifier, Object version)
+    {
+        List list = null;
+        Module[] modules = m_mgr.getModules();
+        for (int modIdx = 0; modIdx < modules.length; modIdx++)
+        {
+            Object[][] exports = getExportsAttribute(modules[modIdx]);
+            for (int expIdx = 0; expIdx < exports.length; expIdx++)
+            {
+                // If the identifiers are comparable and compatible,
+                // then add the export identifier to the list.
+                if (m_compatPolicy.isCompatible(
+                        exports[expIdx][IDENTIFIER_IDX], exports[expIdx][VERSION_IDX],
+                        identifier, version))
+                {
+                    if (list == null)
+                    {
+                        list = new ArrayList();
+                    }
+                    list.add(modules[modIdx]);
+                }
+            }
+        }
+
+        if (list == null)
+        {
+            return null;
+        }
+
+        Module[] result = new Module[list.size()];
+        return (Module[]) list.toArray(result);
+    }
+
+    /**
+     * Invalidates a module by flushing its class loader and
+     * re-initializing its meta-data values.
+     * @param module the module to be invalidated.
+     * @param attributes the attributes associated with the module, since they
+     *        might have changed.
+     * @param resSources the resource sources associated wih the module, since they
+     *        might have changed.
+     * @param libSources the library sources associated wih the module, since they
+     *        might have changed.
+    **/
+    public void invalidate(
+        Module module, Object[][] attributes,
+        ResourceSource[] resSources, LibrarySource[] libSources)
+    {
+        // Synchronize on the module manager, because we don't want
+        // anything to change while we are in the middle of this
+        // operation.
+        synchronized (m_mgr)
+        {
+            m_mgr.resetModule(module, attributes, resSources, libSources);
+        }
+
+        // Fire invalidation event if necessary.
+        fireModuleInvalidated(m_mgr.getModule(module.getId()));
+    }
+
+    //
+    // Event handling methods for validation events.
+    //
+
+    /**
+     * Adds a validation listener to this import search policy. Validation
+     * listeners are notified when a module is validated and/or invalidated
+     * by the search policy.
+     * @param l the validation listener to add.
+    **/
+    public void addValidationListener(ValidationListener l)
+    {
+        // Verify listener.
+        if (l == null)
+        {
+            throw new IllegalArgumentException("Listener is null");
+        }
+
+        // Use the m_noListeners object as a lock.
+        synchronized (m_noListeners)
+        {
+            // If we have no listeners, then just add the new listener.
+            if (m_listeners == m_noListeners)
+            {
+                m_listeners = new ValidationListener[] { l };
+            }
+            // Otherwise, we need to do some array copying.
+            // Notice, the old array is always valid, so if
+            // the dispatch thread is in the middle of a dispatch,
+            // then it has a reference to the old listener array
+            // and is not affected by the new value.
+            else
+            {
+                ValidationListener[] newList = new ValidationListener[m_listeners.length + 1];
+                System.arraycopy(m_listeners, 0, newList, 0, m_listeners.length);
+                newList[m_listeners.length] = l;
+                m_listeners = newList;
+            }
+        }
+    }
+
+    /**
+     * Removes a validation listener to this import search policy.
+     * @param l the validation listener to remove.
+    **/
+    public void removeValidationListener(ValidationListener l)
+    {
+        // Verify listener.
+        if (l == null)
+        {
+            throw new IllegalArgumentException("Listener is null");
+        }
+
+        // Use the m_noListeners object as a lock.
+        synchronized (m_noListeners)
+        {
+            // Try to find the instance in our list.
+            int idx = -1;
+            for (int i = 0; i < m_listeners.length; i++)
+            {
+                if (m_listeners[i].equals(l))
+                {
+                    idx = i;
+                    break;
+                }
+            }
+
+            // If we have the instance, then remove it.
+            if (idx >= 0)
+            {
+                // If this is the last listener, then point to empty list.
+                if (m_listeners.length == 1)
+                {
+                    m_listeners = m_noListeners;
+                }
+                // Otherwise, we need to do some array copying.
+                // Notice, the old array is always valid, so if
+                // the dispatch thread is in the middle of a dispatch,
+                // then it has a reference to the old listener array
+                // and is not affected by the new value.
+                else
+                {
+                    ValidationListener[] newList = new ValidationListener[m_listeners.length - 1];
+                    System.arraycopy(m_listeners, 0, newList, 0, idx);
+                    if (idx < newList.length)
+                    {
+                        System.arraycopy(m_listeners, idx + 1, newList, idx,
+                            newList.length - idx);
+                    }
+                    m_listeners = newList;
+                }
+            }
+        }
+    }
+
+    /**
+     * Fires a validation event for the specified module.
+     * @param module the module that was validated.
+    **/
+    protected void fireModuleValidated(Module module)
+    {
+        // Event holder.
+        ModuleEvent event = null;
+
+        // Get a copy of the listener array, which is guaranteed
+        // to not be null.
+        ValidationListener[] listeners = m_listeners;
+
+        // Loop through listeners and fire events.
+        for (int i = 0; i < listeners.length; i++)
+        {
+            // Lazily create event.
+            if (event == null)
+            {
+                event = new ModuleEvent(m_mgr, module);
+            }
+            listeners[i].moduleValidated(event);
+        }
+    }
+
+    /**
+     * Fires an invalidation event for the specified module.
+     * @param module the module that was invalidated.
+    **/
+    protected void fireModuleInvalidated(Module module)
+    {
+        // Event holder.
+        ModuleEvent event = null;
+
+        // Get a copy of the listener array, which is guaranteed
+        // to not be null.
+        ValidationListener[] listeners = m_listeners;
+
+        // Loop through listeners and fire events.
+        for (int i = 0; i < listeners.length; i++)
+        {
+            // Lazily create event.
+            if (event == null)
+            {
+                event = new ModuleEvent(m_mgr, module);
+            }
+            listeners[i].moduleInvalidated(event);
+        }
+    }
+
+    //
+    // ModuleListener methods.
+    //
+
+    /**
+     * Callback method for <tt>ModuleListener</tt>; this should not
+     * be called directly. This callback is used to initialize module
+     * meta-data attributes; it adds the <tt>VALID_ATTR</tt> attribute
+     * and initializes the resolving module entries in <tt>EXPORTS_ATTR</tt>
+     * and <tt>IMPORTS_ATTR</tt> to <tt>null</tt>.
+    **/
+    public void moduleAdded(ModuleEvent event)
+    {
+        synchronized (event.getModule())
+        {
+            // Add valid attribute to all modules.
+            event.getModule().setAttribute(VALID_ATTR, Boolean.FALSE);
+
+            for (int attrIdx = 0; attrIdx < m_searchAttrs.length; attrIdx++)
+            {
+                Object[][] imports =
+                    getImportsOrExports(event.getModule(), m_searchAttrs[attrIdx]);
+                for (int i = 0; i < imports.length; i++)
+                {
+                    imports[i][RESOLVING_MODULE_IDX] = null;
+                }
+            }
+        }
+    }
+
+    /**
+     * Callback method for <tt>ModuleListener</tt>; this should not
+     * be called directly. This callback is used to re-initialize module
+     * meta-data attributes; it adds the <tt>VALID_ATTR</tt> attribute
+     * and initializes the resolving module entries in <tt>EXPORTS_ATTR</tt>
+     * and <tt>IMPORTS_ATTR</tt> to <tt>null</tt>. It then invalidates
+     * all modules that import from the reset module.
+    **/
+    public void moduleReset(ModuleEvent event)
+    {
+        // This will reset module meta-data.
+        moduleAdded(event);
+
+// TODO: Synchronization?
+        ModuleManager m_mgr = (ModuleManager) event.getSource();
+        List list = createImporterList(m_mgr, event.getModule());
+        for (int i = 0; (list != null) && (i < list.size()); i++)
+        {
+            Module module = (Module) list.get(i);
+            invalidate(
+                module, module.getAttributes(),
+                module.getResourceSources(), module.getLibrarySources());
+        }
+    }
+
+    /**
+     * Callback method for <tt>ModuleListener</tt>; this should not
+     * be called directly. Used to listen for module removal events
+     * in order to invalidate all the modules that import form the
+     * removed moduled.
+    **/
+    public void moduleRemoved(ModuleEvent event)
+    {
+// TODO: Synchronization?
+        ModuleManager m_mgr = (ModuleManager) event.getSource();
+        List list = createImporterList(m_mgr, event.getModule());
+        for (int i = 0; (list != null) && (i < list.size()); i++)
+        {
+            Module module = (Module) list.get(i);
+            invalidate(
+                module, module.getAttributes(),
+                module.getResourceSources(), module.getLibrarySources());
+        }
+    }
+
+    //
+    // Instance utility methods.
+    //
+
+    /**
+     * This utility method returns the module that exports the
+     * specified import identifier and version. This method uses the
+     * <tt>validate()</tt> method to find the exporting module and,
+     * as a result, relies on the compatibility and selection
+     * policies associated with this <tt>ImportSearchPolicy</tt>
+     * instance. If successful, the returned module is guaranteed
+     * to be validated. This method only needs to be used for more
+     * advanced purposes (i.e., check import availability dynamically,
+     * etc.) and need not be used under normal circumstances.
+     * @param identifier the identifier of the import to resolve.
+     * @param version the version of the import to resolve.
+     * @return the exporting module selected to resolve the specified
+     *         import target.
+    **/
+    public Module resolveImportTarget(Object identifier, Object version)
+    {
+        // Create a fake module that imports the specified target
+        // and then try to validate it so we can get the exporting
+        // module that is used to satisfy the import.
+        Object[] targetImport = { identifier, version, null };
+        Object[][] attrs = new Object[][] {
+            new Object[] { EXPORTS_ATTR, new Object[0][0] },
+            new Object[] { IMPORTS_ATTR, new Object[][] { targetImport } },
+            new Object[] { PROPAGATES_ATTR, new Object[0] },
+            new Object[] { VALID_ATTR, Boolean.FALSE}
+        };
+        Module fake = new Module(m_mgr, "resolve import", attrs, null, null, false);
+        try {
+            validate(fake);
+        } catch (ValidationException ex) {
+            // Ignore this.
+        }
+        return (Module) targetImport[RESOLVING_MODULE_IDX];
+    }
+
+    //
+    // Static utility methods.
+    //
+
+    private static final Object[][] m_emptyImports = new Object[0][0];
+    private static final Object[] m_emptyProp = new Object[0];
+
+    /**
+     * Utility method that returns the <tt>VALID_ATTR</tt> attribute for
+     * the specified module.
+     * @param module the module whose <tt>VALID_ATTR</tt> attribute is to
+     *        be retrieved.
+     * @return an instance of <tt>Boolean</tt>.
+    **/
+    public static Boolean getValidAttribute(Module module)
+    {
+        Object value = module.getAttribute(VALID_ATTR);
+        if (value != null)
+        {
+            return (Boolean) value;
+        }
+        return Boolean.FALSE;
+    }
+
+    /**
+     * Utility method that returns the <tt>IMPORTS_ATTR</tt> attribute for
+     * the specified module.
+     * @param module the module whose <tt>IMPORTS_ATTR</tt> attribute is to
+     *        be retrieved.
+     * @return an <tt>Object[][]</tt> value or <tt>null</tt>.
+    **/
+    public static Object[][] getImportsAttribute(Module module)
+    {
+        Object value = module.getAttribute(IMPORTS_ATTR);
+        if (value != null)
+        {
+            return (Object[][]) value;
+        }
+        return m_emptyImports;
+    }
+
+    /**
+     * Utility method that returns the <tt>EXPORTS_ATTR</tt> attribute for
+     * the specified module.
+     * @param module the module whose <tt>EXPORTS_ATTR</tt> attribute is to
+     *        be retrieved.
+     * @return an <tt>Object[][]</tt> value or <tt>null</tt>.
+    **/
+    public static Object[][] getExportsAttribute(Module module)
+    {
+        Object value = module.getAttribute(EXPORTS_ATTR);
+        if (value != null)
+        {
+            return (Object[][]) value;
+        }
+        return m_emptyImports;
+    }
+
+    /**
+     * Utility method that returns the <tt>IMPORTS_ATTR</tt> or the
+     * <tt>EXPORTS_ATTR</tt> attribute for the specified module.
+     * @param module the module whose <tt>IMPORTS_ATTR</tt> or
+     *        <tt>EXPORTS_ATTR</tt> attribute is to be retrieved.
+     * @param name either <tt>IMPORTS_ATTR</tt> or <tt>EXPORTS_ATTR</tt>
+     *        depending on which attribute should be retrieved.
+     * @return an <tt>Object[][]</tt> value or <tt>null</tt>.
+    **/
+    public static Object[][] getImportsOrExports(Module module, String name)
+    {
+        Object value = module.getAttribute(name);
+        if (value != null)
+        {
+            return (Object[][]) value;
+        }
+        return m_emptyImports;
+    }
+
+    /**
+     * Utility method that returns the <tt>PROPAGATES_ATTR</tt> attribute for
+     * the specified module.
+     * @param module the module whose <tt>PROPAGATES_ATTR</tt> attribute is to
+     *        be retrieved.
+     * @return an <tt>Object[]</tt> value or <tt>null</tt>.
+    **/
+    public static Object[] getPropagatesAttribute(Module module)
+    {
+        Object value = module.getAttribute(PROPAGATES_ATTR);
+        if (value != null)
+        {
+            return (Object[]) value;
+        }
+        return m_emptyProp;
+    }
+
+    /**
+     * Utility method to determine if the specified module imports a given
+     * import identifier, regardless of version. This method checks both
+     * imports and exports, since a module is assumed to import what it exports.
+     * @param module the module to check.
+     * @param identifier the import identifier to check.
+     * @return <tt>true</tt> if the module imports the specified
+     *         import identifier or <tt>false</tt> if it does not.
+    **/
+    public static boolean doesImport(Module module, Object identifier)
+    {
+        Object[][] imports = getImportsAttribute(module);
+        for (int i = 0; i < imports.length; i++)
+        {
+            if (imports[i][IDENTIFIER_IDX].equals(identifier))
+            {
+                return true;
+            }
+        }
+        imports = getExportsAttribute(module);
+        for (int i = 0; i < imports.length; i++)
+        {
+            if (imports[i][IDENTIFIER_IDX].equals(identifier))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Utility method to create a list of modules that import from
+     * the specified module.
+     * @param mgr the module manager that contains the module.
+     * @param module the module for which to create an importer list.
+     * @return a list of modules that import from the specified module
+     *         or <tt>null</tt>.
+    **/
+    public static List createImporterList(ModuleManager mgr, Module module)
+    {
+        List list = null;
+        Module[] modules = mgr.getModules();
+        for (int modIdx = 0; modIdx < modules.length; modIdx++)
+        {
+            Object[][] imports = getImportsAttribute(modules[modIdx]);
+            for (int impIdx = 0; impIdx < imports.length; impIdx++)
+            {
+                if (imports[impIdx][RESOLVING_MODULE_IDX] == module)
+                {
+                    if (list == null)
+                    {
+                        list = new ArrayList();
+                    }
+                    list.add(modules[modIdx]);
+                    break;
+                }
+            }
+        }
+
+        return list;
+    }
+
+    /**
+     * Utility method to get the import version number associated with a specific
+     * import identifier of the specified module.
+     * @param module the module to investigate.
+     * @param identifier the import identifier for which the version should
+     *        be retrieved.
+     * @return the version number object or <tt>null</tt>.
+    **/
+    public static Object getImportVersion(Module module, Object identifier)
+    {
+        Object[][] imports = getImportsAttribute(module);
+        for (int i = 0; i < imports.length; i++)
+        {
+            if (imports[i][IDENTIFIER_IDX].equals(identifier))
+            {
+                return imports[i][VERSION_IDX];
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Utility method to get the export version number associated with a specific
+     * export identifier of the specified module.
+     * @param module the module to investigate.
+     * @param identifier the export identifier for which the version should
+     *        be retrieved.
+     * @return the version number object or <tt>null</tt>.
+    **/
+    public static Object getExportVersion(Module module, Object identifier)
+    {
+        Object[][] exports = getExportsAttribute(module);
+        for (int i = 0; i < exports.length; i++)
+        {
+            if (exports[i][IDENTIFIER_IDX].equals(identifier))
+            {
+                return exports[i][VERSION_IDX];
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Utility method to get the resolving module of a specific import
+     * identifier for the specified module.
+     * @param module the module to investigate.
+     * @param identifier the import identifier for which the resolving
+     *        module should be retrieved.
+     * @return the resolving module or <tt>null</tt>.
+    **/
+    public static Module getImportResolvingModule(Module module, Object identifier)
+    {
+        Object[][] imports = getImportsAttribute(module);
+
+        for (int i = 0; i < imports.length; i++)
+        {
+            if (imports[i][IDENTIFIER_IDX].equals(identifier))
+            {
+                return (Module) imports[i][RESOLVING_MODULE_IDX];
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Utility method to get the resolving module of a specific export
+     * identifier for the specified module.
+     * @param module the module to investigate.
+     * @param identifier the export identifier for which the resolving
+     *        module should be retrieved.
+     * @return the resolving module or <tt>null</tt>.
+    **/
+    public static Module getExportResolvingModule(Module module, Object identifier)
+    {
+        Object[][] exports = getExportsAttribute(module);
+
+        for (int i = 0; i < exports.length; i++)
+        {
+            if (exports[i][IDENTIFIER_IDX].equals(identifier))
+            {
+                return (Module) exports[i][RESOLVING_MODULE_IDX];
+            }
+        }
+
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/ResolveException.java b/src/org/apache/felix/moduleloader/search/ResolveException.java
new file mode 100755
index 0000000..abc342c
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/ResolveException.java
@@ -0,0 +1,60 @@
+/*
+ *   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.moduleloader.search;
+
+import org.apache.felix.framework.searchpolicy.R4Package;
+import org.apache.felix.moduleloader.Module;
+
+/**
+ * <p>
+ * This exception is thrown if a module cannot be resolved. The module
+ * that failed to be resolved is recorded, along with the failed import target
+ * identifier and version number. If the error was a result of a propagation
+ * conflict, then the propagation error flag is set.
+ * </p>
+ * @see org.apache.felix.moduleloader.search.ImportSearchPolicy#validate(org.apache.felix.moduleloader.Module)
+**/
+public class ResolveException extends Exception
+{
+    private Module m_module = null;
+    private R4Package m_pkg = null;
+
+    /**
+     * Constructs an exception with the specified message, module,
+     * import identifier, import version number, and propagation flag.
+    **/
+    public ResolveException(String msg, Module module, R4Package pkg)
+    {
+        super(msg);
+        m_module = module;
+        m_pkg = pkg;
+    }
+
+    /**
+     * Returns the module that was being resolved.
+     * @return the module that was being resolved.
+    **/
+    public Module getModule()
+    {
+        return m_module;
+    }
+
+    public R4Package getPackage()
+    {
+        return m_pkg;
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/ResolveListener.java b/src/org/apache/felix/moduleloader/search/ResolveListener.java
new file mode 100755
index 0000000..a502021
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/ResolveListener.java
@@ -0,0 +1,46 @@
+/*
+ *   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.moduleloader.search;
+
+import java.util.EventListener;
+
+import org.apache.felix.moduleloader.ModuleEvent;
+
+/**
+ * <p>
+ * This is an event listener interface for listening to resolution
+ * events that are generated by the <tt>R4SearchPolicy</tt>. Events
+ * are fired when a module is resolved and when it is unresolved.
+ * </p>
+ * @see org.apache.felix.framework.searchpolicy.R4SearchPolicy
+**/
+public interface ResolveListener extends EventListener
+{
+    /**
+     * This is an event callback method that indicates that
+     * a module was resolved.
+     * @param event the module event containing the event data.
+    **/
+    public void moduleResolved(ModuleEvent event);
+
+    /**
+     * This is an event callback method that indicates that
+     * a module was unresolved.
+     * @param event the module event containing the event data.
+    **/
+    public void moduleUnresolved(ModuleEvent event);
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/SelectionPolicy.java b/src/org/apache/felix/moduleloader/search/SelectionPolicy.java
new file mode 100644
index 0000000..2436bce
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/SelectionPolicy.java
@@ -0,0 +1,47 @@
+/*
+ *   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.moduleloader.search;
+
+import org.apache.felix.moduleloader.Module;
+
+/**
+ * <p>
+ * This interface represents the policy for selecting a specific export
+ * target from multiple <i>compatible</i> candidate export targets when
+ * the <tt>ImportSearchPolicy</tt> is trying to resolve an import target
+ * for a given module. A concrete implementation of this interface is
+ * required to create an instance of <tt>ImportSearchPolicy</tt>.
+ * </p>
+ * @see org.apache.felix.moduleloader.search.ImportSearchPolicy
+**/
+public interface SelectionPolicy
+{
+    /**
+     * Selects a single module to resolve the specified import
+     * from the array of compatible candidate modules.
+     * @param module the module that is importing the target.
+     * @param identifier the identifier of the import target.
+     * @param version the version number of the import target.
+     * @param candidates array of compatible candidate modules from which to choose.
+     * @param compatPolicy the compatibility policy that is being used.
+     * @return the selected module or <tt>null</tt> if no module
+     *         can be selected.
+    **/
+    public Module select(
+        Module module, Object identifier, Object version, Module[] candidates,
+        CompatibilityPolicy compatPolicy);
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/SelfContainedSearchPolicy.java b/src/org/apache/felix/moduleloader/search/SelfContainedSearchPolicy.java
new file mode 100644
index 0000000..e2608c6
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/SelfContainedSearchPolicy.java
@@ -0,0 +1,122 @@
+/*
+ *   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.moduleloader.search;
+
+import java.net.URL;
+
+import org.apache.felix.moduleloader.*;
+
+/**
+ * <p>
+ * This class implements a <tt>ModuleLoader</tt> search policy that
+ * assumes that all modules are self-contained. In other words, when
+ * loading a class or resource for a particular module, only that
+ * particular module's resource sources are search. No classes or
+ * resources are shared among modules.
+ * </p>
+ * @see org.apache.felix.moduleloader.SearchPolicy
+ * @see org.apache.felix.moduleloader.Module
+ * @see org.apache.felix.moduleloader.ModuleClassLoader
+ * @see org.apache.felix.moduleloader.ModuleManager
+**/
+public class SelfContainedSearchPolicy implements SearchPolicy
+{
+    private ModuleManager m_mgr = null;
+
+    /**
+     * This method is part of the <tt>SearchPolicy</tt> interface.
+     * This method is called by the <tt>ModuleManager</tt> once to
+     * give the search policy instance a reference to its associated
+     * module manager. This method should be implemented such that
+     * it cannot be called twice; calling this method a second time
+     * should produce an illegal state exception.
+     * @param mgr the module manager associated with this search policy.
+     * @throws java.lang.IllegalStateException if the method is called
+     *         more than once.
+    **/
+    public void setModuleManager(ModuleManager mgr)
+        throws IllegalStateException
+    {
+        if (m_mgr == null)
+        {
+            m_mgr = mgr;
+        }
+        else
+        {
+            throw new IllegalStateException("Module manager is already initialized");
+        }
+    }
+
+    public Object[] definePackage(Module module, String pkgName)
+    {
+        return null;
+    }
+
+    /**
+     * Simply returns <tt>null</tt> which forces the module class
+     * loader to only search the target module's resource sources
+     * for the specified class.
+     * @param parent the parent class loader of the delegating class loader.
+     * @param module the target module that is loading the class.
+     * @param name the name of the class being loaded.
+     * @return <tt>null</tt>.
+    **/
+    public Class findClassBeforeModule(ClassLoader parent, Module module, String name)
+    {
+        // First, try to load from parent.
+        if (parent != null)
+        {
+            try
+            {
+                Class c = parent.loadClass(name);
+                if (c != null)
+                {
+                    return c;
+                }
+            }
+            catch (ClassNotFoundException ex)
+            {
+                // Ignore.
+            }
+        }
+
+        return null;
+    }
+
+    public Class findClassAfterModule(ClassLoader parent, Module module, String name)
+    {
+        return null;
+    }
+
+    /**
+     * Simply returns <tt>null</tt> which forces the module class
+     * loader to only search the target module's resource sources
+     * for the specified resource.
+     * @param parent the parent class loader of the delegating class loader.
+     * @param module the target module that is loading the class.
+     * @param name the name of the resource being loaded.
+     * @return <tt>null</tt>.
+    **/
+    public URL findResource(ClassLoader parent, Module module, String name)
+    {
+        if (parent != null)
+        {
+            return parent.getResource(name);
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/ValidationException.java b/src/org/apache/felix/moduleloader/search/ValidationException.java
new file mode 100644
index 0000000..b996454
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/ValidationException.java
@@ -0,0 +1,88 @@
+/*
+ *   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.moduleloader.search;
+
+import org.apache.felix.moduleloader.Module;
+
+/**
+ * <p>
+ * This exception is thrown if a module cannot be validated. The module
+ * that failed to be validated is recorded, along with the failed import target
+ * identifier and version number. If the error was a result of a propagation
+ * conflict, then the propagation error flag is set.
+ * </p>
+ * @see org.apache.felix.moduleloader.search.ImportSearchPolicy#validate(org.apache.felix.moduleloader.Module)
+**/
+public class ValidationException extends Exception
+{
+    private Module m_module = null;
+    private Object m_identifier = null;
+    private Object m_version = null;
+    private boolean m_isPropagation = false;
+
+    /**
+     * Constructs an exception with the specified message, module,
+     * import identifier, import version number, and propagation flag.
+    **/
+    public ValidationException(String msg, Module module,
+        Object identifier, Object version, boolean isPropagation)
+    {
+        super(msg);
+        m_module = module;
+        m_identifier = identifier;
+        m_version = version;
+        m_isPropagation = isPropagation;
+    }
+
+    /**
+     * Returns the module that was being validated.
+     * @return the module that was being validated.
+    **/
+    public Module getModule()
+    {
+        return m_module;
+    }
+
+    /**
+     * Returns the identifier of the import target that could not be resolved.
+     * @return the identifier of the import target that could not be resolved.
+    **/
+    public Object getIdentifier()
+    {
+        return m_identifier;
+    }
+
+    /**
+     * Returns the version number of the import target that could not be resolved.
+     * @return the version number of the import target that could not be resolved.
+    **/
+    public Object getVersion()
+    {
+        return m_version;
+    }
+
+    /**
+     * Returns a flag indicating whether the exception was caused by a
+     * a propagation conflict.
+     * @return <tt>true</tt> if the exception was thrown due to a propagation
+     *         conflict, <tt>false</tt> otherwise.
+    **/
+    public boolean isPropagationError()
+    {
+        return m_isPropagation;
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/ValidationListener.java b/src/org/apache/felix/moduleloader/search/ValidationListener.java
new file mode 100644
index 0000000..9491531
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/ValidationListener.java
@@ -0,0 +1,46 @@
+/*
+ *   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.moduleloader.search;
+
+import java.util.EventListener;
+
+import org.apache.felix.moduleloader.ModuleEvent;
+
+/**
+ * <p>
+ * This is an event listener interface for listening to validation
+ * events that are generated by the <tt>ImportSearchPolicy</tt>. Events
+ * are fired when a module is validated and when it is invalidated.
+ * </p>
+ * @see org.apache.felix.moduleloader.search.ImportSearchPolicy
+**/
+public interface ValidationListener extends EventListener
+{
+    /**
+     * This is an event callback method that indicates that
+     * a module was validated.
+     * @param event the module event containing the event data.
+    **/
+    public void moduleValidated(ModuleEvent event);
+
+    /**
+     * This is an event callback method that indicates that
+     * a module was invalidated.
+     * @param event the module event containing the event data.
+    **/
+    public void moduleInvalidated(ModuleEvent event);
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/compatibility/ExactCompatibilityPolicy.java b/src/org/apache/felix/moduleloader/search/compatibility/ExactCompatibilityPolicy.java
new file mode 100644
index 0000000..f971683
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/compatibility/ExactCompatibilityPolicy.java
@@ -0,0 +1,72 @@
+/*
+ *   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.moduleloader.search.compatibility;
+
+import org.apache.felix.moduleloader.search.CompatibilityPolicy;
+
+/**
+ * This class implements a simple version numbering compatibility policy for the
+ * <tt>ImportSearchPolicy</tt> where only exact version numbers are considered
+ * to be compatible.  This policy simply returns the result of
+ * "<tt>leftId.equals(rightId) && leftVersion.equals(rightVersion)</tt>". Any
+ * calls to the <tt>compare()</tt> method result in an exception since this
+ * policy has no basis for comparing identifiers and versions.
+ * @see org.apache.felix.moduleloader.search.CompatibilityPolicy
+ * @see org.apache.felix.moduleloader.search.ImportSearchPolicy
+**/
+public class ExactCompatibilityPolicy implements CompatibilityPolicy
+{
+    /**
+     * Compares two versioned identifiers, but since this policy has
+     * no understanding of how to compare identifiers, it always throws
+     * an <tt>IllegalArgumentException</tt>.
+     * @param leftId the identifier to test for compatibility.
+     * @param leftVersion the version number to test for compatibility.
+     * @param rightId the identifier used as the compatibility base line.
+     * @param rightVersion the version used as the compatibility base line.
+     * @return <tt>0</tt> if the identifiers are equal, <tt>-1</tt> if the
+     *         left identifier is less then the right identifier, and <tt>1</tt>
+     *         if the left identifier is greater than the right identifier.
+     * @throws java.lang.IllegalArgumentException if the two identifiers
+     *         are not comparable, i.e., they refer to completely different
+     *         entities.
+    **/
+    public int compare(
+        Object leftId, Object leftVersion,
+        Object rightId, Object rightVersion)
+    {
+        throw new IllegalArgumentException("Identifiers are not comparable.");
+    }
+
+    /**
+     * Returns whether the first import/export target is compatible
+     * with the second. This method simply uses the "<tt>equals()</tt>" method
+     * to test both the identifier and the verison number.
+     * @param leftId the identifier to test for compatibility.
+     * @param leftVersion the version number to test for compatibility.
+     * @param rightId the identifier used as the compatibility base line.
+     * @param rightVersion the version used as the compatibility base line.
+     * @return <tt>true</tt> if the left version number object is compatible
+     *         with the right version number object, otherwise <tt>false</tt>.
+    **/
+    public boolean isCompatible(
+        Object leftId, Object leftVersion,
+        Object rightId, Object rightVersion)
+    {
+        return leftId.equals(rightId) && leftVersion.equals(rightVersion);
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/selection/InteractiveSelectionPolicy.java b/src/org/apache/felix/moduleloader/search/selection/InteractiveSelectionPolicy.java
new file mode 100644
index 0000000..f1ddee7
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/selection/InteractiveSelectionPolicy.java
@@ -0,0 +1,87 @@
+/*
+ *   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.moduleloader.search.selection;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import org.apache.felix.moduleloader.Module;
+import org.apache.felix.moduleloader.search.CompatibilityPolicy;
+import org.apache.felix.moduleloader.search.SelectionPolicy;
+
+/**
+ * This class implements an interactive selection policy for the
+ * <tt>ImportSearchPolicy</tt>. This policy simply uses standard
+ * output to present the list of candidate modules and uses standard
+ * input to allow the user to select a specific module from the
+ * candidates. This selection policy is generally only useful for
+ * debugging purposes.
+ * @see org.apache.felix.moduleloader.search.SelectionPolicy
+ * @see org.apache.felix.moduleloader.search.ImportSearchPolicy
+**/
+public class InteractiveSelectionPolicy implements SelectionPolicy
+{
+    /**
+     * Returns a single package from an array of packages.
+     * @param sources array of packages from which to choose.
+     * @return the selected package or <tt>null</tt> if no package
+     *         can be selected.
+    **/
+    public Module select(Module module, Object target,
+        Object version, Module[] candidates, CompatibilityPolicy compatPolicy)
+    {
+        try {
+            if (candidates.length == 1)
+            {
+                return candidates[0];
+            }
+            // Now start an interactive prompt.
+            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+            do
+            {
+                System.out.println("\nImporting '" + target
+                    + "(" + version + ")" + "' for '" + module + "'.");
+                System.out.println("");
+                for (int i = 0; i < candidates.length; i++)
+                {
+                    System.out.println((i + 1) + ". " + candidates[i]);
+                }
+                System.out.print("Select: ");
+                String s = br.readLine();
+
+                int choice = -1;
+                try {
+                    choice = Integer.parseInt(s);
+                } catch (Exception ex) {
+                }
+
+                if (choice == 0)
+                {
+                    break;
+                }
+                else if ((choice > 0) && (choice <= candidates.length))
+                {
+                    return candidates[choice - 1];
+                }
+            }
+            while (true);
+        } catch (Exception ex) {
+        }
+
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/src/org/apache/felix/moduleloader/search/selection/SimpleSelectionPolicy.java b/src/org/apache/felix/moduleloader/search/selection/SimpleSelectionPolicy.java
new file mode 100644
index 0000000..a2f9648
--- /dev/null
+++ b/src/org/apache/felix/moduleloader/search/selection/SimpleSelectionPolicy.java
@@ -0,0 +1,145 @@
+/*
+ *   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.moduleloader.search.selection;
+
+import java.util.*;
+
+import org.apache.felix.moduleloader.*;
+import org.apache.felix.moduleloader.search.*;
+
+/**
+ * This class implements a reasonably simple selection policy for the
+ * <tt>ImportSearchPolicy</tt>. When given a choice, this selection
+ * policy will always select the newest version of the available
+ * candidates to satisfy the import identifier. In the case where
+ * a candidate has already been selected for a given import identifier,
+ * then the previously selected module will be returned, if possible.
+ * If it is not possible to return the previously selected module, then
+ * a <tt>null</tt> is returned. This policy assumes that classes are
+ * shared globally.
+**/
+public class SimpleSelectionPolicy implements SelectionPolicy, ModuleListener
+{
+    private Map m_resolvedPackageMap = new HashMap();
+    private Map m_resolvedModuleMap = new HashMap();
+
+    /**
+     * Selects a single module to resolve the specified import identifier
+     * from the array of compatible candidate modules. If the import
+     * identifier has not been resolved before, then this selection policy
+     * chooses the module that exports the newest version of the
+     * import identifer. If the import identifier has been resolved already,
+     * then the same module that was chosen before is chosen again.
+     * This ensures that all modules use the same version of all
+     * exported classes.
+     * @param module the module that is importing the target.
+     * @param identifier the identifier of the import target.
+     * @param version the version number of the import target.
+     * @param candidates array of compatible candidate modules from which to choose.
+     * @return the selected module or <tt>null</tt> if no module
+     *         can be selected.
+    **/
+    public synchronized Module select(Module module, Object identifier,
+        Object version, Module[] candidates, CompatibilityPolicy compatPolicy)
+    {
+        // See if package is already resolved.
+        Module selModule = (Module) m_resolvedPackageMap.get(identifier);
+
+        // If no module was previously selected to export the package,
+        // then try to choose one now.
+        if (selModule == null)
+        {
+            Object selVersion = null;
+
+            // Examine all exported instances of the identifier and
+            // choose the one with the newest version number. If
+            // there is more than one source for the newest version,
+            // then just select the first one found.
+            for (int i = 0; i < candidates.length; i++)
+            {
+                Object tmpVersion =
+                    ImportSearchPolicy.getExportVersion(candidates[i], identifier);
+
+                // If this is the first comparison, then
+                // just record it.
+                if (selVersion == null)
+                {
+                    selModule = candidates[i];
+                    selVersion = tmpVersion;
+                }
+                // If the current export package version is greater
+                // than the selected export package version, then
+                // record it instead.
+                else if (compatPolicy.compare(identifier, tmpVersion, identifier, selVersion) >= 0)
+                {
+                    selModule = candidates[i];
+                    selVersion = tmpVersion;
+                }
+            }
+
+            m_resolvedPackageMap.put(identifier, selModule);
+            m_resolvedModuleMap.put(selModule, selModule);
+        }
+        // See if the previously selected export module satisfies
+        // the current request, otherwise return null.
+        else
+        {
+            Object selVersion =
+                ImportSearchPolicy.getExportVersion(selModule, identifier);
+            Module tmpModule = selModule;
+            selModule = null;
+            if (compatPolicy.isCompatible(identifier, selVersion, identifier, version))
+            {
+                selModule = tmpModule;
+            }
+        }
+
+        return selModule;
+    }
+
+    public void moduleAdded(ModuleEvent event)
+    {
+    }
+
+    public void moduleReset(ModuleEvent event)
+    {
+        moduleRemoved(event);
+    }
+
+    public synchronized void moduleRemoved(ModuleEvent event)
+    {
+        // If the module that was removed was chosen for
+        // exporting identifier, then flush it from our
+        // data structures; we assume here that the application
+        // will flush references to the removed module's classes.
+        if (m_resolvedModuleMap.get(event.getModule()) != null)
+        {
+            // Remove from module map.
+            m_resolvedModuleMap.remove(event.getModule());
+            // Remove each exported package from package map.
+            Iterator iter = m_resolvedPackageMap.entrySet().iterator();
+            while (iter.hasNext())
+            {
+                Map.Entry entry = (Map.Entry) iter.next();
+                if (entry.getValue() == event.getModule())
+                {
+                    iter.remove();
+                }
+            }
+        }
+    }
+}
\ No newline at end of file