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.lang.reflect.InvocationHandler;
21 import java.lang.reflect.Method;
22
23 import org.springframework.beans.factory.config.BeanPostProcessor;
24 import org.springframework.core.PriorityOrdered;
25 import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
26
27 import net.bull.javamelody.internal.common.LOG;
28 import net.bull.javamelody.internal.common.Parameters;
29 import net.bull.javamelody.internal.model.Counter;
30
31 /**
32  * Post-processor Spring pour une éventuelle interface {@link ElasticsearchOperations} définie dans le fichier xml Spring.
33  * @author Emeric Vernat
34  */

35 public class SpringElasticsearchOperationsBeanPostProcessor
36         implements BeanPostProcessor, PriorityOrdered {
37     private static final boolean ELASTICSEARCH_OPERATIONS_AVAILABLE = isElasticsearchOperationsAvailable();
38     private static final Counter SERVICES_COUNTER = MonitoringProxy.getServicesCounter();
39     private static final boolean COUNTER_HIDDEN = Parameters
40             .isCounterHidden(SERVICES_COUNTER.getName());
41     private static final boolean DISABLED = Parameter.DISABLED.getValueAsBoolean();
42
43     // l'interface PriorityOrdered place la priorité assez haute dans le contexte Spring
44     // quelle que soit la valeur de order
45     private int order = LOWEST_PRECEDENCE;
46
47     /** {@inheritDoc} */
48     @Override
49     public int getOrder() {
50         return order;
51     }
52
53     /**
54      * Définit la priorité dans le contexte Spring.
55      * @param order int
56      */

57     public void setOrder(int order) {
58         this.order = order;
59     }
60
61     /** {@inheritDoc} */
62     @Override
63     public Object postProcessBeforeInitialization(Object bean, String beanName) {
64         return bean;
65     }
66
67     /** {@inheritDoc} */
68     @Override
69     public Object postProcessAfterInitialization(Object bean, String beanName) {
70         if (ELASTICSEARCH_OPERATIONS_AVAILABLE && bean instanceof ElasticsearchOperations) {
71             final ElasticsearchOperations elasticsearchOperations = (ElasticsearchOperations) bean;
72             if (DISABLED) {
73                 return elasticsearchOperations;
74             }
75             SERVICES_COUNTER.setDisplayed(!COUNTER_HIDDEN);
76             SERVICES_COUNTER.setUsed(true);
77
78             final InvocationHandler invocationHandler = new InvocationHandler() {
79                 /** {@inheritDoc} */
80                 @Override
81                 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
82                     final StringBuilder requestName = new StringBuilder();
83                     requestName.append("elasticsearch.").append(method.getName()).append('(');
84                     if (args != null) {
85                         boolean first = true;
86                         for (final Object arg : args) {
87                             if (first) {
88                                 first = false;
89                             } else {
90                                 requestName.append(", ");
91                             }
92                             if (arg == null) {
93                                 requestName.append("null");
94                             } else if (arg instanceof Class) {
95                                 requestName.append(((Class<?>) arg).getSimpleName());
96                             } else {
97                                 requestName.append(arg.getClass().getSimpleName());
98                             }
99                         }
100                     }
101                     requestName.append(')');
102                     return doInvoke(elasticsearchOperations, method, args, requestName.toString());
103                 }
104             };
105             final ElasticsearchOperations ops = JdbcWrapper.createProxy(elasticsearchOperations,
106                     invocationHandler);
107             LOG.debug("elasticsearch operations monitoring initialized");
108             return ops;
109         }
110
111         return bean;
112     }
113
114     static Object doInvoke(final Object object, final Method method, final Object[] args,
115             final String requestName) throws Throwable {
116         boolean systemError = false;
117         try {
118             SERVICES_COUNTER.bindContextIncludingCpu(requestName);
119             return method.invoke(object, args);
120         } catch (final Error e) {
121             // on catche Error pour avoir les erreurs systèmes
122             // mais pas Exception qui sont fonctionnelles en général
123             systemError = true;
124             throw e;
125         } finally {
126             // on enregistre la requête dans les statistiques
127             SERVICES_COUNTER.addRequestForCurrentContext(systemError);
128         }
129     }
130
131     private static boolean isElasticsearchOperationsAvailable() {
132         try {
133             Class.forName("org.springframework.data.elasticsearch.core.ElasticsearchOperations");
134             return true;
135         } catch (final ClassNotFoundException e) {
136             return false;
137         }
138     }
139 }
140