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
18package org.apache.harmony.tests.java.lang;
19
20import junit.framework.TestCase;
21
22import java.io.IOException;
23import java.io.InputStream;
24import java.net.JarURLConnection;
25import java.net.URL;
26import java.util.Arrays;
27import java.util.jar.JarFile;
28import libcore.io.Streams;
29
30public 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
41    public void setUp() throws Exception {
42        super.setUp();
43        testClassLoader = getClass().getClassLoader();
44    }
45
46    /**
47     * java.lang.ClassLoader#getSystemClassLoader()
48     */
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     */
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     */
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     */
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
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
134    public void test_testClassLoader_getResourceAsStream_withSharpChar() throws Exception {
135        assertGetResourceAsStreamNotNull(testClassLoader, ClassTest.SHARP_RESOURCE_ABS_NAME);
136    }
137
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
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
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
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
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
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
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}