ClassLoaderReflectionTest.java revision f3507d0976cb14ba59e0715f22f4c6b7c97cbae8
11e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson/*
21e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson * Copyright (C) 2010 The Android Open Source Project
31e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson *
41e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
51e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson * you may not use this file except in compliance with the License.
61e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson * You may obtain a copy of the License at
71e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson *
81e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
91e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson *
101e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson * Unless required by applicable law or agreed to in writing, software
111e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
121e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson * See the License for the specific language governing permissions and
141e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson * limitations under the License.
151e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson */
161e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
171e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonpackage libcore.java.lang.reflect;
181e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
191e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonimport java.io.File;
201e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonimport java.io.IOException;
21f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilsonimport java.lang.reflect.Constructor;
22f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilsonimport java.lang.reflect.Field;
23f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilsonimport java.lang.reflect.Method;
241e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonimport java.lang.reflect.ParameterizedType;
251e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonimport java.lang.reflect.Type;
26f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilsonimport java.lang.reflect.TypeVariable;
271e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonimport java.net.MalformedURLException;
281e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonimport java.net.URL;
291e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonimport java.net.URLClassLoader;
301e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonimport java.util.ArrayList;
311e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonimport java.util.Arrays;
321e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonimport java.util.List;
33f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilsonimport java.util.concurrent.Callable;
341e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonimport junit.framework.TestCase;
351e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
36f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson/**
37f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson * This class creates another class loader to load multiple copies of various
38f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson * classes into the VM at once. Then it verifies that reflection resolves the
39f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson * class names using the correct class loader.
40f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson */
411e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilsonpublic final class ClassLoaderReflectionTest extends TestCase {
421e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
43f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    /*
44f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson     * Each of these class instances points to a different copy of the class
45f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson     * than the one in the application class loader!
46f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson     */
47f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    private Class<?> aClass;
48f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    private Class<?> aListClass;
49f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    private Class<?> bClass;
50f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    private Class<?> bStringClass;
51f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    private Class<?> cClass;
52f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    private Class<?> dClass;
53f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    private Class<?> eClass;
54f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    private Class<?> fClass;
55f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson
56f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    @Override protected void setUp() throws Exception {
57f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        String prefix = ClassLoaderReflectionTest.class.getName();
58f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        ClassLoader loader = twoCopiesClassLoader(prefix, getClass().getClassLoader());
59f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        aClass = loader.loadClass(prefix + "$A");
60f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        bClass = loader.loadClass(prefix + "$B");
61f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        cClass = loader.loadClass(prefix + "$C");
62f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        dClass = loader.loadClass(prefix + "$D");
63f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        eClass = loader.loadClass(prefix + "$E");
64f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        fClass = loader.loadClass(prefix + "$F");
65f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        aListClass = loader.loadClass(prefix + "$AList");
66f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        bStringClass = loader.loadClass(prefix + "$BString");
67f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
681e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
691e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    public void testLoadOneClassInTwoClassLoadersSimultaneously() throws Exception {
701e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        assertEquals(aClass.getName(), A.class.getName());
711e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        assertNotSame(aClass, A.class);
721e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    }
731e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
74f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    public void testField() throws Exception {
751e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        assertEquals(aClass, aListClass.getDeclaredField("field").getType());
761e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    }
771e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
781e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    /**
791e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson     * http://code.google.com/p/android/issues/detail?id=10111
801e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson     */
81f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    public void testGenericSuperclassParameter() throws Exception {
82f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertParameterizedType(aListClass.getGenericSuperclass(), ArrayList.class, aClass);
831e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    }
841e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
85f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    public void testGenericSuperclassRawType() throws Exception {
86f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertParameterizedType(bStringClass.getGenericSuperclass(), bClass, String.class);
87f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
88f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson
89f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    public void testTypeParameters() throws Exception {
90f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        TypeVariable<? extends Class<?>>[] typeVariables = cClass.getTypeParameters();
91f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertEquals(2, typeVariables.length);
92f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertTypeVariable(typeVariables[0], "K", String.class);
93f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertTypeVariable(typeVariables[1], "V", aClass);
94f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
95f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson
96f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    public void testGenericInterfaces() throws Exception {
97f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        Type[] types = eClass.getGenericInterfaces();
98f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertEquals(2, types.length);
99f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        // TODO: this test incorrectly assumes that interfaces will be returned in source order!
100f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertParameterizedType(types[0], Callable.class, aClass);
101f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertParameterizedType(types[1], dClass, aClass);
102f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
103f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson
104f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    public void testFieldGenericType() throws Exception {
105f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        Field bString = fClass.getDeclaredField("bString");
106f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertParameterizedType(bString.getGenericType(), bClass, String.class);
107f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        Field listA = fClass.getDeclaredField("listA");
108f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertParameterizedType(listA.getGenericType(), List.class, aClass);
109f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
110f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson
111f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    public void testConstructorGenericType() throws Exception {
112f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        Constructor<?> constructor = fClass.getDeclaredConstructors()[0];
113f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        Type[] parameters = constructor.getGenericParameterTypes();
114f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertParameterizedType(parameters[0], bClass, String.class);
115f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertParameterizedType(parameters[1], List.class, aClass);
116f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
117f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson
118f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    public void testMethodGenericReturnType() throws Exception {
119f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        Method method = fClass.getDeclaredMethod("method", bClass, List.class);
120f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertParameterizedType(method.getGenericReturnType(), bClass, String.class);
121f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
122f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson
123f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    public void testMethodGenericParameterTypes() throws Exception {
124f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        Method method = fClass.getDeclaredMethod("method", bClass, List.class);
125f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        Type[] types = method.getGenericParameterTypes();
126f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertEquals(2, types.length);
127f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertParameterizedType(types[0], bClass, String.class);
128f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertParameterizedType(types[1], List.class, aClass);
1291e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    }
1301e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
1311e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    static class A {}
132f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    static class B<T> {
133f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        T field;
134f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
135f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    static class C<K extends String, V extends A> {}
136f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    interface D<T> {}
137f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    class E implements Callable<A>, D<A> {
138f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        public A call() throws Exception {
139f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson            return null;
140f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        }
141f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
142f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    class F {
143f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        B<String> bString;
144f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        List<A> listA;
145f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        F(B<String> parameter, List<A> anotherParameter) {}
146f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        B<String> method(B<String> parameter, List<A> anotherParameter) {
147f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson            return null;
148f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        }
149f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
1501e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    static class AList extends ArrayList<A> {
1511e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        A field;
1521e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    }
153f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    static class BString extends B<String> {}
1541e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
1551e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    /**
1561e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson     * Returns a class loader that permits multiple copies of the same class to
1571e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson     * be loaded into the same VM at the same time. This loads classes using the
1581e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson     * same classpath as the application class loader.
1591e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson     *
1601e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson     * @param prefix the prefix of classes that can be loaded by both the
1611e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson     *     returned class loader and the application class loader.
1621e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson     */
163f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    private ClassLoader twoCopiesClassLoader(final String prefix, ClassLoader parent)
1641e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson            throws IOException, InterruptedException {
1651e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
1661e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        /*
1671e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         * To load two copies of a given class in the VM, we end up creating two
1681e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         * new class loaders: a bridge class loader and a leaf class loader.
1691e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         *
1701e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         * The bridge class loader is a child of the application class loader.
1711e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         * It never loads any classes. All it does is decide when to delegate to
1721e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         * the application class loader (which has a copy of everything) and
1731e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         * when to fail.
1741e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         *
1751e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         * The leaf class loader is a child of the bridge class loader. It
1761e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         * uses the same classpath as the application class loader. It loads
1771e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         * anything that its parent failed on.
1781e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson         */
1791e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
180f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        ClassLoader bridge = new ClassLoader(parent) {
1811e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson            @Override protected Class<?> loadClass(String className, boolean resolve)
1821e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson                    throws ClassNotFoundException {
1831e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson                if (className.startsWith(prefix)) {
1841e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson                    /* throwing will cause the child class loader to load the class. */
1851e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson                    throw new ClassNotFoundException();
1861e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson                } else {
1871e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson                    return super.loadClass(className, resolve);
1881e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson                }
1891e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson            }
1901e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        };
1911e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
1921e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        try {
1931e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson            // first try to create a PathClassLoader for a dalvik VM...
1941e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson            String classPath = System.getProperty("java.class.path");
1951e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson            return (ClassLoader) Class.forName("dalvik.system.PathClassLoader")
1961e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson                    .getConstructor(String.class, ClassLoader.class)
1971e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson                    .newInstance(classPath, bridge);
1981e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        } catch (Exception ignored) {
1991e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        }
2001e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
2011e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        // fall back to a URLClassLoader on a JVM
2021e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        List<URL> classpath = new ArrayList<URL>();
2031e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        classpath.addAll(classpathToUrls("java.class.path"));
2041e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        classpath.addAll(classpathToUrls("sun.boot.class.path"));
2051e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        return new URLClassLoader(classpath.toArray(new URL[classpath.size()]), bridge);
2061e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    }
2071e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson
2081e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    private List<URL> classpathToUrls(String propertyName) throws MalformedURLException {
2091e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        String classpath = System.getProperty(propertyName);
2101e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        List<URL> result = new ArrayList<URL>();
2111e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        for (String pathElement : classpath.split(File.pathSeparator)) {
2121e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson            result.add(new File(pathElement).toURI().toURL());
2131e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        }
2141e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson        return result;
2151e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson    }
216f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson
217f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    private void assertParameterizedType(Type actual, Type raw, Type... args) {
218f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertTrue(actual.toString(), actual instanceof ParameterizedType);
219f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        ParameterizedType parameterizedType = (ParameterizedType) actual;
220f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertEquals(raw, parameterizedType.getRawType());
221f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertEquals(Arrays.<Type>asList(args),
222f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson                Arrays.asList(parameterizedType.getActualTypeArguments()));
223f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
224f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson
225f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    private void assertTypeVariable(TypeVariable actual, String name, Type... bounds) {
226f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertEquals(name, actual.getName());
227f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson        assertEquals(Arrays.<Type>asList(bounds), Arrays.asList(actual.getBounds()));
228f3507d0976cb14ba59e0715f22f4c6b7c97cbae8Jesse Wilson    }
2291e9105e7b50d142b0560d98222132a0898e700d5Jesse Wilson}
230