PageRenderTime 42ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/com/atlassian/util/profiling/object/ObjectProfiler.java

https://bitbucket.org/atlassian/atlassian-profiling
Java | 198 lines | 121 code | 16 blank | 61 comment | 12 complexity | 8089b2cea68190fbd396ef68c4063c75 MD5 | raw file
  1. package com.atlassian.util.profiling.object;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Proxy;
  6. import java.util.Arrays;
  7. import java.util.HashSet;
  8. import java.util.Set;
  9. import com.atlassian.util.profiling.UtilTimerStack;
  10. import static com.google.common.base.Throwables.propagateIfPossible;
  11. /**
  12. * @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
  13. */
  14. public class ObjectProfiler
  15. {
  16. /**
  17. * <p>Given a class, and an interface that it implements, return a proxied version of the class that implements the
  18. * interface.</p>
  19. *
  20. * The usual use of this is to profile methods from Factory objects:
  21. * <pre>
  22. * public PersistenceManager getPersistenceManager()
  23. * {
  24. * return new DefaultPersistenceManager();
  25. * }
  26. * </pre>
  27. * instead write:
  28. * <pre>
  29. * public PersistenceManager getPersistenceManager()
  30. * {
  31. * return ObjectProfiler.getProfiledObject(PersistenceManager.class, new DefaultPersistenceManager());
  32. * }
  33. * </pre>
  34. *
  35. * <p>A side effect of this is that you will no longer be able to downcast to DefaultPersistenceManager. This is
  36. * probably a *good* thing.</p>
  37. *
  38. * @param interfaceClazz The interface to implement.
  39. * @param o The object to proxy
  40. * @return A proxied object, or the input object if the interfaceClazz wasn't an interface.
  41. */
  42. public static Object getProfiledObject(Class interfaceClazz, Object o)
  43. {
  44. //if we are not active - then do nothing
  45. if (!UtilTimerStack.getDefaultStrategy().isEnabled())
  46. {
  47. return o;
  48. }
  49. //this should always be true - you shouldn't be passing something that isn't an interface
  50. if (interfaceClazz.isInterface())
  51. {
  52. InvocationHandler timerHandler = new TimerInvocationHandler(o);
  53. return Proxy.newProxyInstance(interfaceClazz.getClassLoader(),
  54. new Class[] { interfaceClazz }, timerHandler);
  55. }
  56. else
  57. {
  58. return o;
  59. }
  60. }
  61. /**
  62. * A profiled call {@link Method#invoke(Object, Object...)} . If {@link UtilTimerStack#isActive() } returns false,
  63. * then no profiling is performed.
  64. *
  65. * @param method the Method to invoke
  66. * @param instance the instance to invoke the method on
  67. * @param args an Object[] containing the method arguments
  68. * @return the (optionally proxied) method return value
  69. * @throws Exception if an {@code Exception} occurs during the call to the passed in {@code method}.
  70. */
  71. public static Object profiledInvoke(Method method, Object instance, Object[] args) throws Exception
  72. {
  73. //if we are not active - then do nothing
  74. if (!UtilTimerStack.isActive())
  75. {
  76. try
  77. {
  78. return method.invoke(instance, args);
  79. }
  80. catch (InvocationTargetException unProfiledInvocationException)
  81. {
  82. // unpack target exception (PROF-16)
  83. if (unProfiledInvocationException.getCause() != null)
  84. {
  85. propagateIfPossible(unProfiledInvocationException.getCause(), Exception.class);
  86. throw new RuntimeException(unProfiledInvocationException.getCause());
  87. }
  88. else
  89. {
  90. throw unProfiledInvocationException;
  91. }
  92. }
  93. }
  94. String logLine = getTrimmedClassName(method) + "." + method.getName() + "()";
  95. UtilTimerStack.push(logLine);
  96. try
  97. {
  98. Object returnValue = method.invoke(instance, args);
  99. //if the return value is an interface then we should also proxy it!
  100. if (returnValue != null && method.getReturnType().isInterface())
  101. {
  102. Set interfaces = getAllInterfaces(returnValue.getClass());
  103. InvocationHandler timerHandler = new TimerInvocationHandler(returnValue);
  104. return Proxy.newProxyInstance(returnValue.getClass().getClassLoader(),
  105. (Class[]) interfaces.toArray(new Class[interfaces.size()]), timerHandler);
  106. }
  107. else
  108. {
  109. return returnValue;
  110. }
  111. }
  112. catch (InvocationTargetException profiledInvocationException)
  113. {
  114. //unpack target exception (PROF-16)
  115. if (profiledInvocationException.getCause() != null)
  116. {
  117. propagateIfPossible(profiledInvocationException.getCause(), Exception.class);
  118. throw new RuntimeException(profiledInvocationException.getCause());
  119. }
  120. else
  121. {
  122. throw profiledInvocationException;
  123. }
  124. }
  125. finally
  126. {
  127. UtilTimerStack.pop(logLine);
  128. }
  129. }
  130. /**
  131. * Given a method, get the Method name, with no package information.
  132. *
  133. * @param method the method
  134. * @return the trimmed class name
  135. */
  136. public static String getTrimmedClassName(Method method)
  137. {
  138. String classname = method.getDeclaringClass().getName();
  139. return classname.substring(classname.lastIndexOf('.') + 1);
  140. }
  141. /**
  142. * <p>A simple convenience wrapper for profiling a block of code, reduces repetition of captions used by the
  143. * UtilTimerStack#push and UtilTimerStack#pop methods.</p>
  144. *
  145. * Any exception that is thrown is wrapped as a RuntimeException so as to not adversely mess your source code up.
  146. *
  147. * @param caption the name of the profiling stack
  148. * @param profilable the thing being profiled
  149. * @return the return value of {@link Profilable#profile()}
  150. */
  151. public static Object profile(final String caption, Profilable profilable) throws RuntimeException
  152. {
  153. Object o;
  154. try
  155. {
  156. UtilTimerStack.push(caption);
  157. o = profilable.profile();
  158. }
  159. catch (RuntimeException e)
  160. {
  161. throw e;
  162. }
  163. catch (Exception e)
  164. {
  165. throw new RuntimeException(e);
  166. }
  167. finally
  168. {
  169. UtilTimerStack.pop(caption);
  170. }
  171. return o;
  172. }
  173. protected static Set getAllInterfaces(Class clazz)
  174. {
  175. Set interfaces = new HashSet();
  176. for (Class cls = clazz; cls != null; cls = cls.getSuperclass())
  177. {
  178. interfaces.addAll(Arrays.asList(cls.getInterfaces()));
  179. }
  180. return interfaces;
  181. }
  182. }