001    // Copyright 2006, 2007, 2008, 2011 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.ioc.internal.services;
016    
017    import org.apache.tapestry5.ioc.services.ExceptionTracker;
018    import org.apache.tapestry5.plastic.MethodInvocation;
019    import org.slf4j.Logger;
020    
021    import java.util.Iterator;
022    
023    import static java.lang.String.format;
024    
025    /**
026     * Used by {@link org.apache.tapestry5.ioc.internal.services.LoggingDecoratorImpl} to delegate out logging behavior to a
027     * separate object.
028     */
029    public final class MethodLogger
030    {
031        private static final int BUFFER_SIZE = 200;
032    
033        private static final String ENTER = "ENTER";
034    
035        private static final String EXIT = " EXIT";
036    
037        private static final String FAIL = " FAIL";
038    
039        private final Logger logger;
040    
041        private final ExceptionTracker exceptionTracker;
042    
043        public MethodLogger(Logger logger, ExceptionTracker exceptionTracker)
044        {
045            this.logger = logger;
046            this.exceptionTracker = exceptionTracker;
047        }
048    
049        public boolean isDebugEnabled()
050        {
051            return logger.isDebugEnabled();
052        }
053    
054        /**
055         * Invoked when a method is first entered
056         *
057         * @param invocation identifies method invoked as well as parameters passed to method
058         */
059        public void entry(MethodInvocation invocation)
060        {
061            StringBuilder buffer = new StringBuilder(BUFFER_SIZE);
062    
063            buffer.append(format("[%s] %s(", ENTER, invocation.getMethod().getName()));
064    
065            for (int i = 0; i < invocation.getMethod().getParameterTypes().length; i++)
066            {
067                if (i > 0) buffer.append(", ");
068    
069                convert(buffer, invocation.getParameter(i));
070            }
071    
072            buffer.append(")");
073    
074            logger.debug(buffer.toString());
075        }
076    
077        private void convert(StringBuilder buffer, Object object)
078        {
079            if (object == null)
080            {
081                buffer.append("null");
082                return;
083            }
084    
085            // Minimal, alas: Doesn't handle embedded quotes and other
086            // characters. Really want to convert the string back to what it
087            // would look like as source code.
088    
089            if (object instanceof String)
090            {
091                buffer.append("\"");
092                buffer.append(object.toString());
093                buffer.append("\"");
094                return;
095            }
096    
097            if (object instanceof Object[])
098            {
099                Object[] values = (Object[]) object;
100                buffer.append('{');
101    
102                for (int i = 0; i < values.length; i++)
103                {
104                    if (i > 0) buffer.append(", ");
105    
106                    convert(buffer, values[i]);
107                }
108    
109                buffer.append('}');
110                return;
111            }
112    
113            if (object instanceof Iterable)
114            {
115                Iterable itr = (Iterable) object;
116                boolean first = true;
117    
118                buffer.append('[');
119                Iterator i = itr.iterator();
120                while (i.hasNext())
121                {
122                    if (!first) buffer.append(", ");
123    
124                    convert(buffer, i.next());
125                    first = false;
126                }
127                buffer.append(']');
128                return;
129            }
130    
131            // Might need to add a few more, for things like character values ...
132    
133            buffer.append(object.toString());
134        }
135    
136        /**
137         * Invoked when a method exits (possibly returning a value).
138         *
139         * @param invocation identifies method invocation and  result value
140         */
141        public void exit(MethodInvocation invocation)
142        {
143            StringBuilder buffer = new StringBuilder(BUFFER_SIZE);
144    
145            buffer.append(format("[%s] %s", EXIT, invocation.getMethod().getName()));
146    
147            if (invocation.getMethod().getReturnType() != void.class)
148            {
149                buffer.append(" [");
150                convert(buffer, invocation.getReturnValue());
151                buffer.append(']');
152            }
153    
154            logger.debug(buffer.toString());
155        }
156    
157        /**
158         * Invoked when method invocation instead throws an exception.
159         *
160         * @param invocation identifies method invocation which failed
161         * @param t          exception throws by method invocation
162         */
163        public void fail(MethodInvocation invocation, Throwable t)
164        {
165            logger.debug(
166                    format("[%s] %s -- %s", FAIL,
167                            invocation.getMethod().getName(),
168                            t.getClass().getName()),
169                    exceptionTracker.exceptionLogged(t) ? null : t);
170        }
171    }