/* * $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; } }