/src/main/java/com/atlassian/util/profiling/object/ObjectProfiler.java
Java | 198 lines | 121 code | 16 blank | 61 comment | 12 complexity | 8089b2cea68190fbd396ef68c4063c75 MD5 | raw file
- package com.atlassian.util.profiling.object;
- 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 com.atlassian.util.profiling.UtilTimerStack;
- import static com.google.common.base.Throwables.propagateIfPossible;
- /**
- * @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
- */
- public class ObjectProfiler
- {
- /**
- * <p>Given a class, and an interface that it implements, return a proxied version of the class that implements the
- * interface.</p>
- *
- * The usual use of this is to profile methods from Factory objects:
- * <pre>
- * public PersistenceManager getPersistenceManager()
- * {
- * return new DefaultPersistenceManager();
- * }
- * </pre>
- * instead write:
- * <pre>
- * public PersistenceManager getPersistenceManager()
- * {
- * return ObjectProfiler.getProfiledObject(PersistenceManager.class, new DefaultPersistenceManager());
- * }
- * </pre>
- *
- * <p>A side effect of this is that you will no longer be able to downcast to DefaultPersistenceManager. This is
- * probably a *good* thing.</p>
- *
- * @param interfaceClazz The interface to implement.
- * @param o The object to proxy
- * @return A proxied object, or the input object if the interfaceClazz wasn't an interface.
- */
- public static Object getProfiledObject(Class interfaceClazz, Object o)
- {
- //if we are not active - then do nothing
- if (!UtilTimerStack.getDefaultStrategy().isEnabled())
- {
- return o;
- }
- //this should always be true - you shouldn't be passing something that isn't an interface
- if (interfaceClazz.isInterface())
- {
- InvocationHandler timerHandler = new TimerInvocationHandler(o);
- return Proxy.newProxyInstance(interfaceClazz.getClassLoader(),
- new Class[] { interfaceClazz }, timerHandler);
- }
- else
- {
- return o;
- }
- }
- /**
- * A profiled call {@link Method#invoke(Object, Object...)} . If {@link UtilTimerStack#isActive() } returns false,
- * then no profiling is performed.
- *
- * @param method the Method to invoke
- * @param instance the instance to invoke the method on
- * @param args an Object[] containing the method arguments
- * @return the (optionally proxied) method return value
- * @throws Exception if an {@code Exception} occurs during the call to the passed in {@code method}.
- */
- public static Object profiledInvoke(Method method, Object instance, Object[] args) throws Exception
- {
- //if we are not active - then do nothing
- if (!UtilTimerStack.isActive())
- {
- try
- {
- return method.invoke(instance, args);
- }
- catch (InvocationTargetException unProfiledInvocationException)
- {
- // unpack target exception (PROF-16)
- if (unProfiledInvocationException.getCause() != null)
- {
- propagateIfPossible(unProfiledInvocationException.getCause(), Exception.class);
- throw new RuntimeException(unProfiledInvocationException.getCause());
- }
- else
- {
- throw unProfiledInvocationException;
- }
- }
- }
- String logLine = getTrimmedClassName(method) + "." + method.getName() + "()";
- UtilTimerStack.push(logLine);
- try
- {
- Object returnValue = method.invoke(instance, args);
- //if the return value is an interface then we should also proxy it!
- if (returnValue != null && method.getReturnType().isInterface())
- {
- Set interfaces = getAllInterfaces(returnValue.getClass());
- InvocationHandler timerHandler = new TimerInvocationHandler(returnValue);
- return Proxy.newProxyInstance(returnValue.getClass().getClassLoader(),
- (Class[]) interfaces.toArray(new Class[interfaces.size()]), timerHandler);
- }
- else
- {
- return returnValue;
- }
- }
- catch (InvocationTargetException profiledInvocationException)
- {
- //unpack target exception (PROF-16)
- if (profiledInvocationException.getCause() != null)
- {
- propagateIfPossible(profiledInvocationException.getCause(), Exception.class);
- throw new RuntimeException(profiledInvocationException.getCause());
- }
- else
- {
- throw profiledInvocationException;
- }
- }
- finally
- {
- UtilTimerStack.pop(logLine);
- }
- }
- /**
- * Given a method, get the Method name, with no package information.
- *
- * @param method the method
- * @return the trimmed class name
- */
- public static String getTrimmedClassName(Method method)
- {
- String classname = method.getDeclaringClass().getName();
- return classname.substring(classname.lastIndexOf('.') + 1);
- }
- /**
- * <p>A simple convenience wrapper for profiling a block of code, reduces repetition of captions used by the
- * UtilTimerStack#push and UtilTimerStack#pop methods.</p>
- *
- * Any exception that is thrown is wrapped as a RuntimeException so as to not adversely mess your source code up.
- *
- * @param caption the name of the profiling stack
- * @param profilable the thing being profiled
- * @return the return value of {@link Profilable#profile()}
- */
- public static Object profile(final String caption, Profilable profilable) throws RuntimeException
- {
- Object o;
- try
- {
- UtilTimerStack.push(caption);
- o = profilable.profile();
- }
- catch (RuntimeException e)
- {
- throw e;
- }
- catch (Exception e)
- {
- throw new RuntimeException(e);
- }
- finally
- {
- UtilTimerStack.pop(caption);
- }
- return o;
- }
- protected static Set getAllInterfaces(Class clazz)
- {
- Set interfaces = new HashSet();
- for (Class cls = clazz; cls != null; cls = cls.getSuperclass())
- {
- interfaces.addAll(Arrays.asList(cls.getInterfaces()));
- }
- return interfaces;
- }
- }