package org.jboss.seam.util;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.jboss.seam.log.Log;
import org.jboss.seam.log.Logging;
/**
* An InvocationHandler implementation that delegates method invocations to a specified object,
* optionally allowing the method to be overridden locally.
*
* @author Shane Bryzak
*/
public class DelegatingInvocationHandler<T> implements InvocationHandler
{
private static Log log = Logging.getLog(DelegatingInvocationHandler.class);
private class MethodTarget
{
public Method method;
public Object target;
public MethodTarget(Object target, Method method)
{
this.target = target;
this.method = method;
}
}
private Map<Method,MethodTarget> methodCache = new HashMap<Method,MethodTarget>();
private T delegate;
public DelegatingInvocationHandler(T delegate)
{
this.delegate = delegate;
}
public T getDelegate()
{
return delegate;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
MethodTarget target = methodCache.get(method);
if (target == null && !methodCache.containsKey(method))
{
synchronized(methodCache)
{
if (!methodCache.containsKey(method))
{
try
{
target = new MethodTarget(this, getClass().getMethod(method.getName(), method.getParameterTypes()));
}
catch (NoSuchMethodException ex)
{
// Swallow this, we'll try to find a matching method on the delegate
}
if (target == null)
{
try
{
target = new MethodTarget(delegate, delegate.getClass().getMethod(method.getName(), method.getParameterTypes()));
}
catch (NoSuchMethodException ex)
{
// Swallow this, put a null entry in methodCache
}
}
methodCache.put(method, target);
}
else
{
target = methodCache.get(method);
}
}
}
if (target == null)
{
throw new IllegalStateException("Proxied session does not implement method " + method.getName() +
" with args [" + (args == null ? null : Arrays.asList(args)) + "]");
}
if (log.isTraceEnabled())
{
log.trace("Delegating method " + method.getName() + " with args " +
(args == null ? null : Arrays.asList(args)));
}
return target.method.invoke(target.target, args);
}
}