/* * Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.testlibrary; import java.lang.reflect.*; import java.util.HashMap; import java.util.Map; /** * Factory for proxy that delegates invocations of it's methods * to methods with same signature on target object. * Used to invoke otherwise inaccessible constructors and * instance methods for the purpose of testing. * * @author Peter Levart */ public class TestProxy { /** * Create a proxy instance of type {@code interfaze} and the underlying * delegate object of class with name {@code className} loaded by given * {@code classLoader}. The returned proxy instance delegates all method * invocations to the underlying delegate object so that a matching * method is invoked on the delegate. * * @param interfaze the interface of proxy to be created * @param className the class name of the underlying delegate object * to be created * @param classLoader the class loader used to load the class of * delegate * @param parameterTypes the parameter types of the constructor to invoke * to create new delegate * @param initargs the arguments passed to delegate constructor * @param the generic type of proxy object returned * @return newly created proxy that delegates invocations to newly created * delegate object */ public static T newInstance(Class interfaze, String className, ClassLoader classLoader, Class[] parameterTypes, Object... initargs) { try { Class clazz = Class.forName(className, false, classLoader); Constructor constructor = clazz.getDeclaredConstructor(parameterTypes); constructor.setAccessible(true); Object instance = constructor.newInstance(initargs); @SuppressWarnings("unchecked") T proxy = (T) Proxy.newProxyInstance(interfaze.getClassLoader(), new Class[]{interfaze}, new Handler(interfaze, instance)); return proxy; } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } private TestProxy() {} // not to be instantiated private static class Handler implements InvocationHandler { private final Map methodMap = new HashMap<>(); private final Object target; Handler(Class interfaze, Object target) { this.target = target; Class clazz = target.getClass(); for (Method m : interfaze.getMethods()) { if (Modifier.isStatic(m.getModifiers())) { continue; // skip static methods } Method tm = findMethod(clazz, m.getName(), m.getParameterTypes(), m.getReturnType()); if (tm == null) { throw new NoSuchMethodError("Can't find method with same signature as: " + m + " on target class: " + clazz.getName()); } methodMap.put(m, tm); } } private static Method findMethod(Class clazz, String name, Class[] parameterTypes, Class returnType) { try { Method m = clazz.getDeclaredMethod(name, parameterTypes); if (!Modifier.isStatic(m.getModifiers())) { if (m.getReturnType() != returnType) { throw new NoSuchMethodError("Method: " + m + " has invalid return type. Expecting: " + returnType.getName()); } m.setAccessible(true); return m; } } catch (NoSuchMethodException e) { } Class superclass = clazz.getSuperclass(); if (superclass != null) { Method m = findMethod(superclass, name, parameterTypes, returnType); if (m != null) { return m; } } for (Class interfaze : clazz.getInterfaces()) { Method m = findMethod(interfaze, name, parameterTypes, returnType); if (m != null) { return m; } } return null; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Method targetMethod = methodMap.get(method); if (targetMethod == null) { throw new NoSuchMethodError("No mapping exists for: " + method); } try { return targetMethod.invoke(target, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } } }