001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.geronimo.kernel.config;
017:
018: import java.io.File;
019: import java.io.Serializable;
020: import java.io.InputStream;
021: import java.io.IOException;
022: import java.io.FileOutputStream;
023: import java.net.URLClassLoader;
024: import java.net.URL;
025: import java.util.jar.JarFile;
026: import java.util.jar.JarOutputStream;
027: import java.util.jar.JarEntry;
028: import java.util.Enumeration;
029:
030: import junit.framework.TestCase;
031: import net.sf.cglib.proxy.Enhancer;
032: import net.sf.cglib.proxy.NoOp;
033: import net.sf.cglib.core.NamingPolicy;
034: import net.sf.cglib.core.Predicate;
035: import net.sf.cglib.core.DefaultGeneratorStrategy;
036: import org.apache.geronimo.kernel.repository.Artifact;
037:
038: /**
039: * @version $Rev: 547312 $ $Date: 2007-06-14 09:58:16 -0700 (Thu, 14 Jun 2007) $
040: */
041: public class MultiParentClassLoaderTest extends TestCase {
042: private static final String CLASS_NAME = "TestClass";
043: private static final String ENTRY_NAME = "foo";
044: private static final String ENTRY_VALUE = "bar";
045: private File[] files;
046: private static final String NON_EXISTANT_RESOURCE = "non-existant-resource";
047: private static final String NON_EXISTANT_CLASS = "NonExistant.class";
048: private URLClassLoader[] parents;
049: private MultiParentClassLoader classLoader;
050: private static final Artifact NAME = new Artifact("test", "fake",
051: "1.0", "car");
052:
053: /**
054: * Verify that the test jars are valid
055: * @throws Exception
056: */
057: public void testTestJars() throws Exception {
058: for (int i = 0; i < files.length; i++) {
059: File file = files[i];
060: JarFile jarFile = new JarFile(files[i]);
061: String urlString = "jar:" + file.toURL() + "!/"
062: + ENTRY_NAME;
063: URL url = new URL(files[i].toURL(), urlString);
064: assertStreamContains(ENTRY_VALUE + i, url.openStream());
065: jarFile.close();
066:
067: URLClassLoader urlClassLoader = new URLClassLoader(
068: new URL[] { file.toURL() });
069: // clazz shared by all
070: Class clazz = urlClassLoader.loadClass(CLASS_NAME);
071: assertNotNull(clazz);
072: assertTrue(clazz instanceof Serializable);
073:
074: // clazz specific to this jar
075: clazz = urlClassLoader.loadClass(CLASS_NAME + i);
076: assertNotNull(clazz);
077: assertTrue(clazz instanceof Serializable);
078:
079: // resource shared by all jars
080: InputStream in = urlClassLoader
081: .getResourceAsStream(ENTRY_NAME);
082: assertStreamContains("Should have found value from parent "
083: + i, ENTRY_VALUE + i, in);
084: in.close();
085:
086: // resource specific to this jar
087: in = urlClassLoader.getResourceAsStream(ENTRY_NAME + i);
088: assertStreamContains("Should have found value from parent "
089: + i, ENTRY_VALUE + i + ENTRY_VALUE, in);
090: in.close();
091: }
092: }
093:
094: /**
095: * Verify the get name method returns the name provided to the constructor.
096: */
097: public void testGetName() {
098: assertEquals(NAME, classLoader.getId());
099: }
100:
101: /**
102: * Verufy that the getParents method returns a different array from the one passed to the constructor and that the
103: * parents are in the same order.
104: */
105: public void testGetParents() {
106: ClassLoader[] actualParents = classLoader.getParents();
107: assertNotSame(parents, actualParents);
108: assertEquals(parents.length, actualParents.length);
109: for (int i = 0; i < actualParents.length; i++) {
110: assertEquals(parents[i], actualParents[i]);
111: }
112: }
113:
114: /**
115: * Test loadClass loads in preference of the parents, in order, and then the local urls.
116: * @throws Exception if a problem occurs
117: */
118: public void testLoadClass() throws Exception {
119: // load class specific to my class loader
120: Class clazz = classLoader.loadClass(CLASS_NAME + 33);
121: assertNotNull(clazz);
122: assertTrue(clazz instanceof Serializable);
123: assertEquals(classLoader, clazz.getClassLoader());
124:
125: // load class specific to each parent class loader
126: for (int i = 0; i < parents.length; i++) {
127: URLClassLoader parent = parents[i];
128: clazz = classLoader.loadClass(CLASS_NAME + i);
129: assertNotNull(clazz);
130: assertTrue(clazz instanceof Serializable);
131: assertEquals(parent, clazz.getClassLoader());
132: }
133:
134: // class shared by all class loaders
135: clazz = classLoader.loadClass(CLASS_NAME);
136: assertNotNull(clazz);
137: assertTrue(clazz instanceof Serializable);
138: assertEquals(parents[0], clazz.getClassLoader());
139: }
140:
141: public void testInverseClassLoading() throws Exception {
142: File parentJar = createJarFile(0);
143: ClassLoader parentCl = new URLClassLoader(new URL[] { parentJar
144: .toURL() });
145: File myJar = createJarFile(1);
146: ClassLoader cl = new MultiParentClassLoader(NAME,
147: new URL[] { myJar.toURL() }, parentCl);
148: Class clazz = cl.loadClass(CLASS_NAME);
149: assertSame(parentCl, clazz.getClassLoader());
150:
151: cl = new MultiParentClassLoader(NAME,
152: new URL[] { myJar.toURL() }, parentCl, true,
153: new String[0], new String[0]);
154: clazz = cl.loadClass(CLASS_NAME);
155: assertSame(cl, clazz.getClassLoader());
156: }
157:
158: public void testHiddenClasses() throws Exception {
159: File parentJar = createJarFile(0);
160: ClassLoader parentCl = new URLClassLoader(new URL[] { parentJar
161: .toURL() });
162: File myJar = createJarFile(1);
163: ClassLoader cl = new MultiParentClassLoader(NAME,
164: new URL[] { myJar.toURL() }, parentCl);
165: Class clazz = cl.loadClass(CLASS_NAME);
166: assertSame(parentCl, clazz.getClassLoader());
167:
168: cl = new MultiParentClassLoader(NAME,
169: new URL[] { myJar.toURL() }, parentCl, false,
170: new String[] { CLASS_NAME }, new String[0]);
171: clazz = cl.loadClass(CLASS_NAME);
172: assertSame(cl, clazz.getClassLoader());
173: }
174:
175: public void testNonOverridableClasses() throws Exception {
176: File parentJar = createJarFile(0);
177: ClassLoader parentCl = new URLClassLoader(new URL[] { parentJar
178: .toURL() });
179: File myJar = createJarFile(1);
180: ClassLoader cl = new MultiParentClassLoader(NAME,
181: new URL[] { myJar.toURL() }, parentCl);
182: Class clazz = cl.loadClass(CLASS_NAME);
183: assertSame(parentCl, clazz.getClassLoader());
184:
185: cl = new MultiParentClassLoader(NAME,
186: new URL[] { myJar.toURL() }, parentCl, true,
187: new String[0], new String[] { CLASS_NAME });
188: clazz = cl.loadClass(CLASS_NAME);
189: assertSame(parentCl, clazz.getClassLoader());
190: }
191:
192: /**
193: * Test that an attempt to load a non-existant class causes a ClassNotFoundException.
194: */
195: public void testLoadNonExistantClass() {
196: try {
197: classLoader.loadClass(NON_EXISTANT_CLASS);
198: fail("loadClass should have thrown a ClassNotFoundException");
199: } catch (ClassNotFoundException e) {
200: // expected
201: }
202: }
203:
204: /**
205: * Test getResourceAsStream loads in preference of the parents, in order, and then the local urls.
206: * @throws Exception if a problem occurs
207: */
208: public void testGetResourceAsStream() throws Exception {
209: InputStream in = classLoader
210: .getResourceAsStream(ENTRY_NAME + 33);
211: assertStreamContains("Should have found value from my file",
212: ENTRY_VALUE + 33 + ENTRY_VALUE, in);
213: in.close();
214:
215: for (int i = 0; i < parents.length; i++) {
216: in = classLoader.getResourceAsStream(ENTRY_NAME + i);
217: assertStreamContains("Should have found value from parent "
218: + i, ENTRY_VALUE + i + ENTRY_VALUE, in);
219: in.close();
220: }
221:
222: in = classLoader.getResourceAsStream(ENTRY_NAME);
223: assertStreamContains(
224: "Should have found value from first parent",
225: ENTRY_VALUE + 0, in);
226: in.close();
227: }
228:
229: /**
230: * Test getResourceAsStream returns null when attempt is made to loade a non-existant resource.
231: */
232: public void testGetNonExistantResourceAsStream() throws Exception {
233: InputStream in = classLoader
234: .getResourceAsStream(NON_EXISTANT_RESOURCE);
235: assertNull(in);
236: }
237:
238: /**
239: * Test getResource loads in preference of the parents, in order, and then the local urls.
240: * @throws Exception if a problem occurs
241: */
242: public void testGetResource() throws Exception {
243: URL resource = classLoader.getResource(ENTRY_NAME + 33);
244: assertURLContains("Should have found value from my file",
245: ENTRY_VALUE + 33 + ENTRY_VALUE, resource);
246:
247: for (int i = 0; i < parents.length; i++) {
248: resource = classLoader.getResource(ENTRY_NAME + i);
249: assertURLContains("Should have found value from parent "
250: + i, ENTRY_VALUE + i + ENTRY_VALUE, resource);
251: }
252:
253: resource = classLoader.getResource(ENTRY_NAME);
254: assertURLContains("Should have found value from first parent",
255: ENTRY_VALUE + 0, resource);
256: }
257:
258: /**
259: * Test getResource returns null when attempt is made to loade a non-existant resource.
260: */
261: public void testGetNonExistantResource() throws Exception {
262: URL resource = classLoader.getResource(NON_EXISTANT_RESOURCE);
263: assertNull(resource);
264: }
265:
266: /**
267: * Test getResource returns an enumeration in preference of the parents, in order, and then the local urls.
268: * @throws Exception if a problem occurs
269: */
270: public void testGetResources() throws Exception {
271: Enumeration resources = classLoader.getResources(ENTRY_NAME);
272: assertNotNull(resources);
273: assertTrue(resources.hasMoreElements());
274:
275: // there should be one entry for each parent
276: for (int i = 0; i < parents.length; i++) {
277: URL resource = (URL) resources.nextElement();
278: assertURLContains("Should have found value from parent "
279: + i, ENTRY_VALUE + i, resource);
280: }
281:
282: // and one entry from my url
283: assertTrue(resources.hasMoreElements());
284: URL resource = (URL) resources.nextElement();
285: assertURLContains("Should have found value from my file",
286: ENTRY_VALUE + 33, resource);
287: }
288:
289: /**
290: * Test getResources returns an empty enumeration when attempt is made to loade a non-existant resource.
291: */
292: public void testGetNonExistantResources() throws Exception {
293: Enumeration resources = classLoader
294: .getResources(NON_EXISTANT_RESOURCE);
295: assertNotNull(resources);
296: assertFalse(resources.hasMoreElements());
297: }
298:
299: private void assertStreamContains(String expectedValue,
300: InputStream in) throws IOException {
301: assertStreamContains(null, expectedValue, in);
302: }
303:
304: private void assertStreamContains(String message,
305: String expectedValue, InputStream in) throws IOException {
306: String entryValue;
307: try {
308: StringBuffer stringBuffer = new StringBuffer();
309: byte[] bytes = new byte[4000];
310: for (int count = in.read(bytes); count != -1; count = in
311: .read(bytes)) {
312: stringBuffer.append(new String(bytes, 0, count));
313: }
314: entryValue = stringBuffer.toString();
315: } finally {
316: in.close();
317: }
318: assertEquals(message, expectedValue, entryValue);
319: }
320:
321: private void assertURLContains(String message,
322: String expectedValue, URL resource) throws IOException {
323: InputStream in;
324: assertNotNull(resource);
325: in = resource.openStream();
326: assertStreamContains(message, expectedValue, in);
327: }
328:
329: private static void assertFileExists(File file) {
330: assertTrue("File should exist: " + file, file.canRead());
331: }
332:
333: protected void setUp() throws Exception {
334: super .setUp();
335: files = new File[3];
336: for (int i = 0; i < files.length; i++) {
337: files[i] = createJarFile(i);
338: }
339:
340: parents = new URLClassLoader[3];
341: for (int i = 0; i < parents.length; i++) {
342: parents[i] = new URLClassLoader(new URL[] { files[i]
343: .toURL() });
344: }
345:
346: File myFile = createJarFile(33);
347: classLoader = new MultiParentClassLoader(NAME,
348: new URL[] { myFile.toURL() }, parents);
349: }
350:
351: private static File createJarFile(int i) throws IOException {
352: File file = File.createTempFile("test-" + i + "-", ".jar");
353: file.deleteOnExit();
354:
355: FileOutputStream out = new FileOutputStream(file);
356: JarOutputStream jarOut = new JarOutputStream(out);
357:
358: // common class shared by everyone
359: jarOut.putNextEntry(new JarEntry(CLASS_NAME + ".class"));
360: jarOut.write(createClass(CLASS_NAME));
361:
362: // class only available in this jar
363: jarOut.putNextEntry(new JarEntry(CLASS_NAME + i + ".class"));
364: jarOut.write(createClass(CLASS_NAME + i));
365:
366: // common resource shared by everyone
367: jarOut.putNextEntry(new JarEntry(ENTRY_NAME));
368: jarOut.write((ENTRY_VALUE + i).getBytes());
369:
370: // resource only available in this jar
371: jarOut.putNextEntry(new JarEntry(ENTRY_NAME + i));
372: jarOut.write((ENTRY_VALUE + i + ENTRY_VALUE).getBytes());
373:
374: jarOut.close();
375: out.close();
376:
377: assertFileExists(file);
378: return file;
379: }
380:
381: private static byte[] createClass(final String name) {
382: Enhancer enhancer = new Enhancer();
383: enhancer.setNamingPolicy(new NamingPolicy() {
384: public String getClassName(String prefix, String source,
385: Object key, Predicate names) {
386: return name;
387: }
388: });
389: enhancer.setClassLoader(new URLClassLoader(new URL[0]));
390: enhancer.setSuperclass(Object.class);
391: enhancer.setInterfaces(new Class[] { Serializable.class });
392: enhancer.setCallbackTypes(new Class[] { NoOp.class });
393: enhancer.setUseFactory(false);
394: ByteCode byteCode = new ByteCode();
395: enhancer.setStrategy(byteCode);
396: enhancer.createClass();
397:
398: return byteCode.getByteCode();
399: }
400:
401: protected void tearDown() throws Exception {
402: for (int i = 0; i < files.length; i++) {
403: files[i].delete();
404: // assertFileNotExists(files[i]);
405: }
406: super .tearDown();
407: }
408:
409: private static class ByteCode extends DefaultGeneratorStrategy {
410: private byte[] byteCode;
411:
412: public byte[] transform(byte[] byteCode) {
413: this .byteCode = byteCode;
414: return byteCode;
415: }
416:
417: public byte[] getByteCode() {
418: return byteCode;
419: }
420: }
421: }
|