    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
    015package org.apache.tapestry5.ioc.internal.services;
    016
    017import org.apache.tapestry5.ioc.services.ExceptionTracker;
    018import org.apache.tapestry5.plastic.MethodInvocation;
    019import org.slf4j.Logger;
    020
    021import java.util.Iterator;
    022
    023import 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 */
    029public 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}