001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.common.proxy;
005:
006: import com.tc.logging.TCLogger;
007: import com.tc.logging.TCLogging;
008: import com.tc.util.Assert;
009:
010: import java.lang.reflect.InvocationHandler;
011: import java.lang.reflect.InvocationTargetException;
012: import java.lang.reflect.Method;
013: import java.lang.reflect.Proxy;
014:
015: /**
016: * This class provides a generic {@link InvocationHandler}, used with {@link java.lang.reflect.Proxy}, that allows
017: * some sanity in generating proxies. Basically, you create a {@link GenericInvocationHandler}as the target of a
018: * {@link Proxy}. When called, the invocation handler will look in an object that you give it at construction time for
019: * a method of the correct signature, and invoke that.
020: * </p>
021: * <p>
022: * If no such method exists, the method {@link #handlerMethodNotFound}will be called instead. By default, this simply
023: * throws a {@link RuntimeException}, but can be overridden in subclasses to do anything you want.
024: */
025: class GenericInvocationHandler implements InvocationHandler {
026: private static final TCLogger logger = TCLogging
027: .getLogger(GenericInvocationHandler.class);
028:
029: private Object handler;
030:
031: /**
032: * Only should be called from subclasses that know what they're doing.
033: */
034: protected GenericInvocationHandler() {
035: this .handler = null;
036: }
037:
038: public GenericInvocationHandler(Object handler) {
039: Assert.eval(handler != null);
040: this .handler = handler;
041: }
042:
043: public Object invoke(Object proxy, Method method, Object[] args)
044: throws Throwable {
045: try {
046: return invokeInternal(proxy, method, args);
047: } catch (Throwable t) {
048: // try to log, but make sure the orignal exception is always re-thrown, even if
049: // logging somehow manages to throw an exception
050: try {
051: if (logger.isDebugEnabled()) {
052: logger.debug("EXCEPTION thrown trying to call "
053: + method, t);
054: }
055: } catch (Throwable loggingError) {
056: // too bad, logging blows
057: }
058:
059: throw t;
060: }
061: }
062:
063: protected Object getHandler() {
064: return this .handler;
065: }
066:
067: protected void setHandler(Object o) {
068: this .handler = o;
069: }
070:
071: private Object invokeInternal(Object proxy, Method method,
072: Object[] args) throws Throwable {
073: try {
074: final Method theMethod = handler.getClass().getMethod(
075: method.getName(), method.getParameterTypes());
076: try {
077: theMethod.setAccessible(true);
078: } catch (SecurityException se) {
079: logger.warn("Cannot setAccessible(true) for method ["
080: + theMethod + "], " + se.getMessage());
081: }
082: return theMethod.invoke(handler, args);
083: } catch (InvocationTargetException ite) {
084: // We need to unwrap this; we want to throw what the method actually
085: // threw, not an InvocationTargetException (which causes all sorts of
086: // madness, since it's unlikely the interface method throws that
087: // exception, and you'll end up with an UndeclaredThrowableException.
088: // Blech.)
089: throw ite.getCause();
090: } catch (NoSuchMethodException nsme) {
091: return handlerMethodNotFound(proxy, method, args);
092: }
093: }
094:
095: private String describeClasses(Class[] classes) {
096: StringBuffer out = new StringBuffer();
097: for (int i = 0; i < classes.length; ++i) {
098: if (i > 0)
099: out.append(", ");
100: out.append(classes[i].getName());
101: }
102: return out.toString();
103: }
104:
105: protected Object handlerMethodNotFound(Object proxy, Method method,
106: Object[] args) throws Throwable {
107: throw new NoSuchMethodError("Handler " + handler
108: + " does not have a method named " + method.getName()
109: + " that is " + "has argument types "
110: + describeClasses(method.getParameterTypes()));
111: }
112:
113: }
|