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 dalvik.system; 18 19 import sun.misc.CompoundEnumeration; 20 21 import java.io.IOException; 22 import java.net.URL; 23 import java.util.Enumeration; 24 25 /** 26 * A {@code ClassLoader} implementation that implements a <b>delegate last</b> lookup policy. 27 * For every class or resource this loader is requested to load, the following lookup order 28 * is employed: 29 * 30 * <ul> 31 * <li>The boot classpath is always searched first</li> 32 * <li>Then, the list of {@code dex} files associated with this classloaders's 33 * {@code dexPath} is searched.</li> 34 * <li>Finally, this classloader will delegate to the specified {@code parent}.</li> 35 * </ul> 36 */ 37 public final class DelegateLastClassLoader extends PathClassLoader { 38 39 /** 40 * Equivalent to calling {@link #DelegateLastClassLoader(String, String, ClassLoader)} 41 * with {@code librarySearchPath = null}. 42 */ 43 public DelegateLastClassLoader(String dexPath, ClassLoader parent) { 44 super(dexPath, parent); 45 } 46 47 /** 48 * Creates a {@code DelegateLastClassLoader} that operates on a given {@code dexPath} 49 * and a {@code librarySearchPath}. 50 * 51 * The {@code dexPath} should consist of one or more of the following, separated by 52 * {@code File.pathSeparator}, which is {@code ":"} on Android. 53 * 54 * <ul> 55 * <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file as well as arbitrary 56 * resources. 57 * <li>Raw ".dex" files (not inside a zip file). 58 * </ul> 59 * 60 * Unlike {@link PathClassLoader}, this classloader will attempt to locate classes 61 * (or resources) using the following lookup order. 62 * <ul> 63 * <li>The boot classpath is always searched first.</li> 64 * <li>Then, the list of {@code dex} files contained in {@code dexPath} is searched./li> 65 * <li>Lastly, this classloader will delegate to the specified {@code parent}.</li> 66 * </ul> 67 * 68 * Note that this is in contrast to other standard classloaders that follow the delegation 69 * model. In those classloaders, the {@code parent} is always searched first. 70 * 71 * {@code librarySearchPath} specifies one more directories containing native library files, 72 * separated by {@code File.pathSeparator}. 73 * 74 * @param dexPath the list of jar/apk files containing classes and resources, delimited by 75 * {@code File.pathSeparator}, which defaults to {@code ":"} on Android. 76 * @param librarySearchPath the list of directories containing native libraries, delimited 77 * by {@code File.pathSeparator}; may be {@code null}. 78 * @param parent the parent class loader 79 */ 80 public DelegateLastClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) { 81 super(dexPath, librarySearchPath, parent); 82 } 83 84 @Override 85 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 86 // First, check whether the class has already been loaded. Return it if that's the 87 // case. 88 Class<?> cl = findLoadedClass(name); 89 if (cl != null) { 90 return cl; 91 } 92 93 // Next, check whether the class in question is present in the boot classpath. 94 try { 95 return Object.class.getClassLoader().loadClass(name); 96 } catch (ClassNotFoundException ignored) { 97 } 98 99 // Next, check whether the class in question is present in the dexPath that this classloader 100 // operates on. 101 ClassNotFoundException fromSuper = null; 102 try { 103 return findClass(name); 104 } catch (ClassNotFoundException ex) { 105 fromSuper = ex; 106 } 107 108 // Finally, check whether the class in question is present in the parent classloader. 109 try { 110 return getParent().loadClass(name); 111 } catch (ClassNotFoundException cnfe) { 112 // The exception we're catching here is the CNFE thrown by the parent of this 113 // classloader. However, we would like to throw a CNFE that provides details about 114 // the class path / list of dex files associated with *this* classloader, so we choose 115 // to throw the exception thrown from that lookup. 116 throw fromSuper; 117 } 118 } 119 120 @Override 121 public URL getResource(String name) { 122 // The lookup order we use here is the same as for classes. 123 124 URL resource = Object.class.getClassLoader().getResource(name); 125 if (resource != null) { 126 return resource; 127 } 128 129 resource = findResource(name); 130 if (resource != null) { 131 return resource; 132 } 133 134 135 final ClassLoader cl = getParent(); 136 return (cl == null) ? null : cl.getResource(name); 137 } 138 139 @Override 140 public Enumeration<URL> getResources(String name) throws IOException { 141 @SuppressWarnings("unchecked") 142 final Enumeration<URL>[] resources = (Enumeration<URL>[]) new Enumeration<?>[] { 143 Object.class.getClassLoader().getResources(name), 144 findResources(name), 145 (getParent() == null) ? null : getParent().getResources(name) }; 146 147 return new CompoundEnumeration<>(resources); 148 } 149 } 150