1
2
3
4
5
6
7
8
9
10
11 package psiprobe.tools;
12
13 import jakarta.servlet.ServletContext;
14 import jakarta.servlet.http.HttpSession;
15
16 import java.io.Serializable;
17 import java.lang.reflect.InvocationTargetException;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.Date;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Locale;
25 import java.util.Set;
26
27 import javax.naming.NamingException;
28
29 import org.apache.catalina.Container;
30 import org.apache.catalina.Context;
31 import org.apache.catalina.Session;
32 import org.apache.catalina.Wrapper;
33 import org.apache.catalina.core.StandardWrapper;
34 import org.apache.commons.lang3.reflect.MethodUtils;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37 import org.springframework.util.ClassUtils;
38
39 import psiprobe.beans.ContainerWrapperBean;
40 import psiprobe.beans.ResourceResolver;
41 import psiprobe.model.Application;
42 import psiprobe.model.ApplicationParam;
43 import psiprobe.model.ApplicationResource;
44 import psiprobe.model.ApplicationSession;
45 import psiprobe.model.Attribute;
46 import psiprobe.model.FilterInfo;
47 import psiprobe.model.ServletInfo;
48 import psiprobe.model.ServletMapping;
49
50
51
52
53 public final class ApplicationUtils {
54
55
56 private static final Logger logger = LoggerFactory.getLogger(ApplicationUtils.class);
57
58
59
60
61 private ApplicationUtils() {
62
63 }
64
65
66
67
68
69
70
71
72
73 public static Application getApplication(Context context, ContainerWrapperBean containerWrapper) {
74 return getApplication(context, null, false, containerWrapper);
75 }
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 public static Application getApplication(Context context, ResourceResolver resourceResolver,
94 boolean calcSize, ContainerWrapperBean containerWrapper) {
95
96
97 logger.debug("Querying webapp: {}", context.getName());
98
99 Application app = new Application();
100 app.setName(!context.getName().isEmpty() ? context.getName() : "/");
101 app.setDocBase(context.getDocBase());
102 app.setDisplayName(context.getDisplayName());
103
104 app.setAvailable(containerWrapper.getTomcatContainer().getAvailable(context));
105 app.setDistributable(context.getDistributable());
106 app.setSessionTimeout(context.getSessionTimeout());
107 app.setServletVersion(context.getServletContext().getMajorVersion() + "."
108 + context.getServletContext().getMinorVersion());
109
110 if (resourceResolver != null) {
111 logger.debug("counting servlet attributes");
112
113 app.setContextAttributeCount(
114 Collections.list(context.getServletContext().getAttributeNames()).size());
115
116 if (app.isAvailable()) {
117 logger.debug("collecting session information");
118
119 app.setSessionCount(context.getManager().findSessions().length);
120
121 boolean serializable = true;
122 long sessionAttributeCount = 0;
123 long size = 0;
124
125 for (Session session : context.getManager().findSessions()) {
126 ApplicationSession appSession = getApplicationSession(session, calcSize, false);
127 if (appSession != null) {
128 sessionAttributeCount += appSession.getObjectCount();
129 serializable = serializable && appSession.isSerializable();
130 size += appSession.getSize();
131 }
132 }
133 app.setSerializable(serializable);
134 app.setSessionAttributeCount(sessionAttributeCount);
135 app.setSize(size);
136 }
137
138 logger.debug("aggregating servlet stats");
139
140 collectApplicationServletStats(context, app);
141
142 if (resourceResolver.supportsPrivateResources() && app.isAvailable()) {
143 int[] scores =
144 getApplicationDataSourceUsageScores(context, resourceResolver, containerWrapper);
145 app.setDataSourceBusyScore(scores[0]);
146 app.setDataSourceEstablishedScore(scores[1]);
147 }
148 }
149
150 return app;
151 }
152
153
154
155
156
157
158
159
160
161 public static void collectApplicationServletStats(Context context, Application app) {
162 int svltCount = 0;
163 long reqCount = 0;
164 long errCount = 0;
165 long procTime = 0;
166 long minTime = Long.MAX_VALUE;
167 long maxTime = 0;
168
169 for (Container container : context.findChildren()) {
170 if (container instanceof StandardWrapper sw) {
171 svltCount++;
172
173
174 Object requestCount = null;
175 try {
176 requestCount = MethodUtils.invokeMethod(sw, "getRequestCount");
177 if (requestCount instanceof Long result) {
178
179 reqCount += result;
180 } else {
181
182 reqCount += (int) requestCount;
183 }
184 } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
185 logger.error("Unable to find getRequestCount", e);
186 }
187
188
189 try {
190 Object errorCount = MethodUtils.invokeMethod(sw, "getErrorCount");
191 if (errorCount instanceof Long result) {
192
193 errCount += result;
194 } else {
195
196 errCount += (int) errorCount;
197 }
198 } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
199 logger.error("Unable to find getErrorCount", e);
200 }
201
202 procTime += sw.getProcessingTime();
203 if (requestCount != null) {
204 if (requestCount instanceof Long result && result > 0) {
205
206 minTime = Math.min(minTime, sw.getMinTime());
207 } else if (requestCount instanceof Integer result && result > 0) {
208
209 minTime = Math.min(minTime, sw.getMinTime());
210 }
211 }
212 maxTime = Math.max(maxTime, sw.getMaxTime());
213 }
214 }
215 app.setServletCount(svltCount);
216 app.setRequestCount(reqCount);
217 app.setErrorCount(errCount);
218 app.setProcessingTime(procTime);
219 app.setMinTime(minTime == Long.MAX_VALUE ? 0 : minTime);
220 app.setMaxTime(maxTime);
221 }
222
223
224
225
226
227
228
229
230
231
232 public static int[] getApplicationDataSourceUsageScores(Context context,
233 ResourceResolver resolver, ContainerWrapperBean containerWrapper) {
234
235 logger.debug("Calculating datasource usage score");
236
237 int[] scores = {0, 0};
238 List<ApplicationResource> appResources;
239 try {
240 appResources = resolver.getApplicationResources(context, containerWrapper);
241 } catch (NamingException e) {
242 throw new RuntimeException(e);
243 }
244 for (ApplicationResource appResource : appResources) {
245 if (appResource.getDataSourceInfo() != null) {
246 scores[0] = Math.max(scores[0], appResource.getDataSourceInfo().getBusyScore());
247 scores[1] = Math.max(scores[1], appResource.getDataSourceInfo().getEstablishedScore());
248 }
249 }
250 return scores;
251 }
252
253
254
255
256
257
258
259
260
261
262 public static ApplicationSession getApplicationSession(Session session, boolean calcSize,
263 boolean addAttributes) {
264
265 ApplicationSession sbean = null;
266 if (session != null && session.isValid()) {
267 sbean = new ApplicationSession();
268
269 sbean.setId(session.getId());
270 sbean.setCreationTime(new Date(session.getCreationTime()));
271 sbean.setLastAccessTime(new Date(session.getLastAccessedTime()));
272 sbean.setMaxIdleTime(session.getMaxInactiveInterval() * 1000);
273 sbean.setManagerType(session.getManager().getClass().getName());
274 sbean.setInfo(session.getClass().getSimpleName());
275
276 boolean sessionSerializable = true;
277 int attributeCount = 0;
278 long size = 0;
279
280 HttpSession httpSession = session.getSession();
281 Set<Object> processedObjects = new HashSet<>(1000);
282
283
284 processedObjects.add(httpSession);
285 try {
286 for (String name : Collections.list(httpSession.getAttributeNames())) {
287 Object obj = httpSession.getAttribute(name);
288 sessionSerializable = sessionSerializable && obj instanceof Serializable;
289
290 long objSize = 0;
291 if (calcSize) {
292 try {
293 objSize += Instruments.sizeOf(name, processedObjects);
294 objSize += Instruments.sizeOf(obj, processedObjects);
295 } catch (Exception ex) {
296 logger.error("Cannot estimate size of attribute '{}'", name, ex);
297 }
298 }
299
300 if (addAttributes) {
301 Attribute saBean = new Attribute();
302 saBean.setName(name);
303 saBean.setType(ClassUtils.getQualifiedName(obj.getClass()));
304 saBean.setValue(obj);
305 saBean.setSize(objSize);
306 saBean.setSerializable(obj instanceof Serializable);
307 sbean.addAttribute(saBean);
308 }
309 attributeCount++;
310 size += objSize;
311 }
312 String lastAccessedIp =
313 (String) httpSession.getAttribute(ApplicationSession.LAST_ACCESSED_BY_IP);
314 if (lastAccessedIp != null) {
315 sbean.setLastAccessedIp(lastAccessedIp);
316 sbean.setLastAccessedIpLocale(
317 (Locale) httpSession.getAttribute(ApplicationSession.LAST_ACCESSED_LOCALE));
318 }
319
320 } catch (IllegalStateException e) {
321 logger.info("Session appears to be invalidated, ignore");
322 logger.trace("", e);
323 }
324
325 sbean.setObjectCount(attributeCount);
326 sbean.setSize(size);
327 sbean.setSerializable(sessionSerializable);
328 }
329
330 return sbean;
331 }
332
333
334
335
336
337
338
339
340 public static List<Attribute> getApplicationAttributes(Context context) {
341 List<Attribute> attrs = new ArrayList<>();
342 ServletContext servletCtx = context.getServletContext();
343 for (String attrName : Collections.list(servletCtx.getAttributeNames())) {
344 Object attrValue = servletCtx.getAttribute(attrName);
345
346 Attribute attr = new Attribute();
347 attr.setName(attrName);
348 attr.setValue(attrValue);
349 attr.setType(ClassUtils.getQualifiedName(attrValue.getClass()));
350 attrs.add(attr);
351 }
352 return attrs;
353 }
354
355
356
357
358
359
360
361
362
363 public static List<ApplicationParam> getApplicationInitParams(Context context,
364 ContainerWrapperBean containerWrapper) {
365
366 return containerWrapper.getTomcatContainer().getApplicationInitParams(context);
367 }
368
369
370
371
372
373
374
375
376
377 public static ServletInfo getApplicationServlet(Context context, String servletName) {
378 Container container = context.findChild(servletName);
379
380 if (container instanceof Wrapper wrapper) {
381 return getServletInfo(wrapper, context.getName());
382 }
383 return null;
384 }
385
386
387
388
389
390
391
392
393
394 private static ServletInfo getServletInfo(Wrapper wrapper, String contextName) {
395 ServletInfo si = new ServletInfo();
396 si.setApplicationName(!contextName.isEmpty() ? contextName : "/");
397 si.setServletName(wrapper.getName());
398 si.setServletClass(wrapper.getServletClass());
399 si.setAvailable(!wrapper.isUnavailable());
400 si.setLoadOnStartup(wrapper.getLoadOnStartup());
401 si.setRunAs(wrapper.getRunAs());
402 si.getMappings().addAll(Arrays.asList(wrapper.findMappings()));
403 if (wrapper instanceof StandardWrapper sw) {
404 si.setAllocationCount(sw.getCountAllocated());
405
406
407 try {
408 Object errorCount = MethodUtils.invokeMethod(sw, "getErrorCount");
409 if (errorCount instanceof Long result) {
410
411 si.setErrorCount(result);
412 } else {
413
414 si.setErrorCount((int) errorCount);
415 }
416 } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
417 logger.error("Unable to find getErrorCount", e);
418 }
419
420 si.setLoadTime(sw.getLoadTime());
421 si.setMaxTime(sw.getMaxTime());
422 si.setMinTime(sw.getMinTime() == Long.MAX_VALUE ? 0 : sw.getMinTime());
423 si.setProcessingTime(sw.getProcessingTime());
424
425
426 try {
427 Object requestCount = MethodUtils.invokeMethod(sw, "getRequestCount");
428 if (requestCount instanceof Long result) {
429
430 si.setRequestCount(result);
431 } else {
432
433 si.setRequestCount((int) requestCount);
434 }
435 } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
436 logger.error("Unable to find getRequestCount", e);
437 }
438
439 }
440 return si;
441 }
442
443
444
445
446
447
448
449
450 public static List<ServletInfo> getApplicationServlets(Context context) {
451 Container[] cns = context.findChildren();
452 List<ServletInfo> servlets = new ArrayList<>(cns.length);
453 for (Container container : cns) {
454 if (container instanceof Wrapper wrapper) {
455 servlets.add(getServletInfo(wrapper, context.getName()));
456 }
457 }
458 return servlets;
459 }
460
461
462
463
464
465
466
467
468 public static List<ServletMapping> getApplicationServletMaps(Context context) {
469 String[] sms = context.findServletMappings();
470 List<ServletMapping> servletMaps = new ArrayList<>(sms.length);
471 for (String servletMapping : sms) {
472 if (servletMapping != null) {
473 String sn = context.findServletMapping(servletMapping);
474 if (sn != null) {
475 ServletMapping sm = new ServletMapping();
476 sm.setApplicationName(!context.getName().isEmpty() ? context.getName() : "/");
477 sm.setUrl(servletMapping);
478 sm.setServletName(sn);
479 Container container = context.findChild(sn);
480 if (container instanceof Wrapper wrapper) {
481 sm.setServletClass(wrapper.getServletClass());
482 sm.setAvailable(!wrapper.isUnavailable());
483 }
484 servletMaps.add(sm);
485 }
486 }
487 }
488 return servletMaps;
489 }
490
491
492
493
494
495
496
497
498
499 public static List<FilterInfo> getApplicationFilters(Context context,
500 ContainerWrapperBean containerWrapper) {
501 return containerWrapper.getTomcatContainer().getApplicationFilters(context);
502 }
503
504 }