1 /*
2  * Copyright 2008-2019 by Emeric Vernat
3  *
4  *     This file is part of Java Melody.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 package net.bull.javamelody;
19
20 import java.io.Serializable;
21
22 import org.aopalliance.intercept.MethodInterceptor;
23 import org.aopalliance.intercept.MethodInvocation;
24 import org.springframework.aop.support.AopUtils;
25
26 import net.bull.javamelody.internal.common.LOG;
27 import net.bull.javamelody.internal.common.Parameters;
28 import net.bull.javamelody.internal.model.Counter;
29
30 /**
31  * Method interceptor that measures the duration of the intercepted call.
32  *
33  * Inspired by Erik van Oosten (Java Simon, Licence LGPL)
34  * @author Emeric Vernat
35  */

36 public class MonitoringSpringInterceptor implements MethodInterceptor, Serializable {
37     private static final long serialVersionUID = -6594338383847482623L;
38     private static final Counter SPRING_COUNTER = MonitoringProxy.getSpringCounter();
39     private static final boolean COUNTER_HIDDEN = Parameters
40             .isCounterHidden(SPRING_COUNTER.getName());
41     private static final boolean DISABLED = Parameter.DISABLED.getValueAsBoolean();
42
43     /**
44      * Constructeur.
45      */

46     public MonitoringSpringInterceptor() {
47         super();
48         // quand cet intercepteur est utilisé, le compteur est affiché
49         // sauf si le paramètre displayed-counters dit le contraire
50         SPRING_COUNTER.setDisplayed(!COUNTER_HIDDEN);
51         // setUsed(true) nécessaire ici si le contexte spring est initialisé avant FilterContext
52         // sinon les statistiques spring ne sont pas affichées
53         SPRING_COUNTER.setUsed(true);
54         LOG.debug("spring interceptor initialized");
55     }
56
57     /**
58      * Performs method invocation.
59      *
60      * @param invocation method invocation
61      * @return return object from the method
62      * @throws Throwable anything thrown by the method
63      */

64     @Override
65     public Object invoke(MethodInvocation invocation) throws Throwable {
66         // cette méthode est appelée par spring aop
67         if (DISABLED || !SPRING_COUNTER.isDisplayed()) {
68             return invocation.proceed();
69         }
70         // nom identifiant la requête
71         final String requestName = getRequestName(invocation);
72
73         boolean systemError = false;
74         try {
75             SPRING_COUNTER.bindContextIncludingCpu(requestName);
76             return invocation.proceed();
77         } catch (final Error e) {
78             // on catche Error pour avoir les erreurs systèmes
79             // mais pas Exception qui sont fonctionnelles en général
80             systemError = true;
81             throw e;
82         } finally {
83             // on enregistre la requête dans les statistiques
84             SPRING_COUNTER.addRequestForCurrentContext(systemError);
85         }
86     }
87
88     /**
89      * Determine request name for a method invocation.
90      *
91      * @param invocation the method invocation (not null)
92      * @return the request name for this invocation
93      */

94     protected String getRequestName(MethodInvocation invocation) {
95         final String classPart = getClassPart(invocation);
96         final String methodPart = getMethodPart(invocation);
97         return classPart + '.' + methodPart;
98     }
99
100     private static String getClassPart(MethodInvocation invocation) {
101         // si guice et pas Spring, alors remplacer AopUtils.getTargetClass() par getMethod().getDeclaringClass()
102         // http://ninomartinez.wordpress.com/2010/05/14/guice-caching-interceptors/
103         // (faire exemple avec un interceptor static)
104         final Class<?> targetClass = AopUtils.getTargetClass(invocation.getThis());
105         final MonitoredWithSpring classAnnotation = targetClass
106                 .getAnnotation(MonitoredWithSpring.class);
107         if (classAnnotation == null || classAnnotation.name() == null
108                 || classAnnotation.name().isEmpty()) {
109             final Class<?> declaringClass = invocation.getMethod().getDeclaringClass();
110             final MonitoredWithSpring declaringClassAnnotation = declaringClass
111                     .getAnnotation(MonitoredWithSpring.class);
112             if (declaringClassAnnotation == null || declaringClassAnnotation.name() == null
113                     || declaringClassAnnotation.name().isEmpty()) {
114                 return targetClass.getSimpleName();
115             }
116             return declaringClassAnnotation.name();
117         }
118         return classAnnotation.name();
119     }
120
121     private static String getMethodPart(MethodInvocation invocation) {
122         final MonitoredWithSpring methodAnnotation = invocation.getMethod()
123                 .getAnnotation(MonitoredWithSpring.class);
124         if (methodAnnotation == null || methodAnnotation.name() == null
125                 || methodAnnotation.name().isEmpty()) {
126             return invocation.getMethod().getName();
127         }
128         return methodAnnotation.name();
129     }
130 }
131