001: /*
002: * Copyright 2005 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.logging.pathable;
017:
018: import java.net.URL;
019: import java.util.ArrayList;
020: import java.util.Arrays;
021: import java.util.Enumeration;
022:
023: import junit.framework.Test;
024: import junit.framework.TestCase;
025:
026: import org.apache.commons.logging.PathableClassLoader;
027: import org.apache.commons.logging.PathableTestSuite;
028:
029: /**
030: * Tests for the PathableTestSuite and PathableClassLoader functionality,
031: * where lookup order for the PathableClassLoader is child-first.
032: * <p>
033: * These tests assume:
034: * <ul>
035: * <li>junit is in system classpath
036: * <li>nothing else is in system classpath
037: * </ul>
038: */
039:
040: public class ChildFirstTestCase extends TestCase {
041:
042: /**
043: * Set up a custom classloader hierarchy for this test case.
044: * The hierarchy is:
045: * <ul>
046: * <li> contextloader: child-first.
047: * <li> childloader: child-first, used to load test case.
048: * <li> parentloader: child-first, parent is the bootclassloader.
049: * </ul>
050: */
051: public static Test suite() throws Exception {
052: Class this Class = ChildFirstTestCase.class;
053: ClassLoader this ClassLoader = this Class.getClassLoader();
054:
055: // Make the parent a direct child of the bootloader to hide all
056: // other classes in the system classpath
057: PathableClassLoader parent = new PathableClassLoader(null);
058: parent.setParentFirst(false);
059:
060: // Make the junit classes visible as a special case, as junit
061: // won't be able to call this class at all without this. The
062: // junit classes must be visible from the classloader that loaded
063: // this class, so use that as the source for future access to classes
064: // from the junit package.
065: parent.useExplicitLoader("junit.", this ClassLoader);
066:
067: // Make the commons-logging.jar classes visible via the parent
068: parent.addLogicalLib("commons-logging");
069:
070: // Create a child classloader to load the test case through
071: PathableClassLoader child = new PathableClassLoader(parent);
072: child.setParentFirst(false);
073:
074: // Obviously, the child classloader needs to have the test classes
075: // in its path!
076: child.addLogicalLib("testclasses");
077: child.addLogicalLib("commons-logging-adapters");
078:
079: // Create a third classloader to be the context classloader.
080: PathableClassLoader context = new PathableClassLoader(child);
081: context.setParentFirst(false);
082:
083: // reload this class via the child classloader
084: Class testClass = child.loadClass(this Class.getName());
085:
086: // and return our custom TestSuite class
087: return new PathableTestSuite(testClass, context);
088: }
089:
090: /**
091: * Test that the classloader hierarchy is as expected, and that
092: * calling loadClass() on various classloaders works as expected.
093: * Note that for this test case, parent-first classloading is
094: * in effect.
095: */
096: public void testPaths() throws Exception {
097: // the context classloader is not expected to be null
098: ClassLoader contextLoader = Thread.currentThread()
099: .getContextClassLoader();
100: assertNotNull("Context classloader is null", contextLoader);
101: assertEquals("Context classloader has unexpected type",
102: PathableClassLoader.class.getName(), contextLoader
103: .getClass().getName());
104:
105: // the classloader that loaded this class is obviously not null
106: ClassLoader this Loader = this .getClass().getClassLoader();
107: assertNotNull("thisLoader is null", this Loader);
108: assertEquals("thisLoader has unexpected type",
109: PathableClassLoader.class.getName(), this Loader
110: .getClass().getName());
111:
112: // the suite method specified that the context classloader's parent
113: // is the loader that loaded this test case.
114: assertSame("Context classloader is not child of thisLoader",
115: this Loader, contextLoader.getParent());
116:
117: // thisLoader's parent should be available
118: ClassLoader parentLoader = this Loader.getParent();
119: assertNotNull("Parent classloader is null", parentLoader);
120: assertEquals("Parent classloader has unexpected type",
121: PathableClassLoader.class.getName(), parentLoader
122: .getClass().getName());
123:
124: // parent should have a parent of null
125: assertNull("Parent classloader has non-null parent",
126: parentLoader.getParent());
127:
128: // getSystemClassloader is not a PathableClassLoader; it's of a
129: // built-in type. This also verifies that system classloader is none of
130: // (context, child, parent).
131: ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
132: assertNotNull("System classloader is null", systemLoader);
133: assertFalse("System classloader has unexpected type",
134: PathableClassLoader.class.getName().equals(
135: systemLoader.getClass().getName()));
136:
137: // junit classes should be visible; their classloader is system.
138: // this will of course throw an exception if not found.
139: Class junitTest = contextLoader
140: .loadClass("junit.framework.Test");
141: assertSame("Junit not loaded via systemloader", systemLoader,
142: junitTest.getClassLoader());
143:
144: // jcl api classes should be visible only via the parent
145: Class logClass = contextLoader
146: .loadClass("org.apache.commons.logging.Log");
147: assertSame("Log class not loaded via parent", logClass
148: .getClassLoader(), parentLoader);
149:
150: // jcl adapter classes should be visible via both parent and child. However
151: // as the classloaders are child-first we should see the child one.
152: Class log4jClass = contextLoader
153: .loadClass("org.apache.commons.logging.impl.Log4JLogger");
154: assertSame("Log4JLogger not loaded via child", log4jClass
155: .getClassLoader(), this Loader);
156:
157: // test classes should be visible via the child only
158: Class testClass = contextLoader
159: .loadClass("org.apache.commons.logging.PathableTestSuite");
160: assertSame("PathableTestSuite not loaded via child", testClass
161: .getClassLoader(), this Loader);
162:
163: // test loading of class that is not available
164: try {
165: Class noSuchClass = contextLoader
166: .loadClass("no.such.class");
167: fail("Class no.such.class is unexpectedly available");
168: } catch (ClassNotFoundException ex) {
169: // ok
170: }
171:
172: // String class classloader is null
173: Class stringClass = contextLoader.loadClass("java.lang.String");
174: assertNull("String class classloader is not null!", stringClass
175: .getClassLoader());
176: }
177:
178: /**
179: * Test that the various flavours of ClassLoader.getResource work as expected.
180: */
181: public void testResource() {
182: URL resource;
183:
184: ClassLoader contextLoader = Thread.currentThread()
185: .getContextClassLoader();
186: ClassLoader childLoader = contextLoader.getParent();
187:
188: // getResource where it doesn't exist
189: resource = childLoader.getResource("nosuchfile");
190: assertNull("Non-null URL returned for invalid resource name",
191: resource);
192:
193: // getResource where it is accessable only to parent classloader
194: resource = childLoader
195: .getResource("org/apache/commons/logging/Log.class");
196: assertNotNull("Unable to locate Log.class resource", resource);
197:
198: // getResource where it is accessable only to child classloader
199: resource = childLoader
200: .getResource("org/apache/commons/logging/PathableTestSuite.class");
201: assertNotNull(
202: "Unable to locate PathableTestSuite.class resource",
203: resource);
204:
205: // getResource where it is accessable to both classloaders. The one visible
206: // to the child should be returned. The URL returned will be of form
207: // jar:file:/x/y.jar!path/to/resource. The filename part should include the jarname
208: // of form commons-logging-adapters-nnnn.jar, not commons-logging-nnnn.jar
209: resource = childLoader
210: .getResource("org/apache/commons/logging/impl/Log4JLogger.class");
211: assertNotNull("Unable to locate Log4JLogger.class resource",
212: resource);
213: assertTrue("Incorrect source for Log4JLogger class", resource
214: .toString().indexOf("/commons-logging-adapters-1.") > 0);
215: }
216:
217: /**
218: * Test that the various flavours of ClassLoader.getResources work as expected.
219: */
220: public void testResources() throws Exception {
221: Enumeration resources;
222: URL[] urls;
223:
224: // verify the classloader hierarchy
225: ClassLoader contextLoader = Thread.currentThread()
226: .getContextClassLoader();
227: ClassLoader childLoader = contextLoader.getParent();
228: ClassLoader parentLoader = childLoader.getParent();
229: ClassLoader bootLoader = parentLoader.getParent();
230: assertNull("Unexpected classloader hierarchy", bootLoader);
231:
232: // getResources where no instances exist
233: resources = childLoader.getResources("nosuchfile");
234: urls = toURLArray(resources);
235: assertEquals("Non-null URL returned for invalid resource name",
236: 0, urls.length);
237:
238: // getResources where the resource only exists in the parent
239: resources = childLoader
240: .getResources("org/apache/commons/logging/Log.class");
241: urls = toURLArray(resources);
242: assertEquals("Unexpected number of Log.class resources found",
243: 1, urls.length);
244:
245: // getResources where the resource only exists in the child
246: resources = childLoader
247: .getResources("org/apache/commons/logging/PathableTestSuite.class");
248: urls = toURLArray(resources);
249: assertEquals(
250: "Unexpected number of PathableTestSuite.class resources found",
251: 1, urls.length);
252:
253: // getResources where the resource exists in both.
254: // resources should be returned in order (child-resource, parent-resource).
255: //
256: // IMPORTANT: due to the fact that in java 1.4 and earlier method
257: // ClassLoader.getResources is final it isn't possible for PathableClassLoader
258: // to override this. So even when child-first is enabled the resource order
259: // is still (parent-resources, child-resources). This test verifies the expected
260: // behaviour - even though it's not the desired behaviour.
261:
262: resources = childLoader
263: .getResources("org/apache/commons/logging/impl/Log4JLogger.class");
264: urls = toURLArray(resources);
265: assertEquals(
266: "Unexpected number of Log4JLogger.class resources found",
267: 2, urls.length);
268:
269: // There is no gaurantee about the ordering of results returned from getResources
270: // To make this test portable across JVMs, sort the string to give them a known order
271: String[] urlsToStrings = new String[2];
272: urlsToStrings[0] = urls[0].toString();
273: urlsToStrings[1] = urls[1].toString();
274: Arrays.sort(urlsToStrings);
275: assertTrue("Incorrect source for Log4JLogger class",
276: urlsToStrings[0].indexOf("/commons-logging-1.") > 0);
277: assertTrue("Incorrect source for Log4JLogger class",
278: urlsToStrings[1]
279: .indexOf("/commons-logging-adapters-1.") > 0);
280: }
281:
282: /**
283: * Utility method to convert an enumeration-of-URLs into an array of URLs.
284: */
285: private static URL[] toURLArray(Enumeration e) {
286: ArrayList l = new ArrayList();
287: while (e.hasMoreElements()) {
288: URL u = (URL) e.nextElement();
289: l.add(u);
290: }
291: URL[] tmp = new URL[l.size()];
292: return (URL[]) l.toArray(tmp);
293: }
294:
295: /**
296: * Test that getResourceAsStream works.
297: */
298: public void testResourceAsStream() throws Exception {
299: java.io.InputStream is;
300:
301: // verify the classloader hierarchy
302: ClassLoader contextLoader = Thread.currentThread()
303: .getContextClassLoader();
304: ClassLoader childLoader = contextLoader.getParent();
305: ClassLoader parentLoader = childLoader.getParent();
306: ClassLoader bootLoader = parentLoader.getParent();
307: assertNull("Unexpected classloader hierarchy", bootLoader);
308:
309: // getResourceAsStream where no instances exist
310: is = childLoader.getResourceAsStream("nosuchfile");
311: assertNull("Invalid resource returned non-null stream", is);
312:
313: // getResourceAsStream where resource does exist
314: is = childLoader
315: .getResourceAsStream("org/apache/commons/logging/Log.class");
316: assertNotNull("Null returned for valid resource", is);
317: is.close();
318:
319: // It would be nice to test parent-first ordering here, but that would require
320: // having a resource with the same name in both the parent and child loaders,
321: // but with different contents. That's a little tricky to set up so we'll
322: // skip that for now.
323: }
324: }
|