1
18 package net.bull.javamelody.internal.model;
19
20 import java.io.Serializable;
21 import java.lang.management.ManagementFactory;
22 import java.lang.management.OperatingSystemMXBean;
23 import java.lang.management.ThreadMXBean;
24 import java.sql.Connection;
25 import java.sql.DatabaseMetaData;
26 import java.sql.DriverManager;
27 import java.sql.SQLException;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.Comparator;
32 import java.util.Date;
33 import java.util.List;
34 import java.util.Locale;
35 import java.util.Map;
36
37 import javax.servlet.ServletContext;
38 import javax.sql.DataSource;
39
40 import net.bull.javamelody.JdbcWrapper;
41 import net.bull.javamelody.SessionListener;
42 import net.bull.javamelody.SpringContext;
43 import net.bull.javamelody.internal.common.Parameters;
44 import net.bull.javamelody.internal.model.HsErrPid.HsErrPidComparator;
45
46
54 public class JavaInformations implements Serializable {
55 public static final double HIGH_USAGE_THRESHOLD_IN_PERCENTS = 95d;
56 private static final long serialVersionUID = 3281861236369720876L;
57 private static final Date START_DATE = new Date();
58 private static final boolean SPRING_AVAILABLE = isSpringAvailable();
59 private static boolean localWebXmlExists = true;
60 private static boolean localPomXmlExists = true;
61 private final MemoryInformations memoryInformations;
62 @SuppressWarnings("all")
63 private final List<TomcatInformations> tomcatInformationsList;
64 private final int sessionCount;
65 private final long sessionAgeSum;
66 private final int activeThreadCount;
67 private final int usedConnectionCount;
68 private final int maxConnectionCount;
69 private final int activeConnectionCount;
70 private final long transactionCount;
71 private final long processCpuTimeMillis;
72 private final double systemLoadAverage;
73 private final double systemCpuLoad;
74 private final long unixOpenFileDescriptorCount;
75 private final long unixMaxFileDescriptorCount;
76 private final String host;
77 private final String os;
78 private final int availableProcessors;
79 private final String javaVersion;
80 private final String jvmVersion;
81 private final String pid;
82 private final String serverInfo;
83 private final String contextPath;
84 private final String contextDisplayName;
85 private final String webappVersion;
86 private final Date startDate;
87 private final String jvmArguments;
88 private final long freeDiskSpaceInTemp;
89 private final long usableDiskSpaceInTemp;
90 private final int threadCount;
91 private final int peakThreadCount;
92 private final long totalStartedThreadCount;
93 private final String dataBaseVersion;
94 private final String dataSourceDetails;
95 @SuppressWarnings("all")
96 private final List<ThreadInformations> threadInformationsList;
97 @SuppressWarnings("all")
98 private final List<CacheInformations> cacheInformationsList;
99 @SuppressWarnings("all")
100 private final List<JCacheInformations> jcacheInformationsList;
101 @SuppressWarnings("all")
102 private final List<JobInformations> jobInformationsList;
103 @SuppressWarnings("all")
104 private final List<HsErrPid> hsErrPidList;
105 private final boolean webXmlExists = localWebXmlExists;
106 private final boolean pomXmlExists = localPomXmlExists;
107 private final boolean springBeanExists;
108
109 static final class ThreadInformationsComparator
110 implements Comparator<ThreadInformations>, Serializable {
111 private static final long serialVersionUID = 1L;
112
113
114 @Override
115 public int compare(ThreadInformations thread1, ThreadInformations thread2) {
116 return thread1.getName().compareToIgnoreCase(thread2.getName());
117 }
118 }
119
120 static final class CacheInformationsComparator
121 implements Comparator<CacheInformations>, Serializable {
122 private static final long serialVersionUID = 1L;
123
124
125 @Override
126 public int compare(CacheInformations cache1, CacheInformations cache2) {
127 return cache1.getName().compareToIgnoreCase(cache2.getName());
128 }
129 }
130
131 static final class JCacheInformationsComparator
132 implements Comparator<JCacheInformations>, Serializable {
133 private static final long serialVersionUID = 1L;
134
135
136 @Override
137 public int compare(JCacheInformations cache1, JCacheInformations cache2) {
138 return cache1.getName().compareToIgnoreCase(cache2.getName());
139 }
140 }
141
142 static final class JobInformationsComparator
143 implements Comparator<JobInformations>, Serializable {
144 private static final long serialVersionUID = 1L;
145
146
147 @Override
148 public int compare(JobInformations job1, JobInformations job2) {
149 return job1.getName().compareToIgnoreCase(job2.getName());
150 }
151 }
152
153
154 public JavaInformations(ServletContext servletContext, boolean includeDetails) {
155
156 super();
157 memoryInformations = new MemoryInformations();
158 tomcatInformationsList = TomcatInformations.buildTomcatInformationsList();
159 sessionCount = SessionListener.getSessionCount();
160 sessionAgeSum = SessionListener.getSessionAgeSum();
161 activeThreadCount = JdbcWrapper.getActiveThreadCount();
162 usedConnectionCount = JdbcWrapper.getUsedConnectionCount();
163 activeConnectionCount = JdbcWrapper.getActiveConnectionCount();
164 maxConnectionCount = JdbcWrapper.getMaxConnectionCount();
165 transactionCount = JdbcWrapper.getTransactionCount();
166 systemLoadAverage = buildSystemLoadAverage();
167 systemCpuLoad = buildSystemCpuLoad();
168 processCpuTimeMillis = buildProcessCpuTimeMillis();
169 unixOpenFileDescriptorCount = buildOpenFileDescriptorCount();
170 unixMaxFileDescriptorCount = buildMaxFileDescriptorCount();
171 host = Parameters.getHostName() + '@' + Parameters.getHostAddress();
172 os = buildOS();
173 availableProcessors = Runtime.getRuntime().availableProcessors();
174 javaVersion = System.getProperty("java.runtime.name") + ", "
175 + System.getProperty("java.runtime.version");
176 jvmVersion = System.getProperty("java.vm.name") + ", "
177 + System.getProperty("java.vm.version") + ", " + System.getProperty("java.vm.info");
178 if (servletContext == null) {
179 serverInfo = null;
180 contextPath = null;
181 contextDisplayName = null;
182 webappVersion = null;
183 } else {
184 serverInfo = servletContext.getServerInfo();
185 contextPath = Parameters.getContextPath(servletContext);
186 contextDisplayName = servletContext.getServletContextName();
187 webappVersion = MavenArtifact.getWebappVersion();
188 }
189 startDate = START_DATE;
190 jvmArguments = buildJvmArguments();
191 final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
192 threadCount = threadBean.getThreadCount();
193 peakThreadCount = threadBean.getPeakThreadCount();
194 totalStartedThreadCount = threadBean.getTotalStartedThreadCount();
195 freeDiskSpaceInTemp = Parameters.TEMPORARY_DIRECTORY.getFreeSpace();
196 usableDiskSpaceInTemp = Parameters.TEMPORARY_DIRECTORY.getUsableSpace();
197 springBeanExists = SPRING_AVAILABLE && SpringContext.getSingleton() != null;
198
199 if (includeDetails) {
200 dataBaseVersion = buildDataBaseVersion();
201 dataSourceDetails = buildDataSourceDetails();
202 threadInformationsList = buildThreadInformationsList();
203 cacheInformationsList = CacheInformations.buildCacheInformationsList();
204 jcacheInformationsList = JCacheInformations.buildJCacheInformationsList();
205 jobInformationsList = JobInformations.buildJobInformationsList();
206 hsErrPidList = HsErrPid.buildHsErrPidList();
207 pid = PID.getPID();
208 } else {
209 dataBaseVersion = null;
210 dataSourceDetails = null;
211 threadInformationsList = null;
212 cacheInformationsList = null;
213 jcacheInformationsList = null;
214 jobInformationsList = null;
215 hsErrPidList = null;
216 pid = null;
217 }
218 }
219
220 public static void setWebXmlExistsAndPomXmlExists(boolean webXmlExists, boolean pomXmlExists) {
221 localWebXmlExists = webXmlExists;
222 localPomXmlExists = pomXmlExists;
223 }
224
225 public boolean doesWebXmlExists() {
226 return webXmlExists;
227 }
228
229 public boolean doesPomXmlExists() {
230 return pomXmlExists;
231 }
232
233 private static String buildOS() {
234 final String name = System.getProperty("os.name");
235 final String version = System.getProperty("os.version");
236 final String patchLevel = System.getProperty("sun.os.patch.level");
237 final String arch = System.getProperty("os.arch");
238 final String bits = System.getProperty("sun.arch.data.model");
239
240 final StringBuilder sb = new StringBuilder();
241 sb.append(name).append(", ");
242 if (!name.toLowerCase(Locale.ENGLISH).contains("windows")) {
243
244
245 sb.append(version).append(' ');
246 }
247 if (!"unknown".equals(patchLevel)) {
248
249
250 sb.append(patchLevel);
251 }
252 sb.append(", ").append(arch).append('/').append(bits);
253 return sb.toString();
254 }
255
256 private static long buildProcessCpuTimeMillis() {
257
258 final long processCpuTime = MBeansAccessor.getLongFromOperatingSystem("ProcessCpuTime");
259 if (processCpuTime >= 0L) {
260 return processCpuTime / 1000000;
261 }
262 return -1;
263 }
264
265 private static long buildOpenFileDescriptorCount() {
266 try {
267 return MBeansAccessor.getLongFromOperatingSystem("OpenFileDescriptorCount");
268 } catch (final Throwable e) {
269
270
271 return -1;
272 }
273 }
274
275 private static long buildMaxFileDescriptorCount() {
276 try {
277 return MBeansAccessor.getLongFromOperatingSystem("MaxFileDescriptorCount");
278 } catch (final Throwable e) {
279
280
281 return -1;
282 }
283 }
284
285 private static double buildSystemCpuLoad() {
286
287
288
289
290
291
292 final double systemCpu = MBeansAccessor.getDoubleFromOperatingSystem("SystemCpuLoad");
293 if (systemCpu >= 0D) {
294 return systemCpu * 100;
295 }
296
297 return -1;
298 }
299
300 private static double buildSystemLoadAverage() {
301
302
303
304
305
306 final OperatingSystemMXBean operatingSystem = ManagementFactory.getOperatingSystemMXBean();
307 if (operatingSystem.getSystemLoadAverage() >= 0) {
308
309 return operatingSystem.getSystemLoadAverage();
310 }
311 return -1;
312 }
313
314 private static String buildJvmArguments() {
315 final StringBuilder jvmArgs = new StringBuilder();
316 for (final String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
317 jvmArgs.append(jvmArg).append('\n');
318 }
319 if (jvmArgs.length() > 0) {
320 jvmArgs.deleteCharAt(jvmArgs.length() - 1);
321 }
322 return jvmArgs.toString();
323 }
324
325 public static List<ThreadInformations> buildThreadInformationsList() {
326 final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
327 final Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
328 final List<Thread> threads = new ArrayList<>(stackTraces.keySet());
329
330
331
332
333
334
335
336
337
338 final boolean cpuTimeEnabled = threadBean.isThreadCpuTimeSupported()
339 && threadBean.isThreadCpuTimeEnabled();
340 final long[] deadlockedThreads = getDeadlockedThreads(threadBean);
341 final List<ThreadInformations> threadInfosList = new ArrayList<>(threads.size());
342
343 final String hostAddress = Parameters.getHostAddress();
344 for (final Thread thread : threads) {
345 final StackTraceElement[] stackTraceElements = stackTraces.get(thread);
346 final List<StackTraceElement> stackTraceElementList = stackTraceElements == null ? null
347 : new ArrayList<>(Arrays.asList(stackTraceElements));
348 final long cpuTimeMillis;
349 final long userTimeMillis;
350 if (cpuTimeEnabled) {
351 cpuTimeMillis = threadBean.getThreadCpuTime(thread.getId()) / 1000000;
352 userTimeMillis = threadBean.getThreadUserTime(thread.getId()) / 1000000;
353 } else {
354 cpuTimeMillis = -1;
355 userTimeMillis = -1;
356 }
357 final boolean deadlocked = deadlockedThreads != null
358 && Arrays.binarySearch(deadlockedThreads, thread.getId()) >= 0;
359
360 threadInfosList.add(new ThreadInformations(thread, stackTraceElementList, cpuTimeMillis,
361 userTimeMillis, deadlocked, hostAddress));
362 }
363
364 return threadInfosList;
365 }
366
367 static List<Thread> getThreadsFromThreadGroups() {
368 ThreadGroup group = Thread.currentThread().getThreadGroup();
369 while (group.getParent() != null) {
370 group = group.getParent();
371 }
372 final Thread[] threadsArray = new Thread[group.activeCount()];
373 group.enumerate(threadsArray, true);
374 final List<Thread> threads = new ArrayList<>(threadsArray.length);
375 for (final Thread thread : threadsArray) {
376
377 if (thread != null) {
378 threads.add(thread);
379 }
380 }
381 return threads;
382 }
383
384 private static long[] getDeadlockedThreads(ThreadMXBean threadBean) {
385 final long[] deadlockedThreads;
386 if (threadBean.isSynchronizerUsageSupported()) {
387 deadlockedThreads = threadBean.findDeadlockedThreads();
388 } else {
389 deadlockedThreads = threadBean.findMonitorDeadlockedThreads();
390 }
391 if (deadlockedThreads != null) {
392 Arrays.sort(deadlockedThreads);
393 }
394 return deadlockedThreads;
395 }
396
397 private static String buildDataBaseVersion() {
398 if (Parameters.isNoDatabase()) {
399 return null;
400 }
401 final StringBuilder result = new StringBuilder();
402 try {
403
404
405 if (Parameters.getLastConnectUrl() != null) {
406 final Connection connection = DriverManager.getConnection(
407 Parameters.getLastConnectUrl(), Parameters.getLastConnectInfo());
408 connection.setAutoCommit(false);
409 try {
410 appendDataBaseVersion(result, connection);
411 } finally {
412
413 connection.close();
414 }
415 }
416
417
418
419 final Map<String, DataSource> dataSources = JdbcWrapper.getJndiAndSpringDataSources();
420 for (final Map.Entry<String, DataSource> entry : dataSources.entrySet()) {
421 final String name = entry.getKey();
422 final DataSource dataSource = entry.getValue();
423
424
425
426 try (Connection connection = dataSource.getConnection()) {
427 if (result.length() > 0) {
428 result.append("\n\n");
429 }
430 result.append(name).append(":\n");
431 appendDataBaseVersion(result, connection);
432
433 }
434 }
435 } catch (final Exception e) {
436 result.append(e);
437 }
438 if (result.length() > 0) {
439 return result.toString();
440 }
441 return null;
442 }
443
444 private static void appendDataBaseVersion(StringBuilder result, Connection connection)
445 throws SQLException {
446 final DatabaseMetaData metaData = connection.getMetaData();
447
448 final String url = metaData.getURL();
449 if (url != null) {
450
451
452
453 result.append(url.replaceAll("(?<=password=).*", "\\$")).append('\n');
454 }
455 result.append(metaData.getDatabaseProductName()).append(", ")
456 .append(metaData.getDatabaseProductVersion()).append('\n');
457 result.append("Driver JDBC:\n").append(metaData.getDriverName()).append(", ")
458 .append(metaData.getDriverVersion());
459 }
460
461 private static String buildDataSourceDetails() {
462 final Map<String, Map<String, Object>> dataSourcesProperties = JdbcWrapper
463 .getBasicDataSourceProperties();
464 final StringBuilder sb = new StringBuilder();
465 for (final Map.Entry<String, Map<String, Object>> entry : dataSourcesProperties
466 .entrySet()) {
467 final Map<String, Object> dataSourceProperties = entry.getValue();
468 if (dataSourceProperties.isEmpty()) {
469 continue;
470 }
471 if (sb.length() > 0) {
472 sb.append('\n');
473 }
474 final String name = entry.getKey();
475 if (name != null) {
476 sb.append(name).append(":\n");
477 }
478 for (final Map.Entry<String, Object> propertyEntry : dataSourceProperties.entrySet()) {
479 sb.append(propertyEntry.getKey()).append(" = ").append(propertyEntry.getValue())
480 .append('\n');
481 }
482 }
483 if (sb.length() == 0) {
484 return null;
485 }
486 return sb.toString();
487 }
488
489 public MemoryInformations getMemoryInformations() {
490 return memoryInformations;
491 }
492
493 public List<TomcatInformations> getTomcatInformationsList() {
494 return tomcatInformationsList;
495 }
496
497 public int getSessionCount() {
498 return sessionCount;
499 }
500
501 long getSessionAgeSum() {
502 return sessionAgeSum;
503 }
504
505 public long getSessionMeanAgeInMinutes() {
506 if (sessionCount > 0) {
507 return sessionAgeSum / sessionCount / 60000;
508 }
509 return -1;
510 }
511
512 public int getActiveThreadCount() {
513 return activeThreadCount;
514 }
515
516 public int getUsedConnectionCount() {
517 return usedConnectionCount;
518 }
519
520 public int getActiveConnectionCount() {
521 return activeConnectionCount;
522 }
523
524 public int getMaxConnectionCount() {
525 return maxConnectionCount;
526 }
527
528 public long getTransactionCount() {
529 return transactionCount;
530 }
531
532 public double getUsedConnectionPercentage() {
533 if (maxConnectionCount > 0) {
534 return 100d * usedConnectionCount / maxConnectionCount;
535 }
536 return -1d;
537 }
538
539 public long getProcessCpuTimeMillis() {
540 return processCpuTimeMillis;
541 }
542
543 public double getSystemLoadAverage() {
544 return systemLoadAverage;
545 }
546
547 public double getSystemCpuLoad() {
548 return systemCpuLoad;
549 }
550
551 public long getUnixOpenFileDescriptorCount() {
552 return unixOpenFileDescriptorCount;
553 }
554
555 public long getUnixMaxFileDescriptorCount() {
556 return unixMaxFileDescriptorCount;
557 }
558
559 public double getUnixOpenFileDescriptorPercentage() {
560 if (unixOpenFileDescriptorCount >= 0) {
561 return 100d * unixOpenFileDescriptorCount / unixMaxFileDescriptorCount;
562 }
563 return -1d;
564 }
565
566 public String getHost() {
567 return host;
568 }
569
570 public String getOS() {
571 return os;
572 }
573
574 public int getAvailableProcessors() {
575 return availableProcessors;
576 }
577
578 public String getJavaVersion() {
579 return javaVersion;
580 }
581
582 public String getJvmVersion() {
583 return jvmVersion;
584 }
585
586 public String getPID() {
587 return pid;
588 }
589
590 public String getServerInfo() {
591 return serverInfo;
592 }
593
594 public String getContextPath() {
595 return contextPath;
596 }
597
598 public String getContextDisplayName() {
599 return contextDisplayName;
600 }
601
602 public String getWebappVersion() {
603 return webappVersion;
604 }
605
606 public Date getStartDate() {
607 return startDate;
608 }
609
610 public String getJvmArguments() {
611 return jvmArguments;
612 }
613
614 public long getFreeDiskSpaceInTemp() {
615 return freeDiskSpaceInTemp;
616 }
617
618 public long getUsableDiskSpaceInTemp() {
619 return usableDiskSpaceInTemp;
620 }
621
622 public int getThreadCount() {
623 return threadCount;
624 }
625
626 public int getPeakThreadCount() {
627 return peakThreadCount;
628 }
629
630 public long getTotalStartedThreadCount() {
631 return totalStartedThreadCount;
632 }
633
634 public String getDataBaseVersion() {
635 return dataBaseVersion;
636 }
637
638 public String getDataSourceDetails() {
639 return dataSourceDetails;
640 }
641
642 public List<ThreadInformations> getThreadInformationsList() {
643
644 final List<ThreadInformations> result = new ArrayList<>(threadInformationsList);
645 Collections.sort(result, new ThreadInformationsComparator());
646 return Collections.unmodifiableList(result);
647 }
648
649 public List<CacheInformations> getCacheInformationsList() {
650
651 final List<CacheInformations> result = new ArrayList<>(cacheInformationsList);
652 Collections.sort(result, new CacheInformationsComparator());
653 return Collections.unmodifiableList(result);
654 }
655
656 public List<JCacheInformations> getJCacheInformationsList() {
657
658 final List<JCacheInformations> result = new ArrayList<>(jcacheInformationsList);
659 Collections.sort(result, new JCacheInformationsComparator());
660 return Collections.unmodifiableList(result);
661 }
662
663 public List<JobInformations> getJobInformationsList() {
664
665 final List<JobInformations> result = new ArrayList<>(jobInformationsList);
666 Collections.sort(result, new JobInformationsComparator());
667 return Collections.unmodifiableList(result);
668 }
669
670 public int getCurrentlyExecutingJobCount() {
671 int result = 0;
672 for (final JobInformations jobInformations : jobInformationsList) {
673 if (jobInformations.isCurrentlyExecuting()) {
674 result++;
675 }
676 }
677 return result;
678 }
679
680 public List<HsErrPid> getHsErrPidList() {
681 if (hsErrPidList != null) {
682
683 final List<HsErrPid> result = new ArrayList<>(hsErrPidList);
684 Collections.sort(result, new HsErrPidComparator());
685 return Collections.unmodifiableList(result);
686 }
687 return null;
688 }
689
690 public boolean isStackTraceEnabled() {
691 for (final ThreadInformations threadInformations : threadInformationsList) {
692 final List<StackTraceElement> stackTrace = threadInformations.getStackTrace();
693 if (stackTrace != null && !stackTrace.isEmpty()) {
694 return true;
695 }
696 }
697 return false;
698 }
699
700 public boolean isCacheEnabled() {
701 return cacheInformationsList != null && !cacheInformationsList.isEmpty();
702 }
703
704 public boolean isJCacheEnabled() {
705 return jcacheInformationsList != null && !jcacheInformationsList.isEmpty();
706 }
707
708 public boolean isJobEnabled() {
709 return jobInformationsList != null && !jobInformationsList.isEmpty();
710 }
711
712 public boolean isSpringBeansEnabled() {
713 return springBeanExists;
714 }
715
716 private static boolean isSpringAvailable() {
717 try {
718 Class.forName("org.springframework.context.ApplicationContextAware");
719 return true;
720 } catch (final ClassNotFoundException e) {
721 return false;
722 }
723 }
724
725
726 @Override
727 public String toString() {
728 return getClass().getSimpleName() + "[pid=" + getPID() + ", host=" + getHost()
729 + ", javaVersion=" + getJavaVersion() + ", serverInfo=" + getServerInfo() + ']';
730 }
731 }
732