/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.communication.core.hooks; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import javax.security.auth.Subject; public abstract class AbstractHooksProxy implements InvocationHandler { private final Object proxiedInstance; private final AtomicReference<Map<MethodKey, Method>> methodTableRef; public AbstractHooksProxy(final Object proxiedInstance) { this.proxiedInstance = proxiedInstance; this.methodTableRef = new AtomicReference<Map<MethodKey, Method>>(); } public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { final Subject subject = getSubject(); if (subject == null) { return invoke(method, args); } else { try { return Subject.doAsPrivileged(subject, new PrivilegedExceptionAction<Object>() { public Object run() throws Exception { return invoke(method, args); } }, null); } catch (final PrivilegedActionException pae) { Throwable cause = pae.getCause(); if (cause instanceof InvocationTargetException) { cause = ((InvocationTargetException) cause).getTargetException(); } throw cause; } } } private Object invoke(final Method method, final Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Method proxyMethod; final Map<MethodKey, Method> methodTable = methodTableRef.get(); if (methodTable == null) { proxyMethod = method; } else { proxyMethod = methodTable.get(new MethodKey(method)); if (proxyMethod == null) { throw new NoSuchMethodException(proxiedInstance + " " + method.getName() + "," + Arrays.toString(args)); //$NON-NLS-1$ //$NON-NLS-2$ } } try { return proxyMethod.invoke(proxiedInstance, args); } catch (final IllegalArgumentException e) { if (methodTable != null) { throw new NoSuchMethodException(proxiedInstance + " " + method.getName() + "," + Arrays.toString(args)); //$NON-NLS-1$ //$NON-NLS-2$ } final Method[] methods = proxiedInstance.getClass().getMethods(); final Map<MethodKey, Method> tempMethodTable = new HashMap<MethodKey, Method>(methods.length); for (final Method keyMethod : methods) { tempMethodTable.put(new MethodKey(keyMethod), keyMethod); } methodTableRef.compareAndSet(null, tempMethodTable); return invoke(method, args); } } protected Object getProxiedInstance() { return proxiedInstance; } public abstract Subject getSubject(); /** * The {@code MethodKey} is an object that can be used as a key for * {@code Method}s in {@code Map}s that does not consider the methods * declaring class. */ private static final class MethodKey { private final Method method; private final Class<?>[] params; public MethodKey(final Method method) { this.method = method; this.params = method.getParameterTypes(); } /** * Compares this <code>MethodKey</code> against the specified object. * Returns true if the objects are the same. Two <code>MethodKeys</code> * are the same if there containing method have the same name and formal * parameter types and return type. */ @Override public boolean equals(final Object obj) { if (obj == null || !(obj instanceof MethodKey)) { return false; } final MethodKey other = (MethodKey) obj; if (!method.getName().equals(other.method.getName())) { return false; } if (!method.getReturnType().equals(other.method.getReturnType())) { return false; } if (params.length != other.params.length) { return false; } for (int i = 0; i < params.length; i++) { if (params[i] != other.params[i]) { return false; } } return true; } /** * Returns a hash-code for this <code>Method</code>. The hash-code is * computed as the hash-code of the method's name and the number of * arguments. */ @Override public int hashCode() { return method.getName().hashCode() + 31 * params.length; } } }