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.util;
005:
006: import org.apache.commons.lang.exception.ExceptionUtils;
007:
008: import java.io.IOException;
009: import java.io.Writer;
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: import java.util.ArrayList;
015: import java.util.List;
016:
017: /**
018: * An {@link InvocationHandler}that logs every call made through it, along with the thread that made it. This can be
019: * very useful for debugging in certain situations.
020: * </p>
021: * <p>
022: * This class will also usefully create proxy wrappers around any objects returned from any calls that implement any of
023: * the interfaces supplied in the <code>furtherProxies</code> argument to the constructor. This allows you to
024: * 'capture' an entire object graph resulting from a series of calls pretty easily.
025: */
026: public class LoggingInvocationHandler implements InvocationHandler {
027:
028: private final Object delegate;
029: private final Writer dest;
030: private final Class[] furtherProxies;
031:
032: public LoggingInvocationHandler(Object delegate, Writer dest,
033: Class[] furtherProxies) {
034: Assert.assertNotNull(delegate);
035: Assert.assertNotNull(dest);
036: Assert.assertNoNullElements(furtherProxies);
037: this .delegate = delegate;
038: this .dest = dest;
039: this .furtherProxies = furtherProxies;
040: }
041:
042: public Object invoke(Object arg0, Method arg1, Object[] arg2)
043: throws Throwable {
044: Thread theThread = Thread.currentThread();
045: StringBuffer message = new StringBuffer();
046:
047: message.append("[" + theThread.getName() + "]: " + delegate
048: + "." + arg1 + "(" + describeArguments(arg2) + ")");
049: try {
050: Object result = arg1.invoke(delegate, arg2);
051: message.append(" ==> " + result);
052: process(message.toString());
053: return reproxify(result);
054: } catch (InvocationTargetException ite) {
055: message.append(" => THROWABLE: ");
056: message
057: .append(ExceptionUtils
058: .getStackTrace(ite.getCause()));
059: message.append(".");
060: process(message.toString());
061: throw ite.getCause();
062: }
063: }
064:
065: private Object reproxify(Object out) {
066: if (out == null)
067: return out;
068: if (Proxy.isProxyClass(out.getClass()))
069: return out;
070:
071: List implementedClasses = new ArrayList();
072:
073: for (int i = 0; i < furtherProxies.length; ++i) {
074: if (furtherProxies[i].isInstance(out)) {
075: implementedClasses.add(furtherProxies[i]);
076: }
077: }
078:
079: if (implementedClasses.size() > 0) {
080: return Proxy.newProxyInstance(getClass().getClassLoader(),
081: (Class[]) implementedClasses
082: .toArray(new Class[implementedClasses
083: .size()]),
084: new LoggingInvocationHandler(out, this .dest,
085: this .furtherProxies));
086: } else {
087: return out;
088: }
089: }
090:
091: private String describeArguments(Object[] arguments) {
092: if (arguments == null)
093: return "";
094:
095: StringBuffer out = new StringBuffer();
096:
097: for (int i = 0; i < arguments.length; ++i) {
098: if (i > 0)
099: out.append(", ");
100: out.append(arguments[i]);
101: }
102:
103: return out.toString();
104: }
105:
106: private void process(String message) {
107: try {
108: dest.write("\n\n" + message + "\n");
109: dest.flush();
110: } catch (IOException ioe) {
111: throw Assert.failure("Got an IOException when writing.",
112: ioe);
113: }
114: }
115:
116: }
|