diff options
author | Jesse Wilson <jessewilson@google.com> | 2010-08-19 17:06:24 -0700 |
---|---|---|
committer | Jesse Wilson <jessewilson@google.com> | 2010-08-19 17:10:08 -0700 |
commit | 649e3c0004d965fe388ad454eeb20e307a12edd2 (patch) | |
tree | 324437003699a947c57c5d21128b926c607183b4 /support/src | |
parent | a63f0d99978c3d6e6fcde1f2562295070f8d7e14 (diff) | |
download | libcore-649e3c0004d965fe388ad454eeb20e307a12edd2.zip libcore-649e3c0004d965fe388ad454eeb20e307a12edd2.tar.gz libcore-649e3c0004d965fe388ad454eeb20e307a12edd2.tar.bz2 |
Fix the exception thrown by getDeclaredFields if the class is unavailable.
http://b/issue?id=2634005
Change-Id: I1ebad41a29c9527565efea539b8f2b31f117f370
Diffstat (limited to 'support/src')
-rw-r--r-- | support/src/test/java/tests/util/ClassLoaderBuilder.java | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/support/src/test/java/tests/util/ClassLoaderBuilder.java b/support/src/test/java/tests/util/ClassLoaderBuilder.java new file mode 100644 index 0000000..8fd18c2 --- /dev/null +++ b/support/src/test/java/tests/util/ClassLoaderBuilder.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.util; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Builds a configured class loader. The constructed class loader can load a + * private copy of a class in addition to the copy in the application class + * loader. It can also refuse to load a class altogether, even if that class + * exists on the classpath. + */ +public final class ClassLoaderBuilder { + private ClassLoader parent = ClassLoaderBuilder.class.getClassLoader(); + private final Set<String> prefixesToNotInherit = new HashSet<String>(); + private final Set<String> prefixesToLoad = new HashSet<String>(); + + public ClassLoaderBuilder parent(ClassLoader parent) { + this.parent = parent; + return this; + } + + /** + * @param classNamePrefix the prefix of classes that can be loaded by both + * the constructed class loader and the application class loader. + */ + public ClassLoaderBuilder withPrivateCopy(String classNamePrefix) { + prefixesToLoad.add(classNamePrefix); + return this; + } + + /** + * @param classNamePrefix the prefix of classes that will not be loaded by + * this class loader. Attempts to load such classes will fail at runtime + * with a NoClassDefFoundError. + */ + public ClassLoaderBuilder without(String classNamePrefix) { + prefixesToNotInherit.add(classNamePrefix); + return this; + } + + public ClassLoader build() { + // make a defensive copy in case this builder is reused! + final Set<String> prefixesToNotInherit = new HashSet<String>(this.prefixesToNotInherit); + final Set<String> prefixesToLoad = new HashSet<String>(this.prefixesToLoad); + + /* + * To load two copies of a given class in the VM, we end up creating two + * new class loaders: a bridge class loader and a leaf class loader. + * + * The bridge class loader is a child of the application class loader. + * It never loads any classes. All it does is decide when to delegate to + * the application class loader (which has a copy of everything) and + * when to fail. + * + * The leaf class loader is a child of the bridge class loader. It + * uses the same classpath as the application class loader. It loads + * anything that its parent failed on. + */ + + ClassLoader bridge = new ClassLoader(parent) { + @Override protected Class<?> loadClass(String className, boolean resolve) + throws ClassNotFoundException { + for (String prefix : prefixesToLoad) { + if (className.startsWith(prefix)) { + /* ClassNotFoundException causes the child loader to load the class. */ + throw new ClassNotFoundException(); + } + } + + for (String prefix : prefixesToNotInherit) { + if (className.startsWith(prefix)) { + /* NoClassDefFoundError prevents the class from being loaded at all. */ + throw new NoClassDefFoundError(); + } + } + + return super.loadClass(className, resolve); + } + }; + + try { + // first try to create a PathClassLoader for a dalvik VM... + String classPath = System.getProperty("java.class.path"); + return (ClassLoader) Class.forName("dalvik.system.PathClassLoader") + .getConstructor(String.class, ClassLoader.class) + .newInstance(classPath, bridge); + } catch (Exception ignored) { + } + + // fall back to a URLClassLoader on a JVM + List<URL> classpath = new ArrayList<URL>(); + classpath.addAll(classpathToUrls("java.class.path")); + classpath.addAll(classpathToUrls("sun.boot.class.path")); + return new URLClassLoader(classpath.toArray(new URL[classpath.size()]), bridge); + } + + private List<URL> classpathToUrls(String propertyName) { + try { + String classpath = System.getProperty(propertyName); + List<URL> result = new ArrayList<URL>(); + for (String pathElement : classpath.split(File.pathSeparator)) { + result.add(new File(pathElement).toURI().toURL()); + } + return result; + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } +} |