1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package org.apache.harmony.tests.java.lang;
19 
20 import junit.framework.TestCase;
21 
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.net.JarURLConnection;
25 import java.net.URL;
26 import java.util.Arrays;
27 import java.util.jar.JarFile;
28 import libcore.io.Streams;
29 
30 public class ClassLoaderTest extends TestCase {
31 
32     /** A resource known to be present in the boot classpath. */
33     private static final String BOOT_RESOURCE_NAME = "java/util/logging/logging.properties";
34 
35     /** A resource known to be present in the classpath associated with the test class. */
36     private static final String TEST_RESOURCE_NAME = ClassTest.RESOURCE_ABS_NAME;
37 
38     private ClassLoader testClassLoader;
39 
40     @Override
setUp()41     public void setUp() throws Exception {
42         super.setUp();
43         testClassLoader = getClass().getClassLoader();
44     }
45 
46     /**
47      * java.lang.ClassLoader#getSystemClassLoader()
48      */
test_getSystemClassLoader()49     public void test_getSystemClassLoader() {
50         // Test for method java.lang.ClassLoader
51         // java.lang.ClassLoader.getSystemClassLoader()
52         ClassLoader cl = ClassLoader.getSystemClassLoader();
53         assertNotNull(cl);
54 
55         // The SystemClassLoader's parent should be the Boot classloader, which is used to load
56         // the various libcore classes.
57         assertNotNull(cl.getParent());
58         Class<?> libcoreClass = Integer.class;
59         assertSame(cl.getParent(), libcoreClass.getClassLoader());
60 
61         // It is difficult to test further because the CTS tests run as an instrumented TestCase.
62         // Android apps do not have a system classpath, and rely on an application classloader to
63         // load app classes and resources, not the System ClassLoader. The System ClassLoader is not
64         // usually the parent of the application class loader.
65     }
66 
67     /**
68      * java.lang.ClassLoader#getSystemResource(java.lang.String)
69      */
test_getSystemResourceLjava_lang_String()70     public void test_getSystemResourceLjava_lang_String() {
71         // Test for method java.net.URL
72         // java.lang.ClassLoader.getSystemResource(java.lang.String)
73 
74         // It is difficult to test this because the CTS tests run as an instrumented TestCase.
75         // Android apps do not have a system classpath, and rely on an application classloader to
76         // load app classes and resources, not the System ClassLoader.
77     }
78 
79     /**
80      * java.lang.ClassLoader#getResource(java.lang.String)
81      */
test_testClassLoader_getResourceLjava_lang_String()82     public void test_testClassLoader_getResourceLjava_lang_String() {
83         // Test for method java.net.URL
84         // java.lang.ClassLoader.getResource(java.lang.String)
85 
86         // Test basic class loader behavior for the ClassLoader that was used to load the test
87         // class while being deliberately vague about which classloader it actually is.
88 
89         ClassLoader parentClassLoader = testClassLoader.getParent();
90         assertNull(parentClassLoader.getResource(TEST_RESOURCE_NAME));
91         assertGetResourceIsValid(parentClassLoader, BOOT_RESOURCE_NAME);
92 
93         assertGetResourceIsValid(testClassLoader, TEST_RESOURCE_NAME);
94         assertGetResourceIsValid(testClassLoader, BOOT_RESOURCE_NAME);
95     }
96 
97     /**
98      * java.lang.ClassLoader#getResourceAsStream(java.lang.String)
99      */
test_testClassLoader_getResourceAsStreamLjava_lang_String()100     public void test_testClassLoader_getResourceAsStreamLjava_lang_String() throws Exception {
101         // Test for method java.io.InputStream
102         // java.lang.ClassLoader.getResourceAsStream(java.lang.String)
103 
104         // Test basic class loader behavior for the ClassLoader that was used to load the test
105         // class while being deliberately vague about which classloader it actually is.
106 
107         ClassLoader parentClassLoader = testClassLoader.getParent();
108         assertGetResourceAsStreamNotNull(parentClassLoader, BOOT_RESOURCE_NAME);
109         assertNull(parentClassLoader.getResourceAsStream(TEST_RESOURCE_NAME));
110 
111         assertGetResourceAsStreamNotNull(testClassLoader, BOOT_RESOURCE_NAME);
112         assertGetResourceAsStreamNotNull(testClassLoader, TEST_RESOURCE_NAME);
113     }
114 
test_testClassLoader_loadClass()115     public void test_testClassLoader_loadClass() throws Exception {
116         // Test basic class loader behavior for the ClassLoader that was used to load the test
117         // class while being deliberately vague about which classloader it actually is.
118         String integerClassName = Integer.class.getName();
119         String testClassName = ClassLoaderTest.class.getName();
120 
121         ClassLoader parentClassLoader = testClassLoader.getParent();
122         assertSame(Integer.class, parentClassLoader.loadClass(integerClassName));
123         try {
124             parentClassLoader.loadClass(testClassName);
125             fail();
126         } catch (ClassNotFoundException expected) {
127         }
128 
129         assertSame(Integer.class, testClassLoader.loadClass(integerClassName));
130         assertSame(this.getClass(), testClassLoader.loadClass(testClassName));
131     }
132 
133     //Regression Test for JIRA-2047
test_testClassLoader_getResourceAsStream_withSharpChar()134     public void test_testClassLoader_getResourceAsStream_withSharpChar() throws Exception {
135         assertGetResourceAsStreamNotNull(testClassLoader, ClassTest.SHARP_RESOURCE_ABS_NAME);
136     }
137 
testUncachedJarStreamBehavior()138     public void testUncachedJarStreamBehavior() throws Exception {
139         URL resourceFromJar = testClassLoader.getResource(TEST_RESOURCE_NAME);
140         JarURLConnection uncachedConnection = (JarURLConnection) resourceFromJar.openConnection();
141         uncachedConnection.setUseCaches(false);
142         JarFile uncachedJarFile = uncachedConnection.getJarFile();
143         InputStream is = uncachedConnection.getInputStream();
144         is.close();
145 
146         assertTrue("Closing the stream should close a cached connection",
147                 isJarUrlConnectClosed(uncachedConnection));
148 
149         // Closing the stream closes the JarFile.
150         assertTrue(isJarFileClosed(uncachedJarFile));
151     }
152 
testCachedJarStreamBehavior()153     public void testCachedJarStreamBehavior() throws Exception {
154         URL resourceFromJar = testClassLoader.getResource(TEST_RESOURCE_NAME);
155         JarURLConnection cachedConnection1 = (JarURLConnection) resourceFromJar.openConnection();
156         assertTrue(cachedConnection1.getUseCaches());
157 
158         JarURLConnection cachedConnection2 = (JarURLConnection) resourceFromJar.openConnection();
159         assertTrue(cachedConnection2.getUseCaches());
160 
161         InputStream is1 = cachedConnection1.getInputStream();
162         byte[] resourceData1 = Streams.readFullyNoClose(is1);
163         is1.close();
164         assertFalse("Closing the stream should not close a cached connection",
165                 isJarUrlConnectClosed(cachedConnection1));
166 
167         InputStream is2 = cachedConnection2.getInputStream();
168         byte[] resourceData2 = Streams.readFullyNoClose(is2);
169         is2.close();
170         assertFalse("Closing the stream should not close a cached connection",
171                 isJarUrlConnectClosed(cachedConnection2));
172 
173         assertEquals(Arrays.toString(resourceData1), Arrays.toString(resourceData2));
174     }
175 
testResourceJarFileBehavior()176     public void testResourceJarFileBehavior() throws Exception {
177         URL resourceFromJar = testClassLoader.getResource(TEST_RESOURCE_NAME);
178         JarURLConnection urlConnection1 = (JarURLConnection) resourceFromJar.openConnection();
179         assertTrue(urlConnection1.getUseCaches());
180 
181         JarURLConnection urlConnection2 = (JarURLConnection) resourceFromJar.openConnection();
182         assertTrue(urlConnection1.getUseCaches());
183         assertNotSame(urlConnection1, urlConnection2);
184 
185         JarURLConnection uncachedConnection = (JarURLConnection) resourceFromJar.openConnection();
186         assertNotSame(uncachedConnection, urlConnection2);
187         uncachedConnection.setUseCaches(false);
188 
189         JarFile jarFile1 = urlConnection1.getJarFile();
190         JarFile jarFile2 = urlConnection2.getJarFile();
191         // Note: This implies nobody should ever call JarFile.close() when caching is enabled.
192         // We cannot test this, because it will break later tests.
193         assertSame(jarFile1, jarFile2);
194 
195         JarFile uncachedJarFile = uncachedConnection.getJarFile();
196         assertNotSame(jarFile1, uncachedJarFile);
197         uncachedJarFile.close();
198 
199         assertFalse(isJarFileClosed(jarFile1));
200         assertTrue(isJarFileClosed(uncachedJarFile));
201     }
202 
assertGetResourceAsStreamNotNull(ClassLoader classLoader, String resourceName)203     private static void assertGetResourceAsStreamNotNull(ClassLoader classLoader,
204             String resourceName) throws IOException {
205         InputStream is = null;
206         try {
207             is = classLoader.getResourceAsStream(resourceName);
208             assertNotNull(is);
209         } finally {
210             if (is != null) {
211                 is.close();
212             }
213         }
214     }
215 
assertGetResourceIsValid(ClassLoader classLoader, String resourceName)216     private static void assertGetResourceIsValid(ClassLoader classLoader, String resourceName) {
217         java.net.URL u = classLoader.getResource(resourceName);
218         assertNotNull(u);
219         InputStream is = null;
220         try {
221             is = u.openStream();
222             assertNotNull(is);
223             is.close();
224         } catch (IOException e) {
225             fail("IOException getting stream for resource : " + e.getMessage());
226         }
227     }
228 
isJarFileClosed(JarFile jarFile)229     private static boolean isJarFileClosed(JarFile jarFile) {
230         // Indirectly detect that the JarFile has been closed.
231         try {
232             jarFile.getEntry("anyName");
233             return false;
234         } catch (IllegalStateException expected) {
235             return true;
236         }
237     }
238 
isJarUrlConnectClosed(JarURLConnection jarURLConnection)239     private static boolean isJarUrlConnectClosed(JarURLConnection jarURLConnection)
240             throws IOException {
241         // Indirectly detect that the jarURLConnection has been closed.
242         try {
243             jarURLConnection.getInputStream();
244             return false;
245         } catch (IllegalStateException e) {
246             return true;
247         }
248     }
249 }