/*
* $Id$
*
* Copyright 2006, The jCoderZ.org Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
* * Neither the name of the jCoderZ.org Project nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.jcoderz.commons.util;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <p>
* This class can be used to proxy any object, providing entering and
* exiting logging for all <i>interfaces</i> of the object.
* </p>
* <p>
* <b>Note:</b> Java Dynamic Proxies only work on <i>interfaces</i>.
* The object returned by the {@link #getProxy(Object)} can be cast to
* any interface implemented by the argument or one of its ancestors. It
* can't, however, be cast to an implementation class.
* </p>
*
* @author Albrecht Messner
* @author Andreas Mandel
*/
public final class LoggingProxy
implements InvocationHandler
{
private final Object mRealObject;
private final String mRealObjectClassName;
private final Logger mObjectLogger;
/**
* Create a proxy that directs all calls to the real object and logs all
* method calls with entering/exiting/throwing, using the given logger.
*
* @param realObject the object for which a proxy is created
* @param logger the logger to which calls are logged
*/
private LoggingProxy (Object realObject, Logger logger)
{
mRealObject = realObject;
mRealObjectClassName = mRealObject.getClass().getName();
mObjectLogger = logger;
}
/**
* Static factory that wraps an object into a proxy depending on the
* log level for that object.
*
* @param obj an object for which a proxy should be created
* @return a logging proxy for the obj, if the log level for that
* object is FINER or finest, the object itself otherwise
*/
public static Object getProxy (Object obj)
{
final String classname = obj.getClass().getName();
final Logger logger = Logger.getLogger(classname);
final Object proxy;
if (logger.isLoggable(Level.FINER))
{
// collect all interfaces implemented by this objects class and
// its super classes
// Note: Ne do not add super-interfaces here....
final Set interfaces = new HashSet();
Class currentClass = obj.getClass();
while (currentClass != null)
{
interfaces.addAll(Arrays.asList(currentClass.getInterfaces()));
currentClass = currentClass.getSuperclass();
}
proxy = Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
(Class[]) interfaces.toArray(new Class[interfaces.size()]),
new LoggingProxy(obj, logger));
}
else
{
proxy = obj;
}
return proxy;
}
/**
* Log the entering, exiting and throwing events of the proxied object.
*
* @see java.lang.reflect.InvocationHandler#invoke(
* java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke (Object proxy, Method method, Object[] args)
throws Throwable
{
final boolean isLoggable = mObjectLogger.isLoggable(Level.FINER);
if (isLoggable)
{
if (args == null)
{
mObjectLogger.entering(mRealObjectClassName, method.getName());
}
else
{
final Object[] args2 = new Object[args.length];
for (int i = 0; i < args.length; i++)
{
if (args[i] != null && args[i].getClass().isArray())
{
args2[i] = ArraysUtil.toString(args[i]);
}
else
{
args2[i] = args[i];
}
}
mObjectLogger.entering(
mRealObjectClassName, method.getName(), args2);
}
}
final Object result = invokeMethod(method, args, isLoggable);
if (isLoggable)
{
if (result != null || method.getReturnType() != Void.TYPE)
{
mObjectLogger.exiting(
mRealObjectClassName, method.getName(),
ArraysUtil.toString(result));
}
else
{
mObjectLogger.exiting(mRealObjectClassName, method.getName());
}
}
return result;
}
private Object invokeMethod (Method method, Object[] args,
boolean isLoggable)
throws Throwable
{
final Object result;
try
{
result = method.invoke(mRealObject, args);
}
catch (InvocationTargetException x)
{
if (isLoggable)
{
mObjectLogger.throwing(
mRealObjectClassName, method.getName(), x.getCause());
}
throw x.getCause();
}
catch (Exception x)
{
if (isLoggable)
{
mObjectLogger.throwing(mRealObjectClassName, method.getName(), x);
}
throw x;
}
return result;
}
}