geronimo-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From a..@apache.org
Subject svn commit: r439496 - in /geronimo/xbean/branches/colossus: ./ xbean-classloader/ xbean-classloader/src/main/java/org/apache/xbean/classloader/ xbean-classloader/src/main/java/org/apache/xbean/server/classloader/ xbean-classloader/src/main/java/org/apa...
Date Fri, 01 Sep 2006 22:47:01 GMT
Author: adc
Date: Fri Sep  1 15:47:00 2006
New Revision: 439496

URL: http://svn.apache.org/viewvc?rev=439496&view=rev
Log:
Refactor classloader

Added:
    geronimo/xbean/branches/colossus/xbean-classloader/
      - copied from r439480, geronimo/xbean/branches/colossus/xbean-server/
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/ClassLoaderUtil.java
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/DestroyableClassLoader.java
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileClassLoader.java
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/MultiParentClassLoader.java
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/NamedClassLoader.java
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/ThreadContextClassLoaderFactoryBean.java
    geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/classloader/
    geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/classloader/JarFileClassLoaderTest.java
    geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/classloader/MultiParentClassLoaderTest.java
    geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/classloader/
      - copied from r439480, geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/classloader/
    geronimo/xbean/branches/colossus/xbean-server/src/test/java/org/apache/xbean/classloader/
      - copied from r439480, geronimo/xbean/branches/colossus/xbean-server/src/test/java/org/apache/xbean/server/classloader/
Removed:
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/server/classloader/
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/server/deployer/
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/server/loader/
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/server/main/
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/server/propertyeditor/
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/server/repository/
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/server/spring/
    geronimo/xbean/branches/colossus/xbean-classloader/src/main/resources/META-INF/xbean-bootstrap.xml
    geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/server/classloader/
    geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/server/deployer/
    geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/server/spring/
    geronimo/xbean/branches/colossus/xbean-classloader/src/test/repository/
    geronimo/xbean/branches/colossus/xbean-classloader/src/test/resources/
    geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/classloader/ClassLoaderUtil.java
    geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/classloader/DestroyableClassLoader.java
    geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/classloader/JarFileClassLoader.java
    geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/classloader/MultiParentClassLoader.java
    geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/classloader/NamedClassLoader.java
    geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/classloader/ThreadContextClassLoaderFactoryBean.java
    geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/classloader/
    geronimo/xbean/branches/colossus/xbean-server/src/test/java/org/apache/xbean/classloader/JarFileClassLoaderTest.java
    geronimo/xbean/branches/colossus/xbean-server/src/test/java/org/apache/xbean/classloader/MultiParentClassLoaderTest.java
    geronimo/xbean/branches/colossus/xbean-server/src/test/java/org/apache/xbean/server/classloader/
Modified:
    geronimo/xbean/branches/colossus/pom.xml
    geronimo/xbean/branches/colossus/xbean-classloader/pom.xml
    geronimo/xbean/branches/colossus/xbean-server/pom.xml
    geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/deployer/FileDeployer.java
    geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/spring/configuration/ClassLoaderXmlPreprocessor.java

Modified: geronimo/xbean/branches/colossus/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/pom.xml?rev=439496&r1=439495&r2=439496&view=diff
==============================================================================
--- geronimo/xbean/branches/colossus/pom.xml (original)
+++ geronimo/xbean/branches/colossus/pom.xml Fri Sep  1 15:47:00 2006
@@ -173,6 +173,7 @@
 
   <modules>
     <module>xbean-bootstrap</module>
+    <module>xbean-classloader</module>
     <module>xbean-classpath</module>
     <!--
     <module>xbean-jmx</module>
@@ -314,6 +315,11 @@
         <groupId>org.apache.xbean</groupId>
         <artifactId>xbean-bootstrap</artifactId>
         <version>COL-SNAPSHOT</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.xbean</groupId>
+        <artifactId>xbean-classloader</artifactId>
+       <version>COL-SNAPSHOT</version>
       </dependency>
       <dependency>
         <groupId>org.apache.xbean</groupId>

Modified: geronimo/xbean/branches/colossus/xbean-classloader/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-classloader/pom.xml?rev=439496&r1=439480&r2=439496&view=diff
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-classloader/pom.xml (original)
+++ geronimo/xbean/branches/colossus/xbean-classloader/pom.xml Fri Sep  1 15:47:00 2006
@@ -5,8 +5,8 @@
     <version>COL-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
-  <artifactId>xbean-server</artifactId>
-  <name>XBean :: Server</name>
+  <artifactId>xbean-classloader</artifactId>
+  <name>XBean :: Classloader</name>
   <version>COL-SNAPSHOT</version>
   <build>
     <plugins>
@@ -37,30 +37,13 @@
   </build>
   <dependencies>
     <dependency>
-      <groupId>org.apache.xbean</groupId>
-      <artifactId>xbean-bootstrap</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.xbean</groupId>
-      <artifactId>xbean-spring</artifactId>
-    </dependency>
-    <dependency>
       <groupId>cglib</groupId>
       <artifactId>cglib-nodep</artifactId>
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.apache.xbean</groupId>
-      <artifactId>xbean-kernel</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>commons-logging</groupId>
-      <artifactId>commons-logging</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>mx4j</groupId>
-      <artifactId>mx4j</artifactId>
-      <optional>true</optional>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring</artifactId>
     </dependency>
   </dependencies>
 </project>

