1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package libcore.dalvik.system; 18 19 import junit.framework.TestCase; 20 import libcore.io.Streams; 21 22 import java.io.File; 23 import java.io.InputStream; 24 import java.lang.reflect.Method; 25 import java.net.URL; 26 import java.nio.charset.StandardCharsets; 27 import java.util.ArrayList; 28 import java.util.Arrays; 29 import java.util.Enumeration; 30 import java.util.List; 31 import java.util.Map; 32 33 import dalvik.system.DelegateLastClassLoader; 34 import dalvik.system.PathClassLoader; 35 36 public class DelegateLastClassLoaderTest extends TestCase { 37 38 // NOTE: the jar files used by this test are created by create_test_jars.sh in the same 39 // directory as this test. 40 // 41 // Contents of child.jar 42 // ---------------------------------------------------- 43 // A.java 44 // ------ 45 // package libcore.test.delegatelast; 46 // public class A { 47 // public String toString() { 48 // return "A_child"; 49 // } 50 // } 51 // 52 // Child.java 53 // ---------- 54 // package libcore.test.delegatelast; 55 // 56 // public class Child { 57 // public String toString() { 58 // return "Child_child"; 59 // } 60 // } 61 // 62 // Contents of parent.jar 63 // ---------------------------------------------------- 64 // A.java 65 // ------ 66 // package libcore.test.delegatelast; 67 // public class A { 68 // public String toString() { 69 // return "A_parent"; 70 // } 71 // } 72 // 73 // Parent.java 74 // ---------- 75 // package libcore.test.delegatelast; 76 // 77 // public class Parent { 78 // public String toString() { 79 // return "Parent_parent"; 80 // } 81 // } 82 83 private Map<String, File> resourcesMap; 84 85 @Override 86 public void setUp() throws Exception { 87 resourcesMap = ClassLoaderTestSupport.setupAndCopyResources( 88 Arrays.asList("parent.jar", "child.jar", "bootoverride.jar")); 89 } 90 91 @Override 92 public void tearDown() throws Exception { 93 ClassLoaderTestSupport.cleanUpResources(resourcesMap); 94 } 95 96 private ClassLoader createClassLoader(String parentName, String thisName) { 97 File parentPath = resourcesMap.get(parentName); 98 File thisPath = resourcesMap.get(thisName); 99 assertNotNull(parentPath); 100 assertNotNull(thisPath); 101 102 ClassLoader parent = new PathClassLoader(parentPath.getAbsolutePath(), 103 Object.class.getClassLoader()); 104 105 return new DelegateLastClassLoader(thisPath.getAbsolutePath(), parent); 106 } 107 108 private static String callMethod(ClassLoader cl, String name) throws Exception { 109 Class<?> clazz = cl.loadClass("libcore.test.delegatelast.A"); 110 assertSame(cl, clazz.getClassLoader()); 111 112 Method method = clazz.getMethod(name); 113 Object obj = clazz.newInstance(); 114 return (String) method.invoke(obj); 115 } 116 117 private static String readResource(ClassLoader cl, String resourceName) throws Exception { 118 InputStream in = cl.getResourceAsStream(resourceName); 119 assertNotNull(in); 120 121 byte[] contents = Streams.readFully(in); 122 return new String(contents, StandardCharsets.UTF_8); 123 } 124 125 private static List<String> readResources(ClassLoader cl, String resourceName) 126 throws Exception { 127 Enumeration<URL> resources = cl.getResources(resourceName); 128 129 List<String> contents = new ArrayList<>(); 130 while (resources.hasMoreElements()) { 131 URL url = resources.nextElement(); 132 133 try (InputStream is = url.openStream()) { 134 byte[] bytes = Streams.readFully(is); 135 contents.add(new String(bytes, StandardCharsets.UTF_8)); 136 } 137 } 138 139 return contents; 140 } 141 142 public void testLookupOrder_loadClass() throws Exception { 143 ClassLoader delegate = createClassLoader("parent.jar", "child.jar"); 144 assertEquals("A_child", callMethod(delegate, "toString")); 145 146 // Note that the order is reversed here. "parent" is looked up before "child". 147 delegate = createClassLoader("child.jar", "parent.jar"); 148 assertEquals("A_parent", callMethod(delegate, "toString")); 149 } 150 151 public void testLookupOrder_foundInParent() throws Exception { 152 ClassLoader delegate = createClassLoader("parent.jar", "child.jar"); 153 154 Class<?> child = delegate.loadClass("libcore.test.delegatelast.Child"); 155 assertSame(delegate, child.getClassLoader()); 156 157 Class<?> parent = delegate.loadClass("libcore.test.delegatelast.Parent"); 158 assertSame(delegate.getParent(), parent.getClassLoader()); 159 } 160 161 public void testLoadClass_exceptionMessages() throws Exception { 162 ClassLoader delegate = createClassLoader("parent.jar", "child.jar"); 163 164 ClassNotFoundException msgFromDelegate = null; 165 try { 166 delegate.loadClass("libcore.test.delegatelast.NonExistent"); 167 fail(); 168 } catch (ClassNotFoundException ex) { 169 msgFromDelegate = ex; 170 } 171 172 ClassLoader pathClassLoader = new PathClassLoader( 173 resourcesMap.get("child.jar").getAbsolutePath(), null, null); 174 175 ClassNotFoundException msgFromPathClassloader = null; 176 try { 177 pathClassLoader.loadClass("libcore.test.delegatelast.NonExistent"); 178 fail(); 179 } catch (ClassNotFoundException ex) { 180 msgFromPathClassloader = ex; 181 } 182 183 assertEquals(msgFromPathClassloader.getMessage(), msgFromDelegate.getMessage()); 184 } 185 186 public void testLookupOrder_getResource() throws Exception { 187 ClassLoader delegate = createClassLoader("parent.jar", "child.jar"); 188 assertEquals("child", readResource(delegate, "resource.txt")); 189 190 delegate = createClassLoader("child.jar", "parent.jar"); 191 assertEquals("parent", readResource(delegate, "resource.txt")); 192 } 193 194 public void testLookupOrder_getResources() throws Exception { 195 ClassLoader delegate = createClassLoader("parent.jar", "child.jar"); 196 List<String> resources = readResources(delegate, "resource.txt"); 197 198 assertEquals(2, resources.size()); 199 assertEquals("child", resources.get(0)); 200 assertEquals("parent", resources.get(1)); 201 202 delegate = createClassLoader("child.jar", "parent.jar"); 203 resources = readResources(delegate, "resource.txt"); 204 205 assertEquals(2, resources.size()); 206 assertEquals("parent", resources.get(0)); 207 assertEquals("child", resources.get(1)); 208 } 209 210 public void testLookupOrder_bootOverride() throws Exception { 211 // The dex file in this jar contains a single class ("java.util.HashMap") and a single 212 // resource ("android/icu/ICUConfig.properties"). Both of these appear in the boot 213 // classpath as well. 214 File override = resourcesMap.get("bootoverride.jar"); 215 assertNotNull(override); 216 217 ClassLoader cl = new DelegateLastClassLoader(override.getAbsolutePath(), null); 218 219 Class<?> clazz = cl.loadClass("java.util.HashMap"); 220 // This should be loaded by the boot classloader and not "cl". 221 assertNotSame(cl, clazz.getClassLoader()); 222 223 String resource = readResource(cl, "android/icu/ICUConfig.properties"); 224 assertFalse("NOT ICU".equals(resource)); 225 226 List<String> resources = readResources(cl, "android/icu/ICUConfig.properties"); 227 assertEquals(2, resources.size()); 228 assertFalse("NOT ICU".equals(resources.get(0))); 229 assertTrue("NOT ICU".equals(resources.get(1))); 230 } 231 } 232