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}