Added: geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/ClassLoaderUtil.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/ClassLoaderUtil.java?rev=439496&view=auto
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/ClassLoaderUtil.java (added)
+++ geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/ClassLoaderUtil.java Fri Sep  1 15:47:00 2006
@@ -0,0 +1,89 @@
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  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.xbean.classloader;
+
+import java.util.Map;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
+
+/**
+ * Utility methods for class loader manipulation in a server environment.
+ *
+ * @author Dain Sundstrom
+ * @version $Id$
+ * @since 2.0
+ */
+public final class ClassLoaderUtil {
+    private ClassLoaderUtil() {
+    }
+
+    /**
+     * Cleans well known class loader leaks in VMs and libraries.  There is a lot of bad code out there and this method
+     * will clear up the know problems.  This method should only be called when the class loader will no longer be used.
+     * It this method is called two often it can have a serious impact on preformance.
+     * @param classLoader the class loader to destroy
+     */
+    public static void destroy(ClassLoader classLoader) {
+        releaseCommonsLoggingCache(classLoader);
+        clearSunSoftCache(ObjectInputStream.class, "subclassAudits");
+        clearSunSoftCache(ObjectOutputStream.class, "subclassAudits");
+        clearSunSoftCache(ObjectStreamClass.class, "localDescs");
+        clearSunSoftCache(ObjectStreamClass.class, "reflectors");
+    }
+
+    /**
+     * Clears the caches maintained by the SunVM object stream implementation.  This method uses reflection and
+     * setAccessable to obtain access to the Sun cache.  The cache is locked with a synchronize monitor and cleared.
+     * This method completely clears the class loader cache which will impact preformance of object serialization.
+     * @param clazz the name of the class containing the cache field
+     * @param fieldName the name of the cache field
+     */
+    public static void clearSunSoftCache(Class clazz, String fieldName) {
+        Map cache = null;
+        try {
+            Field field = clazz.getDeclaredField(fieldName);
+            field.setAccessible(true);
+            cache = (Map) field.get(null);
+        } catch (Throwable ignored) {
+            // there is nothing a user could do about this anyway
+        }
+
+        if (cache != null) {
+            synchronized (cache) {
+                cache.clear();
+            }
+        }
+    }
+
+    /**
+     * Releases the specified classloader from the Apache Jakarta Commons Logging class loader cache using reflection.
+     * @param classLoader the class loader to release
+     */
+    public static void releaseCommonsLoggingCache(ClassLoader classLoader) {
+        try {
+            Class logFactory = classLoader.loadClass("org.apache.commons.logging.LogFactory");
+            Method release = logFactory.getMethod("release", new Class[] {ClassLoader.class});
+            release.invoke(null, new Object[] {classLoader});
+        } catch (Throwable ignored) {
+            // there is nothing a user could do about this anyway
+        }
+    }
+
+}

Added: geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/DestroyableClassLoader.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/DestroyableClassLoader.java?rev=439496&view=auto
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/DestroyableClassLoader.java (added)
+++ geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/DestroyableClassLoader.java Fri Sep  1 15:47:00 2006
@@ -0,0 +1,33 @@
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  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.xbean.classloader;
+
+/**
+ * DestroyableClassLoader is a mixin interface for a ClassLoader that add a destroy method to propertly cleanup a
+ * classloader then dereferenced by the server.
+ *
+ * @author Dain Sundstrom
+ * @version $Id$
+ * @since 2.0
+ */
+public interface DestroyableClassLoader {
+    /**
+     * Destroys the clasloader releasing all resources.  After this mehtod is called, the class loader will no longer
+     * load any classes or resources.
+     */
+    void destroy();
+}

