From c340a77d5bfe15311366a7a5786309b09439b8f2 Mon Sep 17 00:00:00 2001 From: Simon Kitching Date: Tue, 1 Aug 2006 23:48:14 +0000 Subject: [PATCH] Manually force our custom java.util.logging.Handler class to be loaded via the system classloader. This means that the unit test can successfully run even when the class is not in the system classpath (as happens when running tests with maven2's surefire plugin for example). git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/logging/trunk@427800 13f79535-47bb-0310-9956-ffa450edef68 --- .../jdk14/CustomConfigAPITestCase.java | 8 +- .../jdk14/CustomConfigFullTestCase.java | 4 +- .../logging/jdk14/CustomConfigTestCase.java | 91 ++++++++++++++++--- 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/src/test/org/apache/commons/logging/jdk14/CustomConfigAPITestCase.java b/src/test/org/apache/commons/logging/jdk14/CustomConfigAPITestCase.java index 83f86d1..34431aa 100644 --- a/src/test/org/apache/commons/logging/jdk14/CustomConfigAPITestCase.java +++ b/src/test/org/apache/commons/logging/jdk14/CustomConfigAPITestCase.java @@ -19,8 +19,8 @@ package org.apache.commons.logging.jdk14; import junit.framework.Test; -import org.apache.commons.logging.PathableTestSuite; import org.apache.commons.logging.PathableClassLoader; +import org.apache.commons.logging.PathableTestSuite; /** @@ -30,12 +30,10 @@ import org.apache.commons.logging.PathableClassLoader; public class CustomConfigAPITestCase extends CustomConfigTestCase { - public CustomConfigAPITestCase(String name) { super(name); } - /** * Return the tests included in this test suite. */ @@ -48,7 +46,9 @@ public class CustomConfigAPITestCase extends CustomConfigTestCase { // be able to instantiate it. And this test case must see the same // class in order to be able to access its data. Yes this is ugly // but the whole jdk14 API is a ******* mess anyway. - parent.useSystemLoader("org.apache.commons.logging.jdk14.TestHandler"); + ClassLoader scl = ClassLoader.getSystemClassLoader(); + loadTestHandler(HANDLER_NAME, scl); + parent.useExplicitLoader(HANDLER_NAME, scl); parent.addLogicalLib("commons-logging-api"); PathableClassLoader child = new PathableClassLoader(parent); diff --git a/src/test/org/apache/commons/logging/jdk14/CustomConfigFullTestCase.java b/src/test/org/apache/commons/logging/jdk14/CustomConfigFullTestCase.java index 12bf8bd..310e9c6 100644 --- a/src/test/org/apache/commons/logging/jdk14/CustomConfigFullTestCase.java +++ b/src/test/org/apache/commons/logging/jdk14/CustomConfigFullTestCase.java @@ -49,7 +49,9 @@ public class CustomConfigFullTestCase extends CustomConfigTestCase { // be able to instantiate it. And this test case must see the same // class in order to be able to access its data. Yes this is ugly // but the whole jdk14 API is a ******* mess anyway. - parent.useSystemLoader("org.apache.commons.logging.jdk14.TestHandler"); + ClassLoader scl = ClassLoader.getSystemClassLoader(); + loadTestHandler(HANDLER_NAME, scl); + parent.useExplicitLoader(HANDLER_NAME, scl); parent.addLogicalLib("commons-logging"); PathableClassLoader child = new PathableClassLoader(parent); diff --git a/src/test/org/apache/commons/logging/jdk14/CustomConfigTestCase.java b/src/test/org/apache/commons/logging/jdk14/CustomConfigTestCase.java index c536197..d1f3ec9 100644 --- a/src/test/org/apache/commons/logging/jdk14/CustomConfigTestCase.java +++ b/src/test/org/apache/commons/logging/jdk14/CustomConfigTestCase.java @@ -18,7 +18,9 @@ package org.apache.commons.logging.jdk14; +import java.io.ByteArrayOutputStream; import java.io.InputStream; +import java.lang.reflect.Method; import java.util.Iterator; import java.util.logging.Handler; import java.util.logging.Level; @@ -27,7 +29,9 @@ import java.util.logging.LogRecord; import java.util.logging.Logger; import junit.framework.Test; -import junit.framework.TestSuite; + +import org.apache.commons.logging.PathableClassLoader; +import org.apache.commons.logging.PathableTestSuite; /** @@ -41,6 +45,8 @@ import junit.framework.TestSuite; public class CustomConfigTestCase extends DefaultConfigTestCase { + protected static final String HANDLER_NAME + = "org.apache.commons.logging.jdk14.TestHandler"; // ----------------------------------------------------------- Constructors @@ -99,6 +105,65 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { // ------------------------------------------- JUnit Infrastructure Methods + /** + * Given the name of a class that is somewhere in the classpath of the provided + * classloader, return the contents of the corresponding .class file. + */ + protected static byte[] readClass(String name, ClassLoader srcCL) throws Exception { + String resName = name.replace('.', '/') + ".class"; + System.err.println("Trying to load resource [" + resName + "]"); + InputStream is = srcCL.getResourceAsStream(resName); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + System.err.println("Reading resource [" + resName + "]"); + byte[] buf = new byte[1000]; + for(;;) { + int read = is.read(buf); + if (read <= 0) { + break; + } + baos.write(buf, 0, read); + } + is.close(); + return baos.toByteArray(); + } + + /** + * Make a class available in the system classloader even when its classfile is + * not present in the classpath configured for that classloader. This only + * works for classes for which all dependencies are already loaded in + * that classloader. + */ + protected static void loadTestHandler(String className, ClassLoader targetCL) { + try { + targetCL.loadClass(className); + // fail("Class already in target classloader"); + return; + } catch(ClassNotFoundException ex) { + // ok, go ahead and load it + } + + try { + ClassLoader srcCL = CustomConfigAPITestCase.class.getClassLoader(); + byte[] classData = readClass(className, srcCL); + + Class[] params = new Class[] { + String.class, classData.getClass(), + Integer.TYPE, Integer.TYPE}; + Method m = ClassLoader.class.getDeclaredMethod("defineClass", params); + + Object[] args = new Object[4]; + args[0] = className; + args[1] = classData; + args[2] = new Integer(0); + args[3] = new Integer(classData.length); + m.setAccessible(true); + m.invoke(targetCL, args); + } catch(Exception e) { + e.printStackTrace(); + fail("Unable to load class " + className); + } + } + /** * Set up instance variables required by this test case. */ @@ -116,18 +181,22 @@ public class CustomConfigTestCase extends DefaultConfigTestCase { * Return the tests included in this test suite. */ public static Test suite() throws Exception { - /* - PathableClassLoader loader = new PathableClassLoader(null); - loader.useSystemLoader("junit."); + PathableClassLoader cl = new PathableClassLoader(null); + cl.useExplicitLoader("junit.", Test.class.getClassLoader()); - PathableClassLoader child = new PathableClassLoader(parent); - child.addLogicalLib("testclasses"); - child.addLogicalLib("commons-logging"); + // the TestHandler class must be accessable from the System classloader + // in order for java.util.logging.LogManager.readConfiguration to + // be able to instantiate it. And this test case must see the same + // class in order to be able to access its data. Yes this is ugly + // but the whole jdk14 API is a ******* mess anyway. + ClassLoader scl = ClassLoader.getSystemClassLoader(); + loadTestHandler(HANDLER_NAME, scl); + cl.useExplicitLoader(HANDLER_NAME, scl); + cl.addLogicalLib("commons-logging"); + cl.addLogicalLib("testclasses"); - Class testClass = child.loadClass(CustomConfigTestCase.class.getName()); - return new PathableTestSuite(testClass, child); - */ - return new TestSuite(CustomConfigTestCase.class); + Class testClass = cl.loadClass(CustomConfigTestCase.class.getName()); + return new PathableTestSuite(testClass, cl); } /**