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 parent-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 ParentFirstTestCase extends TestCase {
041:
042: /**
043: * Set up a custom classloader hierarchy for this test case.
044: * The hierarchy is:
045: * <ul>
046: * <li> contextloader: parent-first.
047: * <li> childloader: parent-first, used to load test case.
048: * <li> parentloader: parent-first, parent is the bootclassloader.
049: * </ul>
050: */
051: public static Test suite() throws Exception {
052: Class this Class = ParentFirstTestCase.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:
059: // Make the junit classes visible as a special case, as junit
060: // won't be able to call this class at all without this. The
061: // junit classes must be visible from the classloader that loaded
062: // this class, so use that as the source for future access to classes
063: // from the junit package.
064: parent.useExplicitLoader("junit.", this ClassLoader);
065:
066: // make the commons-logging.jar classes visible via the parent
067: parent.addLogicalLib("commons-logging");
068:
069: // create a child classloader to load the test case through
070: PathableClassLoader child = new PathableClassLoader(parent);
071:
072: // obviously, the child classloader needs to have the test classes
073: // in its path!
074: child.addLogicalLib("testclasses");
075: child.addLogicalLib("commons-logging-adapters");
076:
077: // create a third classloader to be the context classloader.
078: PathableClassLoader context = new PathableClassLoader(child);
079:
080: // reload this class via the child classloader
081: Class testClass = child.loadClass(this Class.getName());
082:
083: // and return our custom TestSuite class
084: return new PathableTestSuite(testClass, context);
085: }
086:
087: /**
088: * Test that the classloader hierarchy is as expected, and that
089: * calling loadClass() on various classloaders works as expected.
090: * Note that for this test case, parent-first classloading is
091: * in effect.
092: */
093: public void testPaths() throws Exception {
094: // the context classloader is not expected to be null
095: ClassLoader contextLoader = Thread.currentThread()
096: .getContextClassLoader();
097: assertNotNull("Context classloader is null", contextLoader);
098: assertEquals("Context classloader has unexpected type",
099: PathableClassLoader.class.getName(), contextLoader
100: .getClass().getName());
101:
102: // the classloader that loaded this class is obviously not null
103: ClassLoader this Loader = this .getClass().getClassLoader();
104: assertNotNull("thisLoader is null", this Loader);
105: assertEquals("thisLoader has unexpected type",
106: PathableClassLoader.class.getName(), this Loader
107: .getClass().getName());
108:
109: // the suite method specified that the context classloader's parent
110: // is the loader that loaded this test case.
111: assertSame("Context classloader is not child of thisLoader",
112: this Loader, contextLoader.getParent());
113:
114: // thisLoader's parent should be available
115: ClassLoader parentLoader = this Loader.getParent();
116: assertNotNull("Parent classloader is null", parentLoader);
117: assertEquals("Parent classloader has unexpected type",
118: PathableClassLoader.class.getName(), parentLoader
119: .getClass().getName());
120:
121: // parent should have a parent of null
122: assertNull("Parent classloader has non-null parent",
123: parentLoader.getParent());
124:
125: // getSystemClassloader is not a PathableClassLoader; it's of a
126: // built-in type. This also verifies that system classloader is none of
127: // (context, child, parent).
128: ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
129: assertNotNull("System classloader is null", systemLoader);
130: assertFalse("System classloader has unexpected type",
131: PathableClassLoader.class.getName().equals(
132: systemLoader.getClass().getName()));
133:
134: // junit classes should be visible; their classloader is system.
135: // this will of course throw an exception if not found.
136: Class junitTest = contextLoader
137: .loadClass("junit.framework.Test");
138: assertSame("Junit not loaded via systemloader", systemLoader,
139: junitTest.getClassLoader());
140:
141: // jcl api classes should be visible only via the parent
142: Class logClass = contextLoader
143: .loadClass("org.apache.commons.logging.Log");
144: assertSame("Log class not loaded via parent", logClass
145: .getClassLoader(), parentLoader);
146:
147: // jcl adapter classes should be visible via both parent and child. However
148: // as the classloaders are parent-first we should see the parent one.
149: Class log4jClass = contextLoader
150: .loadClass("org.apache.commons.logging.impl.Log4JLogger");
151: assertSame("Log4JLogger not loaded via parent", log4jClass
152: .getClassLoader(), parentLoader);
153:
154: // test classes should be visible via the child only
155: Class testClass = contextLoader
156: .loadClass("org.apache.commons.logging.PathableTestSuite");
157: assertSame("PathableTestSuite not loaded via child", testClass
158: .getClassLoader(), this Loader);
159:
160: // test loading of class that is not available
161: try {
162: Class noSuchClass = contextLoader
163: .loadClass("no.such.class");
164: fail("Class no.such.class is unexpectedly available");
165: } catch (ClassNotFoundException ex) {
166: // ok
167: }
168:
169: // String class classloader is null
170: Class stringClass = contextLoader.loadClass("java.lang.String");
171: assertNull("String class classloader is not null!", stringClass
172: .getClassLoader());
173: }
174:
175: /**
176: * Test that the various flavours of ClassLoader.getResource work as expected.
177: */
178: public void testResource() {
179: URL resource;
180:
181: ClassLoader contextLoader = Thread.currentThread()
182: .getContextClassLoader();
183: ClassLoader childLoader = contextLoader.getParent();
184:
185: // getResource where it doesn't exist
186: resource = childLoader.getResource("nosuchfile");
187: assertNull("Non-null URL returned for invalid resource name",
188: resource);
189:
190: // getResource where it is accessable only to parent classloader
191: resource = childLoader
192: .getResource("org/apache/commons/logging/Log.class");
193: assertNotNull("Unable to locate Log.class resource", resource);
194:
195: // getResource where it is accessable only to child classloader
196: resource = childLoader
197: .getResource("org/apache/commons/logging/PathableTestSuite.class");
198: assertNotNull(
199: "Unable to locate PathableTestSuite.class resource",
200: resource);
201:
202: // getResource where it is accessable to both classloaders. The one visible
203: // to the parent should be returned. The URL returned will be of form
204: // jar:file:/x/y.jar!path/to/resource. The filename part should include the jarname
205: // of form commons-logging-nnnn.jar, not commons-logging-adapters-nnnn.jar
206: resource = childLoader
207: .getResource("org/apache/commons/logging/impl/Log4JLogger.class");
208: assertNotNull("Unable to locate Log4JLogger.class resource",
209: resource);
210: assertTrue("Incorrect source for Log4JLogger class", resource
211: .toString().indexOf("/commons-logging-1.") > 0);
212: }
213:
214: /**
215: * Test that the various flavours of ClassLoader.getResources work as expected.
216: */
217: public void testResources() throws Exception {
218: Enumeration resources;
219: URL[] urls;
220:
221: // verify the classloader hierarchy
222: ClassLoader contextLoader = Thread.currentThread()
223: .getContextClassLoader();
224: ClassLoader childLoader = contextLoader.getParent();
225: ClassLoader parentLoader = childLoader.getParent();
226: ClassLoader bootLoader = parentLoader.getParent();
227: assertNull("Unexpected classloader hierarchy", bootLoader);
228:
229: // getResources where no instances exist
230: resources = childLoader.getResources("nosuchfile");
231: urls = toURLArray(resources);
232: assertEquals("Non-null URL returned for invalid resource name",
233: 0, urls.length);
234:
235: // getResources where the resource only exists in the parent
236: resources = childLoader
237: .getResources("org/apache/commons/logging/Log.class");
238: urls = toURLArray(resources);
239: assertEquals("Unexpected number of Log.class resources found",
240: 1, urls.length);
241:
242: // getResources where the resource only exists in the child
243: resources = childLoader
244: .getResources("org/apache/commons/logging/PathableTestSuite.class");
245: urls = toURLArray(resources);
246: assertEquals(
247: "Unexpected number of PathableTestSuite.class resources found",
248: 1, urls.length);
249:
250: // getResources where the resource exists in both.
251: // resources should be returned in order (parent-resource, child-resource)
252: resources = childLoader
253: .getResources("org/apache/commons/logging/impl/Log4JLogger.class");
254: urls = toURLArray(resources);
255: assertEquals(
256: "Unexpected number of Log4JLogger.class resources found",
257: 2, urls.length);
258:
259: // There is no gaurantee about the ordering of results returned from getResources
260: // To make this test portable across JVMs, sort the string to give them a known order
261: String[] urlsToStrings = new String[2];
262: urlsToStrings[0] = urls[0].toString();
263: urlsToStrings[1] = urls[1].toString();
264: Arrays.sort(urlsToStrings);
265: assertTrue("Incorrect source for Log4JLogger class",
266: urlsToStrings[0].indexOf("/commons-logging-1.") > 0);
267: assertTrue("Incorrect source for Log4JLogger class",
268: urlsToStrings[1]
269: .indexOf("/commons-logging-adapters-1.") > 0);
270:
271: }
272:
273: /**
274: * Utility method to convert an enumeration-of-URLs into an array of URLs.
275: */
276: private static URL[] toURLArray(Enumeration e) {
277: ArrayList l = new ArrayList();
278: while (e.hasMoreElements()) {
279: URL u = (URL) e.nextElement();
280: l.add(u);
281: }
282: URL[] tmp = new URL[l.size()];
283: return (URL[]) l.toArray(tmp);
284: }
285:
286: /**
287: * Test that getResourceAsStream works.
288: */
289: public void testResourceAsStream() throws Exception {
290: java.io.InputStream is;
291:
292: // verify the classloader hierarchy
293: ClassLoader contextLoader = Thread.currentThread()
294: .getContextClassLoader();
295: ClassLoader childLoader = contextLoader.getParent();
296: ClassLoader parentLoader = childLoader.getParent();
297: ClassLoader bootLoader = parentLoader.getParent();
298: assertNull("Unexpected classloader hierarchy", bootLoader);
299:
300: // getResourceAsStream where no instances exist
301: is = childLoader.getResourceAsStream("nosuchfile");
302: assertNull("Invalid resource returned non-null stream", is);
303:
304: // getResourceAsStream where resource does exist
305: is = childLoader
306: .getResourceAsStream("org/apache/commons/logging/Log.class");
307: assertNotNull("Null returned for valid resource", is);
308: is.close();
309:
310: // It would be nice to test parent-first ordering here, but that would require
311: // having a resource with the same name in both the parent and child loaders,
312: // but with different contents. That's a little tricky to set up so we'll
313: // skip that for now.
314: }
315: }
|