1
18 package net.bull.javamelody.internal.model;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.lang.management.ManagementFactory;
23 import java.lang.reflect.Method;
24 import java.text.DateFormat;
25 import java.text.SimpleDateFormat;
26 import java.util.Date;
27 import java.util.List;
28 import java.util.Locale;
29
30 import javax.cache.Cache.Entry;
31 import javax.cache.Caching;
32 import javax.cache.spi.CachingProvider;
33 import javax.management.JMException;
34 import javax.management.ObjectName;
35 import javax.management.openmbean.CompositeData;
36 import javax.servlet.http.HttpSession;
37
38 import org.quartz.JobDetail;
39 import org.quartz.Scheduler;
40
41 import net.bull.javamelody.Parameter;
42 import net.bull.javamelody.SessionListener;
43 import net.bull.javamelody.internal.common.I18N;
44 import net.bull.javamelody.internal.common.InputOutput;
45 import net.bull.javamelody.internal.common.LOG;
46 import net.bull.javamelody.internal.common.Parameters;
47 import net.bull.javamelody.internal.web.MailReport;
48 import net.sf.ehcache.Cache;
49 import net.sf.ehcache.CacheManager;
50
51
56 public enum Action {
57
58 MAIL_TEST(""),
59
60
61 CLEAR_COUNTER("http"),
62
63
64 GC("systeminfo"),
65
66
67 INVALIDATE_SESSIONS("systeminfo"),
68
69
70 INVALIDATE_SESSION(""),
71
72
73 LOGOUT(""),
74
75
76 HEAP_DUMP("systeminfo"),
77
78
79 CLEAR_CACHES("caches"),
80
81
82 CLEAR_CACHE("caches"),
83
84
85 CLEAR_CACHE_KEY("caches"),
86
87
88 CLEAR_JCACHES("caches"),
89
90
91 CLEAR_JCACHE("caches"),
92
93
94 CLEAR_JCACHE_KEY("caches"),
95
96
97 KILL_THREAD("threads"),
98
99
100 SEND_THREAD_INTERRUPT("threads"),
101
102
103 PAUSE_JOB("jobs"),
104
105
106 RESUME_JOB("jobs"),
107
108
109 CLEAR_HOTSPOTS(""),
110
111
112 PURGE_OBSOLETE_FILES("bottom");
113
114
117 public static final boolean GC_ENABLED = !ManagementFactory.getRuntimeMXBean()
118 .getInputArguments().contains("-XX:+DisableExplicitGC");
119
120 static final String JAVA_VENDOR = System.getProperty("java.vendor");
121 static final String JAVA_VM_VENDOR = System.getProperty("java.vm.vendor");
122
123 private static final String ALL = "all";
124
125
129 private final String contextName;
130
131 Action(String contextName) {
132 this.contextName = contextName;
133 }
134
135 public String getContextName(String counterName) {
136 if (this == CLEAR_COUNTER && !ALL.equalsIgnoreCase(counterName)) {
137 return counterName;
138 }
139 return contextName;
140 }
141
142
147 public static Action valueOfIgnoreCase(String action) {
148 return valueOf(action.toUpperCase(Locale.ENGLISH).trim());
149 }
150
151
154 public static void checkSystemActionsEnabled() {
155 if (!Parameters.isSystemActionsEnabled()) {
156 throw new IllegalStateException(I18N.getString("Actions_non_activees"));
157 }
158 }
159
160
161 public String execute(Collector collector, CollectorServer collectorServer, String counterName,
162 String sessionId, String threadId, String jobId, String cacheId) throws IOException {
163 return execute(collector, collectorServer, null, counterName, sessionId, threadId, jobId,
164 cacheId, null);
165 }
166
167
168
169 String execute(Collector collector, CollectorServer collectorServer, HttpSession currentSession,
170 String counterName, String sessionId, String threadId, String jobId, String cacheId)
171 throws IOException {
172
173 return execute(collector, collectorServer, currentSession, counterName, sessionId, threadId,
174 jobId, cacheId, null);
175 }
176
177
192
193 public String execute(Collector collector, CollectorServer collectorServer,
194 HttpSession currentSession, String counterName, String sessionId, String threadId,
195 String jobId, String cacheId, String cacheKey) throws IOException {
196
197 final String messageForReport;
198 switch (this) {
199 case CLEAR_COUNTER:
200 assert collector != null;
201 assert counterName != null;
202 messageForReport = clearCounter(collector, counterName);
203 break;
204 case MAIL_TEST:
205 assert collector != null;
206 messageForReport = mailTest(collector, collectorServer);
207 break;
208 case GC:
209 if (GC_ENABLED) {
210
211 final long kbFreed = gc();
212 final long stillUsed = (Runtime.getRuntime().totalMemory()
213 - Runtime.getRuntime().freeMemory()) / 1024;
214 messageForReport = I18N.getFormattedString("ramasse_miette_execute", kbFreed,
215 stillUsed);
216 } else {
217 messageForReport = I18N.getString("ramasse_miette_desactive");
218 }
219 break;
220 case HEAP_DUMP:
221 if (JAVA_VENDOR.contains("IBM") || JAVA_VM_VENDOR.contains("OpenJ9")) {
222 ibmHeapDump();
223 messageForReport = I18N.getString("heap_dump_genere_ibm");
224 } else {
225
226
227
228 final File heapDump = heapDump();
229 final File zipFile = new File(heapDump.getParentFile(),
230 heapDump.getName() + ".zip");
231 InputOutput.zipFile(heapDump, zipFile);
232 InputOutput.deleteFile(heapDump);
233 String message = "";
234 if (Parameter.HEAP_DUMP_S3_BUCKETNAME.getValue() != null) {
235 try {
236 S3.upload(zipFile, Parameter.HEAP_DUMP_S3_BUCKETNAME.getValue());
237 message = I18N.getFormattedString("heap_dump_uploaded_to_s3",
238 zipFile.getName()) + ' ';
239 } catch (final IOException e) {
240 message = "Failed to upload heap dump to S3 - " + e.getMessage() + '\n';
241 }
242 }
243 final String path = zipFile.getPath();
244 messageForReport = message
245 + I18N.getFormattedString("heap_dump_genere", path.replace('\\', '/'));
246 }
247 break;
248 case INVALIDATE_SESSIONS:
249
250 SessionListener.invalidateAllSessionsExceptCurrentSession(currentSession);
251 messageForReport = I18N.getString("sessions_http_invalidees");
252 break;
253 case INVALIDATE_SESSION:
254
255 assert sessionId != null;
256 SessionListener.invalidateSession(sessionId);
257 messageForReport = I18N.getString("session_http_invalidee");
258 break;
259 case LOGOUT:
260
261 if (currentSession != null) {
262 SessionListener.invalidateSession(currentSession.getId());
263 }
264 messageForReport = I18N.getString("logged_out");
265 break;
266 case CLEAR_CACHES:
267 clearCaches();
268 messageForReport = I18N.getString("caches_purges");
269 break;
270 case CLEAR_CACHE:
271 clearCache(cacheId);
272 messageForReport = I18N.getFormattedString("cache_purge", cacheId);
273 break;
274 case CLEAR_CACHE_KEY:
275 clearCacheKey(cacheId, cacheKey);
276 messageForReport = I18N.getFormattedString("cache_key_purge", cacheId, cacheKey);
277 break;
278 case CLEAR_JCACHES:
279 clearJCaches();
280 messageForReport = I18N.getString("caches_purges");
281 break;
282 case CLEAR_JCACHE:
283 clearJCache(cacheId);
284 messageForReport = I18N.getFormattedString("cache_purge", cacheId);
285 break;
286 case CLEAR_JCACHE_KEY:
287 clearJCacheKey(cacheId, cacheKey);
288 messageForReport = I18N.getFormattedString("cache_key_purge", cacheId, cacheKey);
289 break;
290 case KILL_THREAD:
291 assert threadId != null;
292 messageForReport = killThread(threadId);
293 break;
294 case SEND_THREAD_INTERRUPT:
295 assert threadId != null;
296 messageForReport = sendThreadInterrupt(threadId);
297 break;
298 case PAUSE_JOB:
299 assert jobId != null;
300 messageForReport = pauseJob(jobId);
301 break;
302 case RESUME_JOB:
303 assert jobId != null;
304 messageForReport = resumeJob(jobId);
305 break;
306 case CLEAR_HOTSPOTS:
307 assert collector.getSamplingProfiler() != null;
308 collector.getSamplingProfiler().clear();
309 messageForReport = I18N.getString("hotspots_cleared");
310 break;
311 case PURGE_OBSOLETE_FILES:
312 assert collector != null;
313 collector.deleteObsoleteFiles();
314 messageForReport = I18N.getString("fichiers_obsoletes_purges") + '\n'
315 + I18N.getString("Usage_disque") + ": "
316 + (collector.getDiskUsage() / 1024 / 1024 + 1) + ' ' + I18N.getString("Mo");
317 break;
318 default:
319 throw new IllegalStateException(toString());
320 }
321 if (messageForReport != null) {
322
323 LOG.debug("Action '" + this + "' executed. Result: "
324 + messageForReport.replace('\n', ' '));
325 }
326 return messageForReport;
327 }
328
329 private String clearCounter(Collector collector, String counterName) {
330 final String messageForReport;
331 if (ALL.equalsIgnoreCase(counterName)) {
332 for (final Counter counter : collector.getCounters()) {
333 collector.clearCounter(counter.getName());
334 }
335 messageForReport = I18N.getFormattedString("Toutes_statistiques_reinitialisees",
336 counterName);
337 } else {
338
339 collector.clearCounter(counterName);
340 messageForReport = I18N.getFormattedString("Statistiques_reinitialisees", counterName);
341 }
342 return messageForReport;
343 }
344
345 private String mailTest(Collector collector, CollectorServer collectorServer) {
346
347 if (!Parameters.isPdfEnabled()) {
348 throw new IllegalStateException("itext classes not found: add the itext dependency");
349 }
350 if (Parameter.MAIL_SESSION.getValue() == null) {
351 throw new IllegalStateException(
352 "mail-session has no value: add the mail-session parameter");
353 }
354 if (Parameter.ADMIN_EMAILS.getValue() == null) {
355 throw new IllegalStateException(
356 "admin-emails has no value: add the admin-emails parameter");
357 }
358 try {
359 if (collectorServer == null) {
360
361 new MailReport().sendReportMailForLocalServer(collector, Period.JOUR);
362 } else {
363
364 new MailReport().sendReportMail(collector, true, collectorServer
365 .getJavaInformationsByApplication(collector.getApplication()), Period.JOUR);
366 }
367 } catch (final Exception e) {
368 throw new RuntimeException(e);
369 }
370 return "Mail sent with pdf report for the day to admins";
371 }
372
373 private File heapDump() {
374 try {
375 final ObjectName objectName = new ObjectName(
376 "com.sun.management:type=HotSpotDiagnostic");
377 final CompositeData vmOption = (CompositeData) MBeansAccessor.invoke(objectName,
378 "getVMOption", new Object[] { "HeapDumpPath" }, new Class[] { String.class });
379 final String heapDumpPath;
380 if (vmOption == null) {
381 heapDumpPath = null;
382 } else {
383 heapDumpPath = (String) vmOption.get("value");
384 }
385 final String path;
386 if (heapDumpPath == null || heapDumpPath.isEmpty()) {
387 path = Parameters.TEMPORARY_DIRECTORY.getPath();
388 } else {
389
390
391 final File file = new File(heapDumpPath);
392 if (file.exists()) {
393 if (file.isDirectory()) {
394 path = heapDumpPath;
395 } else {
396 path = file.getParent();
397 }
398 } else {
399 if (!file.mkdirs()) {
400 throw new IllegalStateException("Can't create directory " + file.getPath());
401 }
402 path = heapDumpPath;
403 }
404 }
405 final DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss",
406 Locale.getDefault());
407 final File heapDumpFile = new File(path, "heapdump-" + Parameters.getHostName() + '-'
408 + PID.getPID() + '-' + dateFormat.format(new Date()) + ".hprof");
409 if (heapDumpFile.exists()) {
410 try {
411
412
413 Thread.sleep(1000);
414 } catch (final InterruptedException e) {
415 throw new IllegalStateException(e);
416 }
417 return heapDump();
418 }
419 final boolean gcBeforeHeapDump = true;
420 MBeansAccessor.invoke(objectName, "dumpHeap",
421 new Object[] { heapDumpFile.getPath(), gcBeforeHeapDump },
422 new Class[] { String.class, boolean.class });
423 return heapDumpFile;
424 } catch (final JMException e) {
425 throw new IllegalStateException(e);
426 }
427 }
428
429 private void ibmHeapDump() {
430 try {
431 final Class<?> dumpClass = getClass().getClassLoader().loadClass("com.ibm.jvm.Dump");
432 final Class<?>[] argTypes = null;
433 final Method dump = dumpClass.getMethod("HeapDump", argTypes);
434 final Object[] args = null;
435 dump.invoke(null, args);
436 } catch (final Exception e) {
437 throw new IllegalStateException(e);
438 }
439 }
440
441
442 @SuppressWarnings("all")
443 private long gc() {
444 final long before = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
445 Runtime.getRuntime().gc();
446 final long after = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
447 return (before - after) / 1024;
448 }
449
450 private void clearCaches() {
451 final List<CacheManager> allCacheManagers = CacheManager.ALL_CACHE_MANAGERS;
452 for (final CacheManager cacheManager : allCacheManagers) {
453 cacheManager.clearAll();
454 }
455 }
456
457 private void clearCache(String cacheId) {
458 final List<CacheManager> allCacheManagers = CacheManager.ALL_CACHE_MANAGERS;
459 for (final CacheManager cacheManager : allCacheManagers) {
460 final Cache cache = cacheManager.getCache(cacheId);
461 if (cache != null) {
462 cache.removeAll();
463 }
464 }
465 }
466
467 private void clearCacheKey(String cacheId, String cacheKey) {
468 final List<CacheManager> allCacheManagers = CacheManager.ALL_CACHE_MANAGERS;
469 for (final CacheManager cacheManager : allCacheManagers) {
470 final Cache cache = cacheManager.getCache(cacheId);
471 if (cache != null) {
472 final boolean removed = cache.remove(cacheKey);
473 if (!removed) {
474
475 for (final Object key : cache.getKeys()) {
476 if (key != null && key.toString().equals(cacheKey)) {
477 cache.remove(key);
478 break;
479 }
480 }
481 }
482 }
483 }
484 }
485
486 private void clearJCaches() {
487 for (final CachingProvider cachingProvider : Caching.getCachingProviders()) {
488 final javax.cache.CacheManager cacheManager = cachingProvider.getCacheManager();
489 for (final String cacheName : cacheManager.getCacheNames()) {
490 cacheManager.getCache(cacheName).clear();
491 }
492 }
493 }
494
495 private void clearJCache(String cacheId) {
496 for (final CachingProvider cachingProvider : Caching.getCachingProviders()) {
497 final javax.cache.CacheManager cacheManager = cachingProvider.getCacheManager();
498 for (final String cacheName : cacheManager.getCacheNames()) {
499 if (cacheName.equals(cacheId)) {
500
501 cacheManager.getCache(cacheId).clear();
502 break;
503 }
504 }
505 }
506 }
507
508 private void clearJCacheKey(String cacheId, String cacheKey) {
509 for (final CachingProvider cachingProvider : Caching.getCachingProviders()) {
510 final javax.cache.CacheManager cacheManager = cachingProvider.getCacheManager();
511 for (final String cacheName : cacheManager.getCacheNames()) {
512 if (cacheName.equals(cacheId)) {
513
514 final javax.cache.Cache<Object, Object> cache = cacheManager.getCache(cacheId);
515 final boolean removed = cache.remove(cacheKey);
516 if (!removed) {
517
518 for (final Entry<Object, Object> entry : cache) {
519 final Object key = entry.getKey();
520 if (key != null && key.toString().equals(cacheKey)) {
521 cache.remove(key);
522 break;
523 }
524 }
525 }
526 }
527 }
528 }
529 }
530
531 private String killThread(String globalThreadId) {
532 final Long threadId = getThreadIdFromGlobalThreadIdIfSameJvm(globalThreadId);
533 if (threadId != null) {
534 final List<Thread> threads = JavaInformations.getThreadsFromThreadGroups();
535 for (final Thread thread : threads) {
536 if (thread.getId() == threadId.longValue()) {
537 if (thread.getName().startsWith("javamelody")) {
538 return "I will not kill myself";
539 }
540 stopThread(thread);
541 return I18N.getFormattedString("Thread_tue", thread.getName());
542 }
543 }
544 return I18N.getString("Thread_non_trouve");
545 }
546
547
548 return null;
549 }
550
551 @SuppressWarnings("deprecation")
552 private void stopThread(Thread thread) {
553
554 thread.stop();
555 }
556
557 private String sendThreadInterrupt(String globalThreadId) {
558 final Long threadId = getThreadIdFromGlobalThreadIdIfSameJvm(globalThreadId);
559 if (threadId != null) {
560 final List<Thread> threads = JavaInformations.getThreadsFromThreadGroups();
561 for (final Thread thread : threads) {
562 if (thread.getId() == threadId.longValue()) {
563 if (thread.getName().startsWith("javamelody")) {
564 return "I will not interrupt myself";
565 }
566 thread.interrupt();
567 return I18N.getFormattedString("thread_interrupt_sent", thread.getName());
568 }
569 }
570 return I18N.getString("Thread_non_trouve");
571 }
572
573
574 return null;
575 }
576
577 private Long getThreadIdFromGlobalThreadIdIfSameJvm(String globalThreadId) {
578 final String[] values = globalThreadId.split("_");
579 if (values.length != 3) {
580 throw new IllegalArgumentException(globalThreadId);
581 }
582
583 if (values[0].equals(PID.getPID()) && values[1].equals(Parameters.getHostAddress())) {
584 return Long.valueOf(values[2]);
585 }
586 return null;
587 }
588
589 private String pauseJob(String jobId) {
590 if (ALL.equalsIgnoreCase(jobId)) {
591 pauseAllJobs();
592 return I18N.getString("all_jobs_paused");
593 }
594
595 final String[] values = jobId.split("_");
596 if (values.length != 3) {
597 throw new IllegalArgumentException(jobId);
598 }
599
600 if (values[0].equals(PID.getPID()) && values[1].equals(Parameters.getHostAddress())) {
601 if (pauseJobById(Integer.parseInt(values[2]))) {
602 return I18N.getString("job_paused");
603 }
604 return I18N.getString("job_notfound");
605 }
606
607
608 return null;
609 }
610
611 private boolean pauseJobById(int myJobId) {
612 try {
613 final QuartzAdapter quartzAdapter = QuartzAdapter.getSingleton();
614 for (final Scheduler scheduler : JobInformations.getAllSchedulers()) {
615 for (final JobDetail jobDetail : quartzAdapter.getAllJobsOfScheduler(scheduler)) {
616 if (quartzAdapter.getJobFullName(jobDetail).hashCode() == myJobId) {
617 quartzAdapter.pauseJob(jobDetail, scheduler);
618 return true;
619 }
620 }
621 }
622 return false;
623 } catch (final Exception e) {
624 throw new IllegalStateException(e);
625 }
626 }
627
628 private void pauseAllJobs() {
629 try {
630 for (final Scheduler scheduler : JobInformations.getAllSchedulers()) {
631 scheduler.pauseAll();
632 }
633 } catch (final Exception e) {
634 throw new IllegalStateException(e);
635 }
636 }
637
638 private String resumeJob(String jobId) {
639 if (ALL.equalsIgnoreCase(jobId)) {
640 resumeAllJobs();
641 return I18N.getString("all_jobs_resumed");
642 }
643 final String[] values = jobId.split("_");
644 if (values.length != 3) {
645 throw new IllegalArgumentException(jobId);
646 }
647
648 if (values[0].equals(PID.getPID()) && values[1].equals(Parameters.getHostAddress())) {
649 if (resumeJobById(Integer.parseInt(values[2]))) {
650 return I18N.getString("job_resumed");
651 }
652 return I18N.getString("job_notfound");
653 }
654
655
656 return null;
657 }
658
659 private boolean resumeJobById(int myJobId) {
660 try {
661 final QuartzAdapter quartzAdapter = QuartzAdapter.getSingleton();
662 for (final Scheduler scheduler : JobInformations.getAllSchedulers()) {
663 for (final JobDetail jobDetail : quartzAdapter.getAllJobsOfScheduler(scheduler)) {
664 if (quartzAdapter.getJobFullName(jobDetail).hashCode() == myJobId) {
665 quartzAdapter.resumeJob(jobDetail, scheduler);
666 return true;
667 }
668 }
669 }
670 return false;
671 } catch (final Exception e) {
672 throw new IllegalStateException(e);
673 }
674 }
675
676 private void resumeAllJobs() {
677 try {
678 for (final Scheduler scheduler : JobInformations.getAllSchedulers()) {
679 scheduler.resumeAll();
680 }
681 } catch (final Exception e) {
682 throw new IllegalStateException(e);
683 }
684 }
685 }
686