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