001package org.javasimon.spring; 002 003import java.lang.reflect.Method; 004 005import org.aopalliance.intercept.MethodInvocation; 006import org.javasimon.Manager; 007import org.javasimon.aop.Monitored; 008import org.javasimon.source.AbstractMethodStopwatchSource; 009import org.springframework.aop.SpringProxy; 010import org.springframework.aop.framework.Advised; 011import org.springframework.aop.support.AopUtils; 012import org.springframework.core.annotation.AnnotationUtils; 013 014/** 015 * Monitor source providing stopwatches from Spring AOP method invocation. 016 * 017 * @author gquintana 018 */ 019public class SpringStopwatchSource extends AbstractMethodStopwatchSource<MethodInvocation> { 020 021 /** 022 * Constructor with specified {@link Manager}. 023 * 024 * @param manager Simon manager used for producing Stopwatches 025 */ 026 public SpringStopwatchSource(Manager manager) { 027 super(manager); 028 } 029 030 /** Get target class. */ 031 protected final Class<?> getTargetClass(MethodInvocation methodInvocation) { 032 return AopUtils.getTargetClass(methodInvocation.getThis()); 033 } 034 035 /** 036 * Get method being invoked. 037 * 038 * @param methodInvocation Method invocation 039 * @return Method being invoked 040 */ 041 @Override 042 protected Method getTargetMethod(MethodInvocation methodInvocation) { 043 return getTargetMethod(methodInvocation, getTargetClass(methodInvocation)); 044 } 045 046 private Method getTargetMethod(MethodInvocation methodInvocation, Class<?> targetClass) { 047 return AopUtils.getMostSpecificMethod(methodInvocation.getMethod(), targetClass); 048 } 049 050 /** 051 * By default returns {@code true} because it is expected to be called from {@link MonitoringInterceptor} which means that the method call 052 * should be monitored. Pointcuts provided enough mechanism to decide whether the method is monitored or not, but this method can be overridden 053 * if needed. 054 * 055 * @param methodInvocation current method invocation 056 * @return true, if the method invocation should be monitored 057 */ 058 @Override 059 public boolean isMonitored(MethodInvocation methodInvocation) { 060 return true; 061 } 062 063 /** 064 * Returns monitor name for the given method invocation with {@link org.javasimon.aop.Monitored#name()} 065 * and {@link org.javasimon.aop.Monitored#suffix()} applied as expected. 066 * 067 * @param methodInvocation current method invocation 068 * @return name of the Stopwatch for the invocation 069 */ 070 protected String getMonitorName(MethodInvocation methodInvocation) { 071 Class<?> targetClass = getTargetClass(methodInvocation); 072 Method targetMethod = getTargetMethod(methodInvocation, targetClass); 073 074 Monitored methodAnnotation = AnnotationUtils.findAnnotation(targetMethod, Monitored.class); 075 if (methodAnnotation != null && methodAnnotation.name() != null && methodAnnotation.name().length() > 0) { 076 return methodAnnotation.name(); 077 } 078 079 StringBuilder nameBuilder = new StringBuilder(); 080 Monitored classAnnotation = AnnotationUtils.findAnnotation(targetClass, Monitored.class); 081 if (classAnnotation != null && classAnnotation.name() != null && classAnnotation.name().length() > 0) { 082 nameBuilder.append(classAnnotation.name()); 083 } else { 084 nameBuilder.append(getMeaningfulClassName(targetClass)); 085 } 086 nameBuilder.append(Manager.HIERARCHY_DELIMITER); 087 088 String suffix = targetMethod.getName(); 089 if (methodAnnotation != null && methodAnnotation.suffix() != null && methodAnnotation.suffix().length() > 0) { 090 suffix = methodAnnotation.suffix(); 091 } 092 return nameBuilder.append(suffix).toString(); 093 } 094 095 protected String getMeaningfulClassName(Class<?> targetClass) { 096 if (java.lang.reflect.Proxy.isProxyClass(targetClass)) { 097 for (Class<?> iface : targetClass.getInterfaces()) { 098 if (iface != SpringProxy.class && iface != Advised.class) { 099 return iface.getName(); 100 } 101 } 102 } 103 return targetClass.getName(); 104 } 105}