1 /* 2 * Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.testlibrary; 27 28 import java.lang.reflect.*; 29 import java.util.HashMap; 30 import java.util.Map; 31 32 /** 33 * Factory for proxy that delegates invocations of its methods 34 * to methods with same signature on target object. 35 * Used to invoke otherwise inaccessible constructors and 36 * instance methods for the purpose of testing. 37 * 38 * @author Peter Levart 39 */ 40 public class TestProxy { 41 42 /** 43 * Create a proxy instance of type {@code interfaze} and the underlying 44 * delegate object of class with name {@code className} loaded by given 45 * {@code classLoader}. The returned proxy instance delegates all method 46 * invocations to the underlying delegate object so that a matching 47 * method is invoked on the delegate. 48 * 49 * @param interfaze the interface of proxy to be created 50 * @param className the class name of the underlying delegate object 51 * to be created 52 * @param classLoader the class loader used to load the class of 53 * delegate 54 * @param parameterTypes the parameter types of the constructor to invoke 55 * to create new delegate 56 * @param initargs the arguments passed to delegate constructor 57 * @param <T> the generic type of proxy object returned 58 * @return newly created proxy that delegates invocations to newly created 59 * delegate object 60 */ 61 public static <T> T newInstance(Class<T> interfaze, 62 String className, ClassLoader classLoader, 63 Class<?>[] parameterTypes, Object... initargs) { 64 try { 65 Class<?> clazz = Class.forName(className, false, classLoader); 66 Constructor<?> constructor = clazz.getDeclaredConstructor(parameterTypes); 67 constructor.setAccessible(true); 68 Object instance = constructor.newInstance(initargs); 69 @SuppressWarnings("unchecked") 70 T proxy = (T) Proxy.newProxyInstance(interfaze.getClassLoader(), 71 new Class<?>[]{interfaze}, 72 new Handler(interfaze, instance)); 73 return proxy; 74 } catch (ClassNotFoundException | NoSuchMethodException | 75 InvocationTargetException | InstantiationException | 76 IllegalAccessException e) { 77 throw new RuntimeException(e); 78 } 79 } 80 81 private TestProxy() {} // not to be instantiated 82 83 private static class Handler implements InvocationHandler { 84 85 private final Map<Method, Method> methodMap = new HashMap<>(); 86 private final Object target; 87 88 Handler(Class<?> interfaze, Object target) { 89 this.target = target; 90 Class<?> clazz = target.getClass(); 91 for (Method m : interfaze.getMethods()) { 92 if (Modifier.isStatic(m.getModifiers())) { 93 continue; // skip static methods 94 } 95 Method tm = findMethod(clazz, 96 m.getName(), 97 m.getParameterTypes(), 98 m.getReturnType()); 99 if (tm == null) { 100 throw new NoSuchMethodError("Can't find method with same signature as: " + m + 101 " on target class: " + clazz.getName()); 102 } 103 methodMap.put(m, tm); 104 } 105 } 106 107 private static Method findMethod(Class<?> clazz, 108 String name, 109 Class<?>[] parameterTypes, 110 Class<?> returnType) { 111 try { 112 Method m = clazz.getDeclaredMethod(name, parameterTypes); 113 if (!Modifier.isStatic(m.getModifiers())) { 114 if (m.getReturnType() != returnType) { 115 throw new NoSuchMethodError("Method: " + m + 116 " has invalid return type. Expecting: " + 117 returnType.getName()); 118 } 119 m.setAccessible(true); 120 return m; 121 } 122 } catch (NoSuchMethodException e) { 123 } 124 125 Class<?> superclass = clazz.getSuperclass(); 126 if (superclass != null) { 127 Method m = findMethod(superclass, name, parameterTypes, returnType); 128 if (m != null) { 129 return m; 130 } 131 } 132 133 for (Class<?> interfaze : clazz.getInterfaces()) { 134 Method m = findMethod(interfaze, name, parameterTypes, returnType); 135 if (m != null) { 136 return m; 137 } 138 } 139 140 return null; 141 } 142 143 @Override 144 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 145 Method targetMethod = methodMap.get(method); 146 if (targetMethod == null) { 147 throw new NoSuchMethodError("No mapping exists for: " + method); 148 } 149 try { 150 return targetMethod.invoke(target, args); 151 } catch (InvocationTargetException e) { 152 throw e.getTargetException(); 153 } 154 } 155 } 156 }