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&ouml;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    }