Author: skitching Date: Tue Jul 5 01:50:41 2005 New Revision: 209245 URL: http://svn.apache.org/viewcvs?rev=209245&view=rev Log: Added test cases for child-first behaviour of PathableClassLoader.
Added: jakarta/commons/proper/logging/trunk/src/test/org/apache/commons/logging/pathable/ChildFirstTestCase.java (with props) Added: jakarta/commons/proper/logging/trunk/src/test/org/apache/commons/logging/pathable/ChildFirstTestCase.java URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/logging/trunk/src/test/org/apache/commons/logging/pathable/ChildFirstTestCase.java?rev=209245&view=auto ============================================================================== --- jakarta/commons/proper/logging/trunk/src/test/org/apache/commons/logging/pathable/ChildFirstTestCase.java (added) +++ jakarta/commons/proper/logging/trunk/src/test/org/apache/commons/logging/pathable/ChildFirstTestCase.java Tue Jul 5 01:50:41 2005 @@ -0,0 +1,320 @@ +/* + * 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.commons.logging.pathable; + +import java.net.URL; +import java.util.Enumeration; +import java.util.ArrayList; +import java.net.URLClassLoader; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.apache.commons.logging.PathableTestSuite; +import org.apache.commons.logging.PathableClassLoader; + +/** + * Tests for the PathableTestSuite and PathableClassLoader functionality, + * where lookup order for the PathableClassLoader is child-first. + * <p> + * These tests assume: + * <ul> + * <li>junit is in system classpath + * <li>nothing else is in system classpath + * </ul> + */ + +public class ChildFirstTestCase extends TestCase { + + /** + * Set up a custom classloader hierarchy for this test case. + * The hierarchy is: + * <ul> + * <li> contextloader: child-first. + * <li> childloader: child-first, used to load test case. + * <li> parentloader: child-first, parent is the bootclassloader. + * </ul> + */ + public static Test suite() throws Exception { + Class thisClass = ChildFirstTestCase.class; + ClassLoader thisClassLoader = thisClass.getClassLoader(); + + // Make the parent a direct child of the bootloader to hide all + // other classes in the system classpath + PathableClassLoader parent = new PathableClassLoader(null); + parent.setParentFirst(false); + + // Make the junit classes visible as a special case, as junit + // won't be able to call this class at all without this. The + // junit classes must be visible from the classloader that loaded + // this class, so use that as the source for future access to classes + // from the junit package. + parent.useExplicitLoader("junit.", thisClassLoader); + + // Make the commons-logging.jar classes visible via the parent + parent.addLogicalLib("commons-logging"); + + // Create a child classloader to load the test case through + PathableClassLoader child = new PathableClassLoader(parent); + child.setParentFirst(false); + + // Obviously, the child classloader needs to have the test classes + // in its path! + child.addLogicalLib("testclasses"); + child.addLogicalLib("commons-logging-adapters"); + + // Create a third classloader to be the context classloader. + PathableClassLoader context = new PathableClassLoader(child); + context.setParentFirst(false); + + // reload this class via the child classloader + Class testClass = child.loadClass(thisClass.getName()); + + // and return our custom TestSuite class + return new PathableTestSuite(testClass, context); + } + + /** + * Test that the classloader hierarchy is as expected, and that + * calling loadClass() on various classloaders works as expected. + * Note that for this test case, parent-first classloading is + * in effect. + */ + public void testPaths() throws Exception { + // the context classloader is not expected to be null + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + assertNotNull("Context classloader is null", contextLoader); + assertEquals("Context classloader has unexpected type", + PathableClassLoader.class.getName(), + contextLoader.getClass().getName()); + + // the classloader that loaded this class is obviously not null + ClassLoader thisLoader = this.getClass().getClassLoader(); + assertNotNull("thisLoader is null", thisLoader); + assertEquals("thisLoader has unexpected type", + PathableClassLoader.class.getName(), + thisLoader.getClass().getName()); + + // the suite method specified that the context classloader's parent + // is the loader that loaded this test case. + assertSame("Context classloader is not child of thisLoader", + thisLoader, contextLoader.getParent()); + + // thisLoader's parent should be available + ClassLoader parentLoader = thisLoader.getParent(); + assertNotNull("Parent classloader is null", parentLoader); + assertEquals("Parent classloader has unexpected type", + PathableClassLoader.class.getName(), + parentLoader.getClass().getName()); + + // parent should have a parent of null + assertNull("Parent classloader has non-null parent", parentLoader.getParent()); + + // getSystemClassloader is not a PathableClassLoader; it's of a + // built-in type. This also verifies that system classloader is none of + // (context, child, parent). + ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); + assertNotNull("System classloader is null", systemLoader); + assertFalse("System classloader has unexpected type", + PathableClassLoader.class.getName().equals( + systemLoader.getClass().getName())); + + // junit classes should be visible; their classloader is system. + // this will of course throw an exception if not found. + Class junitTest = contextLoader.loadClass("junit.framework.Test"); + assertSame("Junit not loaded via systemloader", + systemLoader, junitTest.getClassLoader()); + + // jcl api classes should be visible only via the parent + Class logClass = contextLoader.loadClass("org.apache.commons.logging.Log"); + assertSame("Log class not loaded via parent", + logClass.getClassLoader(), parentLoader); + + // jcl adapter classes should be visible via both parent and child. However + // as the classloaders are child-first we should see the child one. + Class log4jClass = contextLoader.loadClass("org.apache.commons.logging.impl.Log4J12Logger"); + assertSame("Log4J12Logger not loaded via child", + log4jClass.getClassLoader(), thisLoader); + + // test classes should be visible via the child only + Class testClass = contextLoader.loadClass("org.apache.commons.logging.PathableTestSuite"); + assertSame("PathableTestSuite not loaded via child", + testClass.getClassLoader(), thisLoader); + + // test loading of class that is not available + try { + Class noSuchClass = contextLoader.loadClass("no.such.class"); + fail("Class no.such.class is unexpectedly available"); + } catch(ClassNotFoundException ex) { + // ok + } + + // String class classloader is null + Class stringClass = contextLoader.loadClass("java.lang.String"); + assertNull("String class classloader is not null!", + stringClass.getClassLoader()); + } + + /** + * Test that the various flavours of ClassLoader.getResource work as expected. + */ + public void testResource() { + URL resource; + + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader childLoader = contextLoader.getParent(); + + // getResource where it doesn't exist + resource = childLoader.getResource("nosuchfile"); + assertNull("Non-null URL returned for invalid resource name", resource); + + // getResource where it is accessable only to parent classloader + resource = childLoader.getResource("org/apache/commons/logging/Log.class"); + assertNotNull("Unable to locate Log.class resource", resource); + + // getResource where it is accessable only to child classloader + resource = childLoader.getResource("org/apache/commons/logging/PathableTestSuite.class"); + assertNotNull("Unable to locate PathableTestSuite.class resource", resource); + + // getResource where it is accessable to both classloaders. The one visible + // to the child should be returned. The URL returned will be of form + // jar:file:/x/y.jar!path/to/resource. The filename part should include the jarname + // of form commons-logging-adapters-nnnn.jar, not commons-logging-nnnn.jar + resource = childLoader.getResource("org/apache/commons/logging/impl/Log4J12Logger.class"); + assertNotNull("Unable to locate Log4J12Logger.class resource", resource); + assertTrue("Incorrect source for Log4J12Logger class", + resource.toString().indexOf("/commons-logging-adapters-1.") > 0); + } + + /** + * Test that the various flavours of ClassLoader.getResources work as expected. + */ + public void testResources() throws Exception { + Enumeration resources; + URL[] urls; + + // verify the classloader hierarchy + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader childLoader = contextLoader.getParent(); + ClassLoader parentLoader = childLoader.getParent(); + ClassLoader bootLoader = parentLoader.getParent(); + assertNull("Unexpected classloader hierarchy", bootLoader); + + // getResources where no instances exist + resources = childLoader.getResources("nosuchfile"); + urls = toURLArray(resources); + assertEquals("Non-null URL returned for invalid resource name", 0, urls.length); + + // getResources where the resource only exists in the parent + resources = childLoader.getResources("org/apache/commons/logging/Log.class"); + urls = toURLArray(resources); + assertEquals("Unexpected number of Log.class resources found", 1, urls.length); + + // getResources where the resource only exists in the child + resources = childLoader.getResources("org/apache/commons/logging/PathableTestSuite.class"); + urls = toURLArray(resources); + assertEquals("Unexpected number of PathableTestSuite.class resources found", 1, urls.length); + + // getResources where the resource exists in both. + // resources should be returned in order (child-resource, parent-resource) + resources = childLoader.getResources("org/apache/commons/logging/impl/Log4J12Logger.class"); + urls = toURLArray(resources); + assertEquals("Unexpected number of Log4J12Logger.class resources found", 2, urls.length); + assertTrue("Incorrect source for Log4J12Logger class", + urls[0].toString().indexOf("/commons-logging-adapters-1.") > 0); + assertTrue("Incorrect source for Log4J12Logger class", + urls[1].toString().indexOf("/commons-logging-1.") > 0); + + } + + /** + * Utility method to convert an enumeration-of-URLs into an array of URLs. + */ + private static URL[] toURLArray(Enumeration e) { + ArrayList l = new ArrayList(); + while (e.hasMoreElements()) { + URL u = (URL) e.nextElement(); + l.add(u); + } + URL[] tmp = new URL[l.size()]; + return (URL[]) l.toArray(tmp); + } + + /** + * Test that getResourceAsStream works. + */ + public void testResourceAsStream() throws Exception { + java.io.InputStream is; + + // verify the classloader hierarchy + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader childLoader = contextLoader.getParent(); + ClassLoader parentLoader = childLoader.getParent(); + ClassLoader bootLoader = parentLoader.getParent(); + assertNull("Unexpected classloader hierarchy", bootLoader); + + // getResourceAsStream where no instances exist + is = childLoader.getResourceAsStream("nosuchfile"); + assertNull("Invalid resource returned non-null stream", is); + + // getResourceAsStream where resource does exist + is = childLoader.getResourceAsStream("org/apache/commons/logging/Log.class"); + assertNotNull("Null returned for valid resource", is); + is.close(); + + // It would be nice to test parent-first ordering here, but that would require + // having a resource with the same name in both the parent and child loaders, + // but with different contents. That's a little tricky to set up so we'll + // skip that for now. + } + + /** + * Verify that the context classloader is a custom one, then reset it to + * a non-custom one. + */ + private static void checkAndSetContext() { + ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); + assertEquals("ContextLoader is of unexpected type", + contextLoader.getClass().getName(), + PathableClassLoader.class.getName()); + + URL[] noUrls = new URL[0]; + Thread.currentThread().setContextClassLoader(new URLClassLoader(noUrls)); + } + + /** + * Verify that when a test method modifies the context classloader it is + * reset before the next test is run. + * <p> + * This method works in conjunction with testResetContext2. There is no + * way of knowing which test method junit will run first, but it doesn't + * matter; whichever one of them runs first will modify the contextClassloader. + * If the PathableTestSuite isn't resetting the contextClassLoader then whichever + * of them runs second will fail. Of course if other methods are run in-between + * then those methods might also fail... + */ + public void testResetContext1() { + checkAndSetContext(); + } + + /** + * See testResetContext1. + */ + public void testResetContext2() { + checkAndSetContext(); + } +} Propchange: jakarta/commons/proper/logging/trunk/src/test/org/apache/commons/logging/pathable/ChildFirstTestCase.java ------------------------------------------------------------------------------ svn:keywords = Id --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]