Added: geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileClassLoader.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileClassLoader.java?rev=439496&view=auto
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileClassLoader.java (added)
+++ geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/JarFileClassLoader.java Fri Sep  1 15:47:00 2006
@@ -0,0 +1,411 @@
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  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.xbean.classloader;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLStreamHandlerFactory;
+import java.security.CodeSource;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+/**
+ * The JarFileClassLoader that loads classes and resources from a list of JarFiles.  This method is simmilar to URLClassLoader
+ * except it properly closes JarFiles when the classloader is destroyed so that the file read lock will be released, and
+ * the jar file can be modified and deleted.
+ * <p>
+ * Note: This implementation currently does not work reliably on windows, since the jar URL handler included with the Sun JavaVM
+ * holds a read lock on the JarFile, and this lock is not released when the jar url is dereferenced.  To fix this a
+ * replacement for the jar url handler must be written.
+ *
+ * @author Dain Sundstrom
+ * @version $Id$
+ * @since 2.0
+ */
+public class JarFileClassLoader extends MultiParentClassLoader {
+    private static final URL[] EMPTY_URLS = new URL[0];
+    private final Object lock = new Object();
+    private final LinkedHashMap classPath = new LinkedHashMap();
+    private boolean destroyed = false;
+
+    /**
+     * Creates a JarFileClassLoader that is a child of the system class loader.
+     * @param name the name of this class loader
+     * @param urls a list of URLs from which classes and resources should be loaded
+     */
+    public JarFileClassLoader(String name, URL[] urls) {
+        super(name, EMPTY_URLS);
+        addURLs(urls);
+    }
+
+    /**
+     * Creates a JarFileClassLoader that is a child of the specified class loader.
+     * @param name the name of this class loader
+     * @param urls a list of URLs from which classes and resources should be loaded
+     * @param parent the parent of this class loader
+     */
+    public JarFileClassLoader(String name, URL[] urls, ClassLoader parent) {
+        this(name, urls, new ClassLoader[] {parent});
+    }
+
+    /**
+     * Creates a named class loader as a child of the specified parent and using the specified URLStreamHandlerFactory
+     * for accessing the urls..
+     * @param name the name of this class loader
+     * @param urls the urls from which this class loader will classes and resources
+     * @param parent the parent of this class loader
+     * @param factory the URLStreamHandlerFactory used to access the urls
+     */
+    public JarFileClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
+        this(name, urls, new ClassLoader[] {parent}, factory);
+    }
+
+    /**
+     * Creates a named class loader as a child of the specified parents.
+     * @param name the name of this class loader
+     * @param urls the urls from which this class loader will classes and resources
+     * @param parents the parents of this class loader
+     */
+    public JarFileClassLoader(String name, URL[] urls, ClassLoader[] parents) {
+        super(name, EMPTY_URLS, parents);
+        addURLs(urls);
+    }
+
+    /**
+     * Creates a named class loader as a child of the specified parents and using the specified URLStreamHandlerFactory
+     * for accessing the urls..
+     * @param name the name of this class loader
+     * @param urls the urls from which this class loader will classes and resources
+     * @param parents the parents of this class loader
+     * @param factory the URLStreamHandlerFactory used to access the urls
+     */
+    public JarFileClassLoader(String name, URL[] urls, ClassLoader[] parents, URLStreamHandlerFactory factory) {
+        super(name, EMPTY_URLS, parents, factory);
+        addURLs(urls);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public URL[] getURLs() {
+        return (URL[]) classPath.keySet().toArray(new URL[classPath.keySet().size()]);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void addURL(URL url) {
+        addURLs(Collections.singletonList(url));
+    }
+
+    /**
+     * Adds an array of urls to the end of this class loader.
+     * @param urls the URLs to add
+     */
+    protected void addURLs(URL[] urls) {
+        addURLs(Arrays.asList(urls));
+    }
+
+    /**
+     * Adds a list of urls to the end of this class loader.
+     * @param urls the URLs to add
+     */
+    protected void addURLs(List urls) {
+        LinkedList locationStack = new LinkedList(urls);
+        try {
+            while (!locationStack.isEmpty()) {
+                URL url = (URL) locationStack.removeFirst();
+
+                if (!"file".equals(url.getProtocol())) {
+                    // download the jar
+                    throw new Error("Only local file jars are supported " + url);
+                }
+
+                String path = url.getPath();
+                if (classPath.containsKey(path)) {
+                    continue;
+                }
+
+                File file = new File(path);
+                if (!file.canRead()) {
+                    // can't read file...
+                    continue;
+                }
+
+                // open the jar file
+                JarFile jarFile;
+                try {
+                    jarFile = new JarFile(file);
+                } catch (IOException e) {
+                    // can't seem to open the file
+                    continue;
+                }
+                classPath.put(url, jarFile);
+
+                // push the manifest classpath on the stack (make sure to maintain the order)
+                Manifest manifest = null;
+                try {
+                    manifest = jarFile.getManifest();
+                } catch (IOException ignored) {
+                }
+
+                if (manifest != null) {
+                    Attributes mainAttributes = manifest.getMainAttributes();
+                    String manifestClassPath = mainAttributes.getValue(Attributes.Name.CLASS_PATH);
+                    if (manifestClassPath != null) {
+                        LinkedList classPathUrls = new LinkedList();
+                        for (StringTokenizer tokenizer = new StringTokenizer(manifestClassPath, " "); tokenizer.hasMoreTokens();) {
+                            String entry = tokenizer.nextToken();
+                            File parentDir = file.getParentFile();
+                            File entryFile = new File(parentDir, entry);
+                            // manifest entries are optional... if they aren't there it is ok
+                            if (entryFile.canRead()) {
+                                classPathUrls.addLast(entryFile.getAbsolutePath());
+                            }
+                        }
+                        locationStack.addAll(0, classPathUrls);
+                    }
+                }
+            }
+        } catch (Error e) {
+            destroy();
+            throw e;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void destroy() {
+        synchronized (lock) {
+            if (destroyed) {
+                return;
+            }
+            destroyed = true;
+            for (Iterator iterator = classPath.values().iterator(); iterator.hasNext();) {
+                JarFile jarFile = (JarFile) iterator.next();
+                try {
+                    jarFile.close();
+                } catch (IOException ignored) {
+                }
+            }
+            classPath.clear();
+        }
+        super.destroy();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public URL findResource(String resourceName) {
+        URL jarUrl = null;
+        synchronized (lock) {
+            if (destroyed) {
+                return null;
+            }
+            for (Iterator iterator = classPath.entrySet().iterator(); iterator.hasNext() && jarUrl == null;) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                JarFile jarFile = (JarFile) entry.getValue();
+                JarEntry jarEntry = jarFile.getJarEntry(resourceName);
+                if (jarEntry != null && !jarEntry.isDirectory()) {
+                    jarUrl = (URL) entry.getKey();
+                }
+            }
+        }
+
+
+        try {
+            String urlString = "jar:" + jarUrl + "!/" + resourceName;
+            return new URL(jarUrl, urlString);
+        } catch (MalformedURLException e) {
+            return null;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Enumeration findResources(String resourceName) throws IOException {
+        List resources = new ArrayList();
+        List superResources = Collections.list(super.findResources(resourceName));
+        resources.addAll(superResources);
+
+        synchronized (lock) {
+            if (destroyed) {
+                return Collections.enumeration(Collections.EMPTY_LIST);
+            }
+            for (Iterator iterator = classPath.entrySet().iterator(); iterator.hasNext();) {
+                Map.Entry entry = (Map.Entry) iterator.next();
+                JarFile jarFile = (JarFile) entry.getValue();
+                JarEntry jarEntry = jarFile.getJarEntry(resourceName);
+                if (jarEntry != null && !jarEntry.isDirectory()) {
+                    try {
+                        URL url = (URL) entry.getKey();
+                        String urlString = "jar:" + url + "!/" + resourceName;
+                        resources.add(new URL(url, urlString));
+                    } catch (MalformedURLException e) {
+                    }
+                }
+            }
+        }
+
+        return Collections.enumeration(resources);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Class findClass(String className) throws ClassNotFoundException {
+        SecurityManager securityManager = System.getSecurityManager();
+        if (securityManager != null) {
+            String packageName;
+            int packageEnd = className.lastIndexOf('.');
+            if (packageEnd >= 0) {
+                packageName = className.substring(0, packageEnd);
+                securityManager.checkPackageDefinition(packageName);
+            }
+        }
+
+        Certificate[] certificates = null;
+        URL jarUrl = null;
+        Manifest manifest = null;
+        byte[] bytes;
+        synchronized (lock) {
+            if (destroyed) {
+                throw new ClassNotFoundException("Class loader has been destroyed: " + className);
+            }
+
+            try {
+                String entryName = className.replace('.', '/') + ".class";
+                InputStream inputStream = null;
+                for (Iterator iterator = classPath.entrySet().iterator(); iterator.hasNext() && inputStream == null;) {
+                    Map.Entry entry = (Map.Entry) iterator.next();
+                    jarUrl = (URL) entry.getKey();
+                    JarFile jarFile = (JarFile) entry.getValue();
+                    JarEntry jarEntry = jarFile.getJarEntry(entryName);
+                    if (jarEntry != null && !jarEntry.isDirectory()) {
+                        inputStream = jarFile.getInputStream(jarEntry);
+                        certificates = jarEntry.getCertificates();
+                        manifest = jarFile.getManifest();
+                    }
+                }
+                if (inputStream == null) {
+                    throw new ClassNotFoundException(className);
+                }
+
+                try {
+                    byte[] buffer = new byte[4096];
+                    ByteArrayOutputStream out = new ByteArrayOutputStream();
+                    for (int count = inputStream.read(buffer); count >= 0; count = inputStream.read(buffer)) {
+                        out.write(buffer, 0, count);
+                    }
+                    bytes = out.toByteArray();
+                } finally {
+                    inputStream.close();
+                }
+            } catch (IOException e) {
+                throw new ClassNotFoundException(className, e);
+            }
+        }
+
+        definePackage(className, jarUrl, manifest);
+        CodeSource codeSource = new CodeSource(jarUrl, certificates);
+        Class clazz = defineClass(className, bytes, 0, bytes.length, codeSource);
+        return clazz;
+    }
+
+    private void definePackage(String className, URL jarUrl, Manifest manifest) {
+        int packageEnd = className.lastIndexOf('.');
+        if (packageEnd < 0) {
+            return;
+        }
+
+        String packageName = className.substring(0, packageEnd);
+        String packagePath = packageName.replace('.', '/') + "/";
+
+        Attributes packageAttributes = null;
+        Attributes mainAttributes = null;
+        if (manifest != null) {
+            packageAttributes = manifest.getAttributes(packagePath);
+            mainAttributes = manifest.getMainAttributes();
+        }
+        Package pkg = getPackage(packageName);
+        if (pkg != null) {
+            if (pkg.isSealed()) {
+                if (!pkg.isSealed(jarUrl)) {
+                    throw new SecurityException("Package was already sealed with another URL: package=" + packageName + ", url=" + jarUrl);
+                }
+            } else {
+                if (isSealed(packageAttributes, mainAttributes)) {
+                    throw new SecurityException("Package was already been loaded and not sealed: package=" + packageName + ", url=" + jarUrl);
+                }
+            }
+        } else {
+            String specTitle = getAttribute(Attributes.Name.SPECIFICATION_TITLE, packageAttributes, mainAttributes);
+            String specVendor = getAttribute(Attributes.Name.SPECIFICATION_VENDOR, packageAttributes, mainAttributes);
+            String specVersion = getAttribute(Attributes.Name.SPECIFICATION_VERSION, packageAttributes, mainAttributes);
+            String implTitle = getAttribute(Attributes.Name.IMPLEMENTATION_TITLE, packageAttributes, mainAttributes);
+            String implVendor = getAttribute(Attributes.Name.IMPLEMENTATION_VENDOR, packageAttributes, mainAttributes);
+            String implVersion = getAttribute(Attributes.Name.IMPLEMENTATION_VERSION, packageAttributes, mainAttributes);
+
+            URL sealBase = null;
+            if (isSealed(packageAttributes, mainAttributes)) {
+                sealBase = jarUrl;
+            }
+
+            definePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
+        }
+    }
+
+    private String getAttribute(Attributes.Name name, Attributes packageAttributes, Attributes mainAttributes) {
+        if (packageAttributes != null) {
+            String value = packageAttributes.getValue(name);
+            if (value != null) {
+                return value;
+            }
+        }
+        if (mainAttributes != null) {
+            return mainAttributes.getValue(name);
+        }
+        return null;
+    }
+
+    private boolean isSealed(Attributes packageAttributes, Attributes mainAttributes) {
+        String sealed = getAttribute(Attributes.Name.SEALED, packageAttributes, mainAttributes);
+        if (sealed == null) {
+            return false;
+        }
+        return "true".equalsIgnoreCase(sealed);
+    }
+}
\ No newline at end of file

Added: geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/MultiParentClassLoader.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/MultiParentClassLoader.java?rev=439496&view=auto
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/MultiParentClassLoader.java (added)
+++ geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/MultiParentClassLoader.java Fri Sep  1 15:47:00 2006
@@ -0,0 +1,193 @@
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  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.xbean.classloader;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLStreamHandlerFactory;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * A MultiParentClassLoader is a simple extension of the URLClassLoader that simply changes the single parent class
+ * loader model to support a list of parent class loaders.  Each operation that accesses a parent, has been replaced
+ * with a operation that checks each parent in order.  This getParent method of this class will always return null,
+ * which may be interperated by the calling code to mean that this class loader is a direct child of the system class
+ * loader.
+ *
+ * @author Dain Sundstrom
+ * @version $Id$
+ * @since 2.0
+ */
+public class MultiParentClassLoader extends NamedClassLoader {
+    private final ClassLoader[] parents;
+
+    /**
+     * Creates a named class loader with no parents.
+     * @param name the name of this class loader
+     * @param urls the urls from which this class loader will classes and resources
+     */
+    public MultiParentClassLoader(String name, URL[] urls) {
+        super(name, urls);
+        parents = new ClassLoader[0];
+    }
+
+    /**
+     * Creates a named class loader as a child of the specified parent.
+     * @param name the name of this class loader
+     * @param urls the urls from which this class loader will classes and resources
+     * @param parent the parent of this class loader
+     */
+    public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent) {
+        this(name, urls, new ClassLoader[] {parent});
+    }
+
+    /**
+     * Creates a named class loader as a child of the specified parent and using the specified URLStreamHandlerFactory
+     * for accessing the urls..
+     * @param name the name of this class loader
+     * @param urls the urls from which this class loader will classes and resources
+     * @param parent the parent of this class loader
+     * @param factory the URLStreamHandlerFactory used to access the urls
+     */
+    public MultiParentClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
+        this(name, urls, new ClassLoader[] {parent}, factory);
+    }
+
+    /**
+     * Creates a named class loader as a child of the specified parents.
+     * @param name the name of this class loader
+     * @param urls the urls from which this class loader will classes and resources
+     * @param parents the parents of this class loader
+     */
+    public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents) {
+        super(name, urls);
+        this.parents = copyParents(parents);
+    }
+
+    /**
+     * Creates a named class loader as a child of the specified parents and using the specified URLStreamHandlerFactory
+     * for accessing the urls..
+     * @param name the name of this class loader
+     * @param urls the urls from which this class loader will classes and resources
+     * @param parents the parents of this class loader
+     * @param factory the URLStreamHandlerFactory used to access the urls
+     */
+    public MultiParentClassLoader(String name, URL[] urls, ClassLoader[] parents, URLStreamHandlerFactory factory) {
+        super(name, urls, null, factory);
+        this.parents = copyParents(parents);
+    }
+
+    private static ClassLoader[] copyParents(ClassLoader[] parents) {
+        ClassLoader[] newParentsArray = new ClassLoader[parents.length];
+        for (int i = 0; i < parents.length; i++) {
+            ClassLoader parent = parents[i];
+            if (parent == null) {
+                throw new NullPointerException("parent[" + i + "] is null");
+            }
+            newParentsArray[i] = parent;
+        }
+        return newParentsArray;
+    }
+
+    /**
+     * Gets the parents of this class loader.
+     * @return the parents of this class loader
+     */
+    public ClassLoader[] getParents() {
+        return parents;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        Class clazz = findLoadedClass(name);
+        for (int i = 0; i < parents.length && clazz == null; i++) {
+            ClassLoader parent = parents[i];
+            try {
+                clazz = parent.loadClass(name);
+            } catch (ClassNotFoundException ignored) {
+                // this parent didn't have the class; try the next one
+            }
+        }
+
+        if (clazz == null) {
+            // parents didn't have the class; attempt to load from my urls
+            return super.loadClass(name, resolve);
+        } else {
+            // we found the class; resolve it if requested
+            if (resolve) {
+                resolveClass(clazz);
+            }
+            return clazz;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public URL getResource(String name) {
+        URL url = null;
+        for (int i = 0; i < parents.length && url == null; i++) {
+            ClassLoader parent = parents[i];
+            url = parent.getResource(name);
+        }
+
+        if (url == null) {
+            // parents didn't have the resource; attempt to load it from my urls
+            return super.getResource(name);
+        } else {
+            return url;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Enumeration findResources(String name) throws IOException {
+        List resources = new ArrayList();
+
+        // Add resources from all parents
+        for (int i = 0; i < parents.length; i++) {
+            ClassLoader parent = parents[i];
+            List parentResources = Collections.list(parent.getResources(name));
+            resources.addAll(parentResources);
+        }
+
+        // Add the resources from my urls
+        List myResources = Collections.list(super.findResources(name));
+        resources.addAll(myResources);
+
+        // return an enumeration over the list
+        return Collections.enumeration(resources);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return "[" + getClass().getName() + ":" +
+                " name=" + getName() +
+                " urls=" + Arrays.asList(getURLs()) +
+                " parents=" + Arrays.asList(parents) +
+                "]";
+    }
+}

Added: geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/NamedClassLoader.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/NamedClassLoader.java?rev=439496&view=auto
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/NamedClassLoader.java (added)
+++ geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/NamedClassLoader.java Fri Sep  1 15:47:00 2006
@@ -0,0 +1,94 @@
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  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.xbean.classloader;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.URLStreamHandlerFactory;
+import java.util.Arrays;
+
+
+/**
+ * The NamedClassLoader is a simple extension to URLClassLoader that adds a name and a destroy method that cleans up
+ * the commons logging and JavaVM caches of the classloader.
+ *
+ * @author Dain Sundstrom
+ * @version $Id$
+ * @since 2.0
+ */
+public class NamedClassLoader extends URLClassLoader implements DestroyableClassLoader {
+    private final String name;
+
+    /**
+     * Creates a named class loader with no parents.
+     * @param name the name of this class loader
+     * @param urls the urls from which this class loader will classes and resources
+     */
+    public NamedClassLoader(String name, URL[] urls) {
+        super(urls);
+        this.name = name;
+    }
+
+    /**
+     * Creates a named class loader as a child of the specified parent.
+     * @param name the name of this class loader
+     * @param urls the urls from which this class loader will classes and resources
+     * @param parent the parent of this class loader
+     */
+    public NamedClassLoader(String name, URL[] urls, ClassLoader parent) {
+        super(urls, parent);
+        this.name = name;
+    }
+
+    /**
+     * Creates a named class loader as a child of the specified parent and using the specified URLStreamHandlerFactory
+     * for accessing the urls..
+     * @param name the name of this class loader
+     * @param urls the urls from which this class loader will classes and resources
+     * @param parent the parent of this class loader
+     * @param factory the URLStreamHandlerFactory used to access the urls
+     */
+    public NamedClassLoader(String name, URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
+        super(urls, parent, factory);
+        this.name = name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void destroy() {
+        ClassLoaderUtil.destroy(this);
+    }
+
+    /**
+     * Gets the name of this class loader.
+     * @return the name of this class loader
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return "[" + getClass().getName() + ":" +
+                " name=" + getName() +
+                " urls=" + Arrays.asList(getURLs()) +
+                "]";
+    }
+}

Added: geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/ThreadContextClassLoaderFactoryBean.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/ThreadContextClassLoaderFactoryBean.java?rev=439496&view=auto
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/ThreadContextClassLoaderFactoryBean.java (added)
+++ geronimo/xbean/branches/colossus/xbean-classloader/src/main/java/org/apache/xbean/classloader/ThreadContextClassLoaderFactoryBean.java Fri Sep  1 15:47:00 2006
@@ -0,0 +1,41 @@
+/**
+ *
+ * Copyright 2005-2006 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.xbean.classloader;
+
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ * A factory bean to expose the current thread context class loader.
+ * 
+ * * @org.apache.xbean.XBean namespace="http://xbean.apache.org/schemas/classloader"
+ *                         element="threadContextClassLoader" description="References the ClassLoader of the current thread context"
+ * @version $Revision: 380562 $
+ */
+public class ThreadContextClassLoaderFactoryBean implements FactoryBean {
+
+    public Object getObject() throws Exception {
+        return Thread.currentThread().getContextClassLoader();
+    }
+
+    public Class getObjectType() {
+        return ClassLoader.class;
+    }
+
+    public boolean isSingleton() {
+        return true;
+    }
+}

Added: geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/classloader/JarFileClassLoaderTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/classloader/JarFileClassLoaderTest.java?rev=439496&view=auto
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/classloader/JarFileClassLoaderTest.java (added)
+++ geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/classloader/JarFileClassLoaderTest.java Fri Sep  1 15:47:00 2006
@@ -0,0 +1,32 @@
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  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.xbean.classloader;
+
+import java.net.URL;
+
+/**
+ * Test the JarFileClassLoader.
+ *
+ * @author Dain Sundstrom
+ * @version $Id$
+ * @since 2.0
+ */
+public class JarFileClassLoaderTest extends MultiParentClassLoaderTest {
+    protected MultiParentClassLoader createClassLoader(String name, URL[] urls, ClassLoader[] parents) {
+        return new JarFileClassLoader(name, urls, parents);
+    }
+}

Added: geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/classloader/MultiParentClassLoaderTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/classloader/MultiParentClassLoaderTest.java?rev=439496&view=auto
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/classloader/MultiParentClassLoaderTest.java (added)
+++ geronimo/xbean/branches/colossus/xbean-classloader/src/test/java/org/apache/xbean/classloader/MultiParentClassLoaderTest.java Fri Sep  1 15:47:00 2006
@@ -0,0 +1,360 @@
+/**
+ *
+ * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable.
+ *
+ *  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.xbean.classloader;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Enumeration;
+import java.util.SortedSet;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+
+import junit.framework.TestCase;
+import net.sf.cglib.core.DefaultGeneratorStrategy;
+import net.sf.cglib.core.NamingPolicy;
+import net.sf.cglib.core.Predicate;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.NoOp;
+
+/**
+ * Tests the MultiParentClassLoader including classloading and resource loading.
+ * @author Dain Sundstrom
+ * @version $Id$
+ * @since 2.0
+ */
+public class MultiParentClassLoaderTest extends TestCase {
+    private static final String CLASS_NAME = "TestClass";
+    private static final String ENTRY_NAME = "foo";
+    private static final String ENTRY_VALUE = "bar";
+    private File[] files;
+    private static final String NON_EXISTANT_RESOURCE = "non-existant-resource";
+    private static final String NON_EXISTANT_CLASS = "NonExistant.class";
+    private URLClassLoader[] parents;
+    private File myFile;
+    private MultiParentClassLoader classLoader;
+    private static final String NAME = "my test class loader";
+
+    /**
+     * Verify that the test jars are valid.
+     * @throws Exception if a problem occurs
+     */
+    public void testTestJars() throws Exception {
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            JarFile jarFile = new JarFile(files[i]);
+            String urlString = "jar:" + file.toURL() + "!/" + ENTRY_NAME;
+            URL url = new URL(files[i].toURL(), urlString);
+            assertStreamContains(ENTRY_VALUE + i, url.openStream());
+            jarFile.close();
+
+            URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { file.toURL() } );
+            // clazz shared by all
+            Class clazz = urlClassLoader.loadClass(CLASS_NAME);
+            assertNotNull(clazz);
+            assertTrue(SortedSet.class.isAssignableFrom(clazz));
+
+            // clazz specific to this jar
+            clazz = urlClassLoader.loadClass(CLASS_NAME + i);
+            assertNotNull(clazz);
+            assertTrue(SortedSet.class.isAssignableFrom(clazz));
+
+            // resource shared by all jars
+            InputStream in = urlClassLoader.getResourceAsStream(ENTRY_NAME );
+            assertStreamContains("Should have found value from parent " + i, ENTRY_VALUE + i, in);
+
+            // resource specific to this jar
+            in = urlClassLoader.getResourceAsStream(ENTRY_NAME + i);
+            assertStreamContains("Should have found value from parent " + i, ENTRY_VALUE + i + ENTRY_VALUE, in);
+        }
+    }
+
+    /**
+     * Verify the get name method returns the name provided to the constructor.
+     */
+    public void testGetName() {
+        assertEquals(NAME, classLoader.getName());
+    }
+
+    /**
+     * Verufy that the getParents method returns a different array from the one passed to the constructor and that the
+     * parents are in the same order.
+     */
+    public void testGetParents() {
+        ClassLoader[] actualParents = classLoader.getParents();
+        assertNotSame(parents, actualParents);
+        assertEquals(parents.length, actualParents.length);
+        for (int i = 0; i < actualParents.length; i++) {
+            assertEquals(parents[i], actualParents[i]);
+        }
+    }
+
+    /**
+     * Test loadClass loads in preference of the parents, in order, and then the local urls.
+     * @throws Exception if a problem occurs
+     */
+    public void testLoadClass() throws Exception {
+        // load class specific to my class loader
+        Class clazz = classLoader.loadClass(CLASS_NAME + 33);
+        assertNotNull(clazz);
+        assertTrue(SortedSet.class.isAssignableFrom(clazz));
+        assertEquals(classLoader, clazz.getClassLoader());
+
+        // load class specific to each parent class loader
+        for (int i = 0; i < parents.length; i++) {
+            URLClassLoader parent = parents[i];
+            clazz = classLoader.loadClass(CLASS_NAME + i);
+            assertNotNull(clazz);
+            assertTrue(SortedSet.class.isAssignableFrom(clazz));
+            assertEquals(parent, clazz.getClassLoader());
+        }
+
+        // class shared by all class loaders
+        clazz = classLoader.loadClass(CLASS_NAME);
+        assertNotNull(clazz);
+        assertTrue(SortedSet.class.isAssignableFrom(clazz));
+        assertEquals(parents[0], clazz.getClassLoader());
+    }
+
+    /**
+     * Test that an attempt to load a non-existant class causes a ClassNotFoundException.
+     */
+    public void testLoadNonExistantClass() {
+        try {
+            classLoader.loadClass(NON_EXISTANT_CLASS);
+            fail("loadClass should have thrown a ClassNotFoundException");
+        } catch (ClassNotFoundException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Test getResourceAsStream loads in preference of the parents, in order, and then the local urls.
+     * @throws Exception if a problem occurs
+     */
+    public void testGetResourceAsStream() throws Exception {
+        InputStream in = classLoader.getResourceAsStream(ENTRY_NAME + 33);
+        assertStreamContains("Should have found value from my file", ENTRY_VALUE + 33 + ENTRY_VALUE, in);
+
+        for (int i = 0; i < parents.length; i++) {
+            in = classLoader.getResourceAsStream(ENTRY_NAME + i);
+            assertStreamContains("Should have found value from parent " + i, ENTRY_VALUE + i + ENTRY_VALUE, in);
+        }
+
+        in = classLoader.getResourceAsStream(ENTRY_NAME);
+        assertStreamContains("Should have found value from first parent", ENTRY_VALUE + 0, in);
+    }
+
+    /**
+     * Test getResourceAsStream returns null when attempt is made to loade a non-existant resource.
+     * @throws Exception if a problem occurs
+     */
+    public void testGetNonExistantResourceAsStream() throws Exception {
+        InputStream in = classLoader.getResourceAsStream(NON_EXISTANT_RESOURCE);
+        assertNull(in);
+    }
+
+    /**
+     * Test getResource loads in preference of the parents, in order, and then the local urls.
+     * @throws Exception if a problem occurs
+     */
+    public void testGetResource() throws Exception {
+        URL resource = classLoader.getResource(ENTRY_NAME + 33);
+        assertURLContains("Should have found value from my file", ENTRY_VALUE + 33 + ENTRY_VALUE, resource);
+
+        for (int i = 0; i < parents.length; i++) {
+            resource = classLoader.getResource(ENTRY_NAME + i);
+            assertURLContains("Should have found value from parent " + i, ENTRY_VALUE + i + ENTRY_VALUE, resource);
+        }
+
+        resource = classLoader.getResource(ENTRY_NAME);
+        assertURLContains("Should have found value from first parent", ENTRY_VALUE + 0, resource);
+    }
+
+    /**
+     * Test getResource returns null when attempt is made to loade a non-existant resource.
+     * @throws Exception if a problem occurs
+     */
+    public void testGetNonExistantResource() throws Exception {
+        URL resource = classLoader.getResource(NON_EXISTANT_RESOURCE);
+        assertNull(resource);
+    }
+
+    /**
+     * Test getResource returns an enumeration in preference of the parents, in order, and then the local urls.
+     * @throws Exception if a problem occurs
+     */
+    public void testGetResources() throws Exception {
+        Enumeration resources = classLoader.getResources(ENTRY_NAME);
+        assertNotNull(resources);
+        assertTrue(resources.hasMoreElements());
+
+        // there should be one entry for each parent
+        for (int i = 0; i < parents.length; i++) {
+            URL resource = (URL) resources.nextElement();
+            assertURLContains("Should have found value from parent " + i, ENTRY_VALUE + i, resource);
+        }
+
+        // and one entry from my url
+        assertTrue(resources.hasMoreElements());
+        URL resource = (URL) resources.nextElement();
+        assertURLContains("Should have found value from my file", ENTRY_VALUE + 33, resource);
+    }
+
+    /**
+     * Test getResources returns an empty enumeration when attempt is made to loade a non-existant resource.
+     * @throws Exception if a problem occurs
+     */
+    public void testGetNonExistantResources() throws Exception {
+        Enumeration resources = classLoader.getResources(NON_EXISTANT_RESOURCE);
+        assertNotNull(resources);
+        assertFalse(resources.hasMoreElements());
+    }
+
+    private void assertStreamContains(String expectedValue, InputStream in) throws IOException {
+        assertStreamContains(null, expectedValue, in);
+    }
+
+    private void assertStreamContains(String message, String expectedValue, InputStream in) throws IOException {
+        String entryValue;
+        try {
+            StringBuffer stringBuffer = new StringBuffer();
+            byte[] bytes = new byte[4000];
+            for (int count = in.read(bytes); count != -1; count = in.read(bytes)) {
+                stringBuffer.append(new String(bytes, 0, count));
+            }
+            entryValue = stringBuffer.toString();
+        } finally {
+            in.close();
+        }
+        assertEquals(message, expectedValue, entryValue);
+    }
+
+    private void assertURLContains(String message, String expectedValue, URL resource) throws IOException {
+        InputStream in;
+        assertNotNull(resource);
+        in = resource.openStream();
+        assertStreamContains(message, expectedValue, in);
+    }
+
+    private static void assertFileExists(File file) {
+        assertTrue("File should exist: " + file, file.canRead());
+    }
+
+    private static void assertFileNotExists(File file) {
+        assertTrue("File should not exist: " + file, !file.canRead());
+    }
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        files = new File[3];
+        for (int i = 0; i < files.length; i++) {
+            files[i] = createJarFile(i);
+        }
+
+        parents = new URLClassLoader[3];
+        for (int i = 0; i < parents.length; i++) {
+            parents[i] = new URLClassLoader(new URL[]{files[i].toURL()});
+        }
+
+        myFile = createJarFile(33);
+        classLoader = createClassLoader(NAME, new URL[]{myFile.toURL()}, parents);
+    }
+
+    /**
+     * Creates the class loader to test.
+     * @param name the name of the classloader
+     * @param urls the urls to load classes and resources from
+     * @param parents the parents of the class loader
+     * @return the class loader to test
+     */
+    protected MultiParentClassLoader createClassLoader(String name, URL[] urls, ClassLoader[] parents) {
+        return new MultiParentClassLoader(name, urls, parents);
+    }
+
+    private static File createJarFile(int i) throws IOException {
+        File file = File.createTempFile("test-" + i + "-", ".jar");
+
+        FileOutputStream out = new FileOutputStream(file);
+        JarOutputStream jarOut = new JarOutputStream(out);
+
+        // common class shared by everyone
+        jarOut.putNextEntry(new JarEntry(CLASS_NAME + ".class"));
+        jarOut.write(createClass(CLASS_NAME));
+
+        // class only available in this jar
+        jarOut.putNextEntry(new JarEntry(CLASS_NAME + i + ".class"));
+        jarOut.write(createClass(CLASS_NAME + i));
+
+        // common resource shared by everyone
+        jarOut.putNextEntry(new JarEntry(ENTRY_NAME));
+        jarOut.write((ENTRY_VALUE + i).getBytes());
+
+        // resource only available in this jar
+        jarOut.putNextEntry(new JarEntry(ENTRY_NAME + i));
+        jarOut.write((ENTRY_VALUE + i + ENTRY_VALUE).getBytes());
+
+        jarOut.close();
+        out.close();
+
+        assertFileExists(file);
+        return file;
+    }
+
+    private static byte[] createClass(final String name) {
+        Enhancer enhancer = new Enhancer();
+        enhancer.setNamingPolicy(new NamingPolicy() {
+            public String getClassName(String prefix, String source, Object key, Predicate names) {
+                return name;
+            }
+        });
+        enhancer.setClassLoader(new URLClassLoader(new URL[0]));
+        enhancer.setSuperclass(Object.class);
+        enhancer.setInterfaces(new Class[]{SortedSet.class});
+        enhancer.setCallbackTypes(new Class[]{NoOp.class});
+        enhancer.setUseFactory(false);
+        ByteCode byteCode = new ByteCode();
+        enhancer.setStrategy(byteCode);
+        enhancer.createClass();
+
+        return byteCode.getByteCode();
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        for (int i = 0; i < files.length; i++) {
+            files[i].delete();
+        }
+    }
+
+    private static class ByteCode extends DefaultGeneratorStrategy {
+        private byte[] byteCode;
+
+        public byte[] transform(byte[] byteCode) {
+            this.byteCode = byteCode;
+            return byteCode;
+        }
+
+        public byte[] getByteCode() {
+            return byteCode;
+        }
+    }
+}

Modified: geronimo/xbean/branches/colossus/xbean-server/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-server/pom.xml?rev=439496&r1=439495&r2=439496&view=diff
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-server/pom.xml (original)
+++ geronimo/xbean/branches/colossus/xbean-server/pom.xml Fri Sep  1 15:47:00 2006
@@ -36,10 +36,14 @@
     </plugins>
   </build>
   <dependencies>
-    <dependency>
-      <groupId>org.apache.xbean</groupId>
-      <artifactId>xbean-bootstrap</artifactId>
-    </dependency>
+      <dependency>
+        <groupId>org.apache.xbean</groupId>
+        <artifactId>xbean-bootstrap</artifactId>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.xbean</groupId>
+        <artifactId>xbean-classloader</artifactId>
+      </dependency>
     <dependency>
       <groupId>org.apache.xbean</groupId>
       <artifactId>xbean-spring</artifactId>

Modified: geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/deployer/FileDeployer.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/deployer/FileDeployer.java?rev=439496&r1=439495&r2=439496&view=diff
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/deployer/FileDeployer.java (original)
+++ geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/deployer/FileDeployer.java Fri Sep  1 15:47:00 2006
@@ -23,13 +23,12 @@
 import org.apache.xbean.kernel.ServiceFactory;
 import org.apache.xbean.kernel.ServiceRegistrationException;
 import org.apache.xbean.kernel.StringServiceName;
-import org.apache.xbean.server.classloader.NamedClassLoader;
+import org.apache.xbean.classloader.NamedClassLoader;
 import org.apache.xbean.server.spring.configuration.SpringConfigurationServiceFactory;
 import org.apache.xbean.spring.context.ResourceXmlApplicationContext;
 import org.apache.xbean.spring.context.SpringApplicationContext;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.core.io.FileSystemResource;
@@ -46,7 +45,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.Set;
 import java.util.StringTokenizer;
 
 /**

Modified: geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/spring/configuration/ClassLoaderXmlPreprocessor.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/spring/configuration/ClassLoaderXmlPreprocessor.java?rev=439496&r1=439495&r2=439496&view=diff
==============================================================================
--- geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/spring/configuration/ClassLoaderXmlPreprocessor.java (original)
+++ geronimo/xbean/branches/colossus/xbean-server/src/main/java/org/apache/xbean/server/spring/configuration/ClassLoaderXmlPreprocessor.java Fri Sep  1 15:47:00 2006
@@ -21,7 +21,7 @@
 import java.util.List;
 import java.util.ListIterator;
 
-import org.apache.xbean.server.classloader.MultiParentClassLoader;
+import org.apache.xbean.classloader.MultiParentClassLoader;
 import org.apache.xbean.server.repository.Repository;
 import org.apache.xbean.server.spring.loader.SpringLoader;
 import org.apache.xbean.spring.context.SpringXmlPreprocessor;



Mime
View raw message