001 /*
002 * (c) 2003-2005, 2009, 2010 ThoughtWorks Ltd
003 * All rights reserved.
004 *
005 * The software in this package is published under the terms of the BSD
006 * style license a copy of which has been included with this distribution in
007 * the LICENSE.txt file.
008 *
009 * Created on 27-Jul-2004
010 */
011 package com.thoughtworks.proxy.toys.echo;
012
013 import java.io.PrintWriter;
014 import java.lang.reflect.Method;
015
016 import com.thoughtworks.proxy.ProxyFactory;
017 import com.thoughtworks.proxy.toys.decorate.Decorating;
018 import com.thoughtworks.proxy.toys.decorate.Decorator;
019
020 /**
021 * A {@link com.thoughtworks.proxy.toys.decorate.Decorator} implementation that echoes any invocation to a {@link PrintWriter}.
022 * <p>
023 * The implementation will try to create new proxies for every return value, that can be proxied by the
024 * {@link ProxyFactory} in use.
025 * </p>
026 *
027 * @author Dan North
028 * @author Jörg Schaible
029 * @since 0.1
030 */
031 public class EchoDecorator<T> extends Decorator<T> {
032 private static final long serialVersionUID = 1L;
033 private final PrintWriter out;
034 private final ProxyFactory factory;
035
036 /**
037 * Construct an EchoingDecorator.
038 *
039 * @param out the {@link PrintWriter} receiving the logs
040 * @param factory the {@link ProxyFactory} to use
041 * @since 0.2
042 */
043 public EchoDecorator(final PrintWriter out, final ProxyFactory factory) {
044 this.out = out;
045 this.factory = factory;
046 }
047
048 @Override
049 public Object[] beforeMethodStarts(final T proxy, final Method method, final Object[] args) {
050 printMethodCall(method, args);
051 return super.beforeMethodStarts(proxy, method, args);
052 }
053
054 @Override
055 @SuppressWarnings("unchecked")
056 public Object decorateResult(final T proxy, final Method method, final Object[] args, Object result) {
057 Class returnType = method.getReturnType();
058 printMethodResult(result);
059 if (returnType != Object.class && factory.canProxy(returnType)) {
060 result = Decorating.proxy(result, returnType).visiting(this).build(factory);
061 } else if (result != null && returnType == Object.class && factory.canProxy(result.getClass())) {
062 returnType = result.getClass();
063 result = Decorating.proxy(result, returnType).visiting(this).build(factory);
064 }
065 return result;
066 }
067
068 @Override
069 public Throwable decorateTargetException(
070 final T proxy, final Method method, final Object[] args, final Throwable cause) {
071 printTargetException(cause);
072 return super.decorateTargetException(proxy, method, args, cause);
073 }
074
075 @Override
076 public Exception decorateInvocationException(
077 final T proxy, final Method method, final Object[] args, final Exception cause) {
078 printInvocationException(cause);
079 return super.decorateInvocationException(proxy, method, args, cause);
080 }
081
082 private void printMethodCall(Method method, Object[] args) {
083 final StringBuilder buf = new StringBuilder("[");
084 buf.append(Thread.currentThread().getName());
085 buf.append("] ");
086 buf.append(method.getDeclaringClass().getName());
087 buf.append(".").append(method.getName());
088
089 if (args == null) {
090 args = new Object[0];
091 }
092 buf.append("(");
093 for (int i = 0; i < args.length; i++) {
094 buf.append(i == 0 ? "<" : ", <").append(args[i]).append(">");
095 }
096 buf.append(") ");
097 out.print(buf);
098 out.flush();
099 }
100
101 private void printMethodResult(final Object result) {
102 final StringBuilder buf = new StringBuilder("--> <");
103 buf.append(result == null ? "NULL" : result.toString());
104 buf.append(">");
105 out.println(buf);
106 out.flush();
107 }
108
109 private void printTargetException(final Throwable throwable) {
110 final StringBuilder buf = new StringBuilder("throws ");
111 buf.append(throwable.getClass().getName());
112 buf.append(": ");
113 buf.append(throwable.getMessage());
114 out.println(buf);
115 out.flush();
116 }
117
118 private void printInvocationException(final Throwable throwable) {
119 out.print("INTERNAL ERROR, ");
120 printTargetException(throwable);
121 }
122 }