geronimo-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From d...@apache.org
Subject svn commit: r264851 - in /geronimo/trunk/modules/kernel/src: java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java test/org/apache/geronimo/kernel/config/MultiParentClassLoaderTest.java
Date Tue, 30 Aug 2005 20:31:32 GMT
Author: dain
Date: Tue Aug 30 13:31:13 2005
New Revision: 264851

URL: http://svn.apache.org/viewcvs?rev=264851&view=rev
Log:
Ported MultiParentClassLoader from gbean

Added:
    geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java
  (with props)
    geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/kernel/config/MultiParentClassLoaderTest.java
  (with props)

Added: geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java?rev=264851&view=auto
==============================================================================
--- geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java
(added)
+++ geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java
Tue Aug 30 13:31:13 2005
@@ -0,0 +1,186 @@
+/**
+ *
+ * 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.geronimo.kernel.config;
+
+import java.net.URLClassLoader;
+import java.net.URL;
+import java.net.URLStreamHandlerFactory;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.io.IOException;
+
+/**
+ * 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.
+ * @version $Rev$ $Date$
+ */
+public class MultiParentClassLoader extends URLClassLoader {
+    private final String name;
+    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(urls);
+        this.name = name;
+        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(urls);
+        this.name = name;
+        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(urls, null, factory);
+        this.name = name;
+        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 name of this class loader.
+     * @return the name of this class loader
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Gets the parents of this class loader.
+     * @return the parents of this class loader
+     */
+    public ClassLoader[] getParents() {
+        return parents;
+    }
+
+    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;
+        }
+    }
+
+    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;
+        }
+    }
+
+    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);
+    }
+
+    public String toString() {
+        return "[" + getClass().getName() + " name=" + name + "]";
+    }
+}

Propchange: geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/kernel/src/java/org/apache/geronimo/kernel/config/MultiParentClassLoader.java
------------------------------------------------------------------------------
    svn:keywords = "Date Rev Id"

Added: geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/kernel/config/MultiParentClassLoaderTest.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/kernel/config/MultiParentClassLoaderTest.java?rev=264851&view=auto
==============================================================================
--- geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/kernel/config/MultiParentClassLoaderTest.java
(added)
+++ geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/kernel/config/MultiParentClassLoaderTest.java
Tue Aug 30 13:31:13 2005
@@ -0,0 +1,342 @@
+/**
+ *
+ * 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.geronimo.kernel.config;
+
+import java.io.File;
+import java.io.Serializable;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.FileOutputStream;
+import java.net.URLClassLoader;
+import java.net.URL;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.JarEntry;
+import java.util.Enumeration;
+
+import junit.framework.TestCase;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.NoOp;
+import net.sf.cglib.core.NamingPolicy;
+import net.sf.cglib.core.Predicate;
+import net.sf.cglib.core.DefaultGeneratorStrategy;
+
+/**
+ * @version $Rev$ $Date$
+ */
+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
+     */
+    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(clazz instanceof Serializable);
+
+            // clazz specific to this jar
+            clazz = urlClassLoader.loadClass(CLASS_NAME + i);
+            assertNotNull(clazz);
+            assertTrue(clazz instanceof Serializable);
+
+            // 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(clazz instanceof Serializable);
+        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(clazz instanceof Serializable);
+            assertEquals(parent, clazz.getClassLoader());
+        }
+
+        // class shared by all class loaders
+        clazz = classLoader.loadClass(CLASS_NAME);
+        assertNotNull(clazz);
+        assertTrue(clazz instanceof Serializable);
+        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.
+     */
+    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.
+     */
+    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.
+     */
+    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 {
+        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 = new MultiParentClassLoader(NAME, new URL[]{myFile.toURL()}, 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[]{Serializable.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 {
+        for (int i = 0; i < files.length; i++) {
+            files[i].delete();
+            assertFileNotExists(files[i]);
+        }
+    }
+
+    private static class ByteCode extends DefaultGeneratorStrategy {
+        private byte[] byteCode;
+
+        public byte[] transform(byte[] byteCode) {
+            this.byteCode = byteCode;
+            return byteCode;
+        }
+
+        public byte[] getByteCode() {
+            return byteCode;
+        }
+    }
+}
\ No newline at end of file

Propchange: geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/kernel/config/MultiParentClassLoaderTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/trunk/modules/kernel/src/test/org/apache/geronimo/kernel/config/MultiParentClassLoaderTest.java
------------------------------------------------------------------------------
    svn:keywords = "Date Rev Id"



Mime
View raw message