1
2
3
4
5
6
7
8
9
10
11 package psiprobe.beans;
12
13 import com.maxmind.db.CHMCache;
14 import com.maxmind.geoip2.DatabaseReader;
15 import com.maxmind.geoip2.exception.AddressNotFoundException;
16 import com.maxmind.geoip2.model.CountryResponse;
17 import com.maxmind.geoip2.record.Country;
18
19 import java.io.File;
20 import java.net.InetAddress;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Locale;
26 import java.util.Set;
27
28 import javax.inject.Inject;
29 import javax.management.InstanceNotFoundException;
30 import javax.management.MBeanServer;
31 import javax.management.MBeanServerNotification;
32 import javax.management.MalformedObjectNameException;
33 import javax.management.Notification;
34 import javax.management.NotificationListener;
35 import javax.management.ObjectInstance;
36 import javax.management.ObjectName;
37 import javax.management.RuntimeOperationsException;
38
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 import psiprobe.model.Connector;
43 import psiprobe.model.RequestProcessor;
44 import psiprobe.model.ThreadPool;
45 import psiprobe.model.jmx.ThreadPoolObjectName;
46 import psiprobe.tools.JmxTools;
47
48
49
50
51
52 public class ContainerListenerBean implements NotificationListener {
53
54
55 private static final Logger logger = LoggerFactory.getLogger(ContainerListenerBean.class);
56
57
58 private final Set<String> allowedOperation =
59 new HashSet<>(Arrays.asList("start", "stop", "pause", "resume"));
60
61
62 private List<ThreadPoolObjectName> poolNames;
63
64
65 private List<ObjectName> executorNames;
66
67
68 @Inject
69 private ContainerWrapperBean containerWrapper;
70
71
72
73
74
75
76 public ContainerWrapperBean getContainerWrapper() {
77 return containerWrapper;
78 }
79
80
81
82
83
84
85 public void setContainerWrapper(ContainerWrapperBean containerWrapper) {
86 this.containerWrapper = containerWrapper;
87 }
88
89
90
91
92
93
94 private boolean isInitialized() {
95 return poolNames != null && !poolNames.isEmpty();
96 }
97
98
99
100
101
102
103
104
105 private ThreadPoolObjectName findPool(String name) {
106 if (name != null && isInitialized()) {
107 for (ThreadPoolObjectName threadPoolObjectName : poolNames) {
108 if (name.equals(threadPoolObjectName.getThreadPoolName().getKeyProperty("name"))) {
109 return threadPoolObjectName;
110 }
111 }
112 }
113 return null;
114 }
115
116
117
118
119
120
121
122 @Override
123 public synchronized void handleNotification(Notification notification, Object object) {
124 if (!(notification instanceof MBeanServerNotification)) {
125 return;
126 }
127
128 if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(notification.getType())
129 || MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(notification.getType())) {
130
131 ObjectName objectName = ((MBeanServerNotification) notification).getMBeanName();
132 if ("RequestProcessor".equals(objectName.getKeyProperty("type"))) {
133 ThreadPoolObjectName threadPoolObjectName = findPool(objectName.getKeyProperty("worker"));
134 if (threadPoolObjectName != null) {
135 if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(notification.getType())) {
136 threadPoolObjectName.getRequestProcessorNames().add(objectName);
137 } else {
138 threadPoolObjectName.getRequestProcessorNames().remove(objectName);
139 }
140 }
141 }
142 }
143 }
144
145
146
147
148
149
150
151
152 private synchronized void initialize()
153 throws MalformedObjectNameException, InstanceNotFoundException {
154
155 MBeanServer server = getContainerWrapper().getResourceResolver().getMBeanServer();
156 String serverName = getContainerWrapper().getTomcatContainer().getName();
157 Set<ObjectInstance> threadPools =
158 server.queryMBeans(new ObjectName(serverName + ":type=ThreadPool,name=\"*\""), null);
159 poolNames = new ArrayList<>(threadPools.size());
160 for (ObjectInstance threadPool : threadPools) {
161
162 ThreadPoolObjectName threadPoolObjectName = new ThreadPoolObjectName();
163 ObjectName threadPoolName = threadPool.getObjectName();
164
165 String name = threadPoolName.getKeyProperty("name");
166
167 threadPoolObjectName.setThreadPoolName(threadPoolName);
168 ObjectName grpName = server
169 .getObjectInstance(new ObjectName(
170 threadPoolName.getDomain() + ":type=GlobalRequestProcessor,name=" + name))
171 .getObjectName();
172 threadPoolObjectName.setGlobalRequestProcessorName(grpName);
173
174
175
176
177
178 Set<ObjectInstance> workers = server.queryMBeans(
179 new ObjectName(threadPoolName.getDomain() + ":type=RequestProcessor,*"), null);
180
181 for (ObjectInstance worker : workers) {
182 ObjectName wrkName = worker.getObjectName();
183 if (name.equals(wrkName.getKeyProperty("worker"))) {
184 threadPoolObjectName.getRequestProcessorNames().add(wrkName);
185 }
186 }
187
188 poolNames.add(threadPoolObjectName);
189 }
190
191 Set<ObjectInstance> executors =
192 server.queryMBeans(new ObjectName(serverName + ":type=Executor,*"), null);
193 executorNames = new ArrayList<>(executors.size());
194 for (ObjectInstance executor : executors) {
195 ObjectName executorName = executor.getObjectName();
196 executorNames.add(executorName);
197 }
198
199
200 server.addNotificationListener(new ObjectName("JMImplementation:type=MBeanServerDelegate"),
201 this, null, null);
202
203 }
204
205
206
207
208
209
210
211
212 public synchronized List<ThreadPool> getThreadPools() throws Exception {
213 if (!isInitialized()) {
214 initialize();
215 }
216
217 List<ThreadPool> threadPools = new ArrayList<>(poolNames.size());
218
219 MBeanServer server = getContainerWrapper().getResourceResolver().getMBeanServer();
220
221 for (ObjectName executorName : executorNames) {
222 ThreadPool threadPool = new ThreadPool();
223 threadPool.setName(executorName.getKeyProperty("name"));
224 threadPool.setMaxThreads(JmxTools.getIntAttr(server, executorName, "maxThreads"));
225 threadPool.setMaxSpareThreads(JmxTools.getIntAttr(server, executorName, "largestPoolSize"));
226 threadPool.setMinSpareThreads(JmxTools.getIntAttr(server, executorName, "minSpareThreads"));
227 threadPool.setCurrentThreadsBusy(JmxTools.getIntAttr(server, executorName, "activeCount"));
228 threadPool.setCurrentThreadCount(JmxTools.getIntAttr(server, executorName, "poolSize"));
229 threadPools.add(threadPool);
230 }
231
232 for (ThreadPoolObjectName threadPoolObjectName : poolNames) {
233 ObjectName poolName = threadPoolObjectName.getThreadPoolName();
234
235 ThreadPool threadPool = new ThreadPool();
236 threadPool.setName(poolName.getKeyProperty("name"));
237 threadPool.setMaxThreads(JmxTools.getIntAttr(server, poolName, "maxThreads"));
238
239 if (JmxTools.hasAttribute(server, poolName, "maxSpareThreads")) {
240 threadPool.setMaxSpareThreads(JmxTools.getIntAttr(server, poolName, "maxSpareThreads"));
241 threadPool.setMinSpareThreads(JmxTools.getIntAttr(server, poolName, "minSpareThreads"));
242 }
243
244 threadPool.setCurrentThreadsBusy(JmxTools.getIntAttr(server, poolName, "currentThreadsBusy"));
245 threadPool.setCurrentThreadCount(JmxTools.getIntAttr(server, poolName, "currentThreadCount"));
246
247
248
249
250
251 if (threadPool.getMaxThreads() > -1) {
252 threadPools.add(threadPool);
253 }
254 }
255 return threadPools;
256 }
257
258
259
260
261
262
263
264
265
266 public synchronized void toggleConnectorStatus(String operation, String port) throws Exception {
267
268 if (!allowedOperation.contains(operation)) {
269 logger.error("operation {} not supported", operation);
270 throw new IllegalArgumentException("Not support operation");
271 }
272
273 ObjectName objectName = new ObjectName("Catalina:type=Connector,port=" + port);
274
275 MBeanServer server = getContainerWrapper().getResourceResolver().getMBeanServer();
276
277 JmxTools.invoke(server, objectName, operation, null, null);
278
279 logger.info("operation {} on Connector {} invoked success", operation, objectName);
280 }
281
282
283
284
285
286
287
288
289
290
291 public synchronized List<Connector> getConnectors(boolean includeRequestProcessors)
292 throws Exception {
293
294 boolean workerThreadNameSupported = true;
295
296 if (!isInitialized()) {
297 initialize();
298 }
299
300 List<Connector> connectors = new ArrayList<>(poolNames.size());
301
302 MBeanServer server = getContainerWrapper().getResourceResolver().getMBeanServer();
303
304 for (ThreadPoolObjectName threadPoolObjectName : poolNames) {
305 ObjectName poolName = threadPoolObjectName.getThreadPoolName();
306
307 Connector connector = new Connector();
308
309 String name = poolName.getKeyProperty("name");
310
311 connector.setProtocolHandler(poolName.getKeyProperty("name"));
312
313 if (name.startsWith("\"") && name.endsWith("\"")) {
314 name = name.substring(1, name.length() - 1);
315 }
316
317 String[] arr = name.split("-", -1);
318 String port = "-1";
319 if (arr.length == 3) {
320 port = arr[2];
321 }
322
323 if (!"-1".equals(port)) {
324 String str = "Catalina:type=Connector,port=" + port;
325
326 ObjectName objectName = new ObjectName(str);
327
328
329 connector.setStatus(JmxTools.getStringAttr(server, objectName, "stateName"));
330 connector.setProtocol(JmxTools.getStringAttr(server, objectName, "protocol"));
331 connector
332 .setSecure(Boolean.parseBoolean(JmxTools.getStringAttr(server, objectName, "secure")));
333 connector.setPort(JmxTools.getIntAttr(server, objectName, "port"));
334 connector.setLocalPort(JmxTools.getIntAttr(server, objectName, "localPort"));
335 connector.setSchema(JmxTools.getStringAttr(server, objectName, "schema"));
336 }
337
338 ObjectName grpName = threadPoolObjectName.getGlobalRequestProcessorName();
339
340 connector.setMaxTime(JmxTools.getLongAttr(server, grpName, "maxTime"));
341 connector.setProcessingTime(JmxTools.getLongAttr(server, grpName, "processingTime"));
342 connector.setBytesReceived(JmxTools.getLongAttr(server, grpName, "bytesReceived"));
343 connector.setBytesSent(JmxTools.getLongAttr(server, grpName, "bytesSent"));
344 connector.setRequestCount(JmxTools.getIntAttr(server, grpName, "requestCount"));
345 connector.setErrorCount(JmxTools.getIntAttr(server, grpName, "errorCount"));
346
347 if (includeRequestProcessors) {
348 List<ObjectName> wrkNames = threadPoolObjectName.getRequestProcessorNames();
349 for (ObjectName wrkName : wrkNames) {
350 RequestProcessor rp = new RequestProcessor();
351 rp.setName(wrkName.getKeyProperty("name"));
352 rp.setStage(JmxTools.getIntAttr(server, wrkName, "stage"));
353 rp.setProcessingTime(JmxTools.getLongAttr(server, wrkName, "requestProcessingTime"));
354 rp.setBytesSent(JmxTools.getLongAttr(server, wrkName, "requestBytesSent"));
355 rp.setBytesReceived(JmxTools.getLongAttr(server, wrkName, "requestBytesReceived"));
356 try {
357 rp.setRemoteAddr(JmxTools.getStringAttr(server, wrkName, "remoteAddr"));
358 } catch (RuntimeOperationsException ex) {
359 logger.trace("", ex);
360 }
361
362 if (rp.getRemoteAddr() != null) {
363
364 if (InetAddress.getByName(rp.getRemoteAddr()).isLoopbackAddress()) {
365 rp.setRemoteAddrLocale(new Locale(System.getProperty("user.language"),
366 System.getProperty("user.country")));
367 } else {
368
369 try (DatabaseReader reader = new DatabaseReader.Builder(new File(
370 getClass().getClassLoader().getResource("GeoLite2-Country.mmdb").toURI()))
371 .withCache(new CHMCache()).build()) {
372 CountryResponse response =
373 reader.country(InetAddress.getByName(rp.getRemoteAddr()));
374 Country country = response.getCountry();
375 rp.setRemoteAddrLocale(new Locale("", country.getIsoCode()));
376 } catch (AddressNotFoundException e) {
377 logger.debug("Address Not Found: {}", e.getMessage());
378 logger.trace("", e);
379 }
380 }
381 }
382
383 rp.setVirtualHost(JmxTools.getStringAttr(server, wrkName, "virtualHost"));
384 rp.setMethod(JmxTools.getStringAttr(server, wrkName, "method"));
385 rp.setCurrentUri(JmxTools.getStringAttr(server, wrkName, "currentUri"));
386 rp.setCurrentQueryString(JmxTools.getStringAttr(server, wrkName, "currentQueryString"));
387 rp.setProtocol(JmxTools.getStringAttr(server, wrkName, "protocol"));
388
389
390 if (workerThreadNameSupported
391 && JmxTools.hasAttribute(server, wrkName, "workerThreadName")) {
392
393 rp.setWorkerThreadName(JmxTools.getStringAttr(server, wrkName, "workerThreadName"));
394 rp.setWorkerThreadNameSupported(true);
395 } else {
396
397
398
399
400
401 rp.setWorkerThreadNameSupported(false);
402 workerThreadNameSupported = false;
403 }
404 connector.addRequestProcessor(rp);
405 }
406 }
407
408 connectors.add(connector);
409 }
410 return connectors;
411 }
412
413 }