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.internal.model;
19
20 import java.io.Serializable;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.List;
24
25 import javax.management.AttributeNotFoundException;
26 import javax.management.InstanceNotFoundException;
27 import javax.management.JMException;
28 import javax.management.ObjectName;
29
30 /**
31  * Informations sur Tomcat, sans code html de présentation.
32  * L'état d'une instance est initialisé à son instanciation et non mutable;
33  * il est donc de fait thread-safe.
34  * Cet état est celui d'un serveur Tomcat, similaire à celui fourni dans le manager de Tomcat.
35  * Les instances sont sérialisables pour pouvoir être transmises au serveur de collecte.
36  * @author Emeric Vernat
37  */

38 public final class TomcatInformations implements Serializable {
39     // cette classe utilise la même technique avec les MBeans Tomcat que la webapp manager de Tomcat
40     // http://svn.apache.org/repos/asf/tomcat/trunk/java/org/apache/catalina/manager/StatusManagerServlet.java
41     // http://svn.apache.org/repos/asf/tomcat/trunk/java/org/apache/catalina/manager/StatusTransformer.java
42     // http://svn.apache.org/repos/asf/tomcat/trunk/webapps/manager/xform.xsl
43
44     private static final boolean TOMCAT_USED = System.getProperty("catalina.home") != null;
45
46     private static final long serialVersionUID = -6145865427461051370L;
47
48     @SuppressWarnings("all")
49     private static final List<ObjectName> THREAD_POOLS = new ArrayList<>();
50     @SuppressWarnings("all")
51     private static final List<ObjectName> GLOBAL_REQUEST_PROCESSORS = new ArrayList<>();
52
53     private static int mbeansInitAttemps;
54
55     private final String name;
56     private final int maxThreads;
57     private final int currentThreadCount;
58     private final int currentThreadsBusy;
59     private final long bytesReceived;
60     private final long bytesSent;
61     private final int requestCount;
62     private final int errorCount;
63     private final long processingTime;
64     private final long maxTime;
65
66     private TomcatInformations(ObjectName threadPool) throws JMException {
67         super();
68         name = threadPool.getKeyProperty("name");
69         maxThreads = MBeansAccessor.getAttribute(threadPool, "maxThreads");
70         currentThreadCount = MBeansAccessor.getAttribute(threadPool, "currentThreadCount");
71         currentThreadsBusy = MBeansAccessor.getAttribute(threadPool, "currentThreadsBusy");
72         ObjectName grp = null;
73         for (final ObjectName globalRequestProcessor : GLOBAL_REQUEST_PROCESSORS) {
74             if (name.equals(globalRequestProcessor.getKeyProperty("name"))) {
75                 grp = globalRequestProcessor;
76                 break;
77             }
78         }
79         if (grp != null) {
80             bytesReceived = MBeansAccessor.getAttribute(grp, "bytesReceived");
81             bytesSent = MBeansAccessor.getAttribute(grp, "bytesSent");
82             requestCount = MBeansAccessor.getAttribute(grp, "requestCount");
83             errorCount = MBeansAccessor.getAttribute(grp, "errorCount");
84             processingTime = MBeansAccessor.getAttribute(grp, "processingTime");
85             maxTime = MBeansAccessor.getAttribute(grp, "maxTime");
86         } else {
87             bytesReceived = 0;
88             bytesSent = 0;
89             requestCount = 0;
90             errorCount = 0;
91             processingTime = 0;
92             maxTime = 0;
93         }
94     }
95
96     static List<TomcatInformations> buildTomcatInformationsList() {
97         if (!TOMCAT_USED) {
98             return Collections.emptyList();
99         }
100         try {
101             synchronized (THREAD_POOLS) {
102                 if ((THREAD_POOLS.isEmpty() || GLOBAL_REQUEST_PROCESSORS.isEmpty())
103                         && mbeansInitAttemps < 10) {
104                     // lors du premier appel dans Tomcat lors du déploiement de la webapp,
105                     // ce initMBeans ne fonctionne pas car les MBeans n'existent pas encore,
106                     // donc il faut réessayer plus tard
107                     initMBeans();
108
109                     // issue 406, Tomcat mbeans never found in jboss eap 6.2,
110                     // we must stop initMBeans at some point
111                     mbeansInitAttemps++;
112                 }
113             }
114             final List<TomcatInformations> tomcatInformationsList = new ArrayList<>(
115                     THREAD_POOLS.size());
116             // rq: le processor correspondant au threadPool peut se retrouver selon
117             // threadPool.getKeyProperty("name").equals(globalRequestProcessor.getKeyProperty("name"))
118             for (final ObjectName threadPool : THREAD_POOLS) {
119                 tomcatInformationsList.add(new TomcatInformations(threadPool));
120             }
121             return tomcatInformationsList;
122         } catch (final InstanceNotFoundException | AttributeNotFoundException e) {
123             // catch InstanceNotFoundException nécessaire pour JBoss 6.0 quand appelé depuis MonitoringFilter.destroy via
124             // writeHtmlToLastShutdownFile
125             // issue 220 and end of issue 133:
126             // AttributeNotFoundException: No attribute called maxThreads (in some JBossAS or JBossWeb)
127             return Collections.emptyList();
128         } catch (final JMException e) {
129             // n'est pas censé arriver
130             throw new IllegalStateException(e);
131         }
132     }
133
134     // visibilité package pour réinitialisation en test unitaire
135     public static void initMBeans() {
136         // rq: en général, il y a 2 connecteurs (http et ajp 1.3) définis dans server.xml et donc
137         // 2 threadPools et 2 globalRequestProcessors de même nom : http-8080 et jk-8009 (ajp13)
138         THREAD_POOLS.clear();
139         GLOBAL_REQUEST_PROCESSORS.clear();
140         THREAD_POOLS.addAll(MBeansAccessor.getTomcatThreadPools());
141         GLOBAL_REQUEST_PROCESSORS.addAll(MBeansAccessor.getTomcatGlobalRequestProcessors());
142     }
143
144     public String getName() {
145         return name;
146     }
147
148     public int getMaxThreads() {
149         return maxThreads;
150     }
151
152     int getCurrentThreadCount() {
153         return currentThreadCount;
154     }
155
156     public int getCurrentThreadsBusy() {
157         return currentThreadsBusy;
158     }
159
160     public long getBytesReceived() {
161         return bytesReceived;
162     }
163
164     public long getBytesSent() {
165         return bytesSent;
166     }
167
168     public int getRequestCount() {
169         return requestCount;
170     }
171
172     public int getErrorCount() {
173         return errorCount;
174     }
175
176     public long getProcessingTime() {
177         return processingTime;
178     }
179
180     public long getMaxTime() {
181         return maxTime;
182     }
183
184     /** {@inheritDoc} */
185     @Override
186     public String toString() {
187         return getClass().getSimpleName() + "[name=" + getName() + ", maxThreads=" + getMaxThreads()
188                 + ", currentThreadCount=" + getCurrentThreadCount() + ", currentThreadsBusy="
189                 + getCurrentThreadsBusy() + ", bytesReceived=" + getBytesReceived() + ", bytesSent="
190                 + getBytesSent() + ", requestCount=" + getRequestCount() + ", errorCount="
191                 + getErrorCount() + ", processingTime=" + getProcessingTime() + ", maxTime="
192                 + getMaxTime() + ']';
193     }
194 }
195