PageRenderTime 28ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/javamelody-core/src/main/java/net/bull/javamelody/Action.java

https://gitlab.com/tuandung.bui/javamelody
Java | 525 lines | 384 code | 47 blank | 94 comment | 80 complexity | 584d106c8bf50db416eb757d956cf154 MD5 | raw file
  1. /*
  2. * Copyright 2008-2016 by Emeric Vernat
  3. *
  4. * This file is part of Java Melody.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package net.bull.javamelody;
  19. import java.io.File;
  20. import java.io.IOException;
  21. import java.lang.management.ManagementFactory;
  22. import java.lang.reflect.Method;
  23. import java.text.DateFormat;
  24. import java.text.SimpleDateFormat;
  25. import java.util.Date;
  26. import java.util.List;
  27. import java.util.Locale;
  28. import javax.management.JMException;
  29. import javax.management.MBeanServer;
  30. import javax.management.ObjectInstance;
  31. import javax.management.ObjectName;
  32. import javax.servlet.http.HttpSession;
  33. import org.quartz.JobDetail;
  34. import org.quartz.Scheduler;
  35. import net.sf.ehcache.Cache;
  36. import net.sf.ehcache.CacheManager;
  37. /**
  38. * Énumération des actions possibles dans l'IHM.
  39. * @author Emeric Vernat
  40. * @author <a href="mailto:davidkarlsen@gmail.com">David J. M. Karlsen (IBM heapdump support)<a>
  41. */
  42. enum Action { // NOPMD
  43. /** Test d'envoi du rapport pdf par mail. */
  44. MAIL_TEST(""),
  45. /** Réinitialisation d'un compteur non périodique. */
  46. CLEAR_COUNTER("http"),
  47. /** Garbage Collect. */
  48. GC("systeminfo"),
  49. /** Invalidations des sessions http. */
  50. INVALIDATE_SESSIONS("systeminfo"),
  51. /** Invalidation d'une session http. */
  52. INVALIDATE_SESSION(""),
  53. /** Invalidation de la session http courante. */
  54. LOGOUT(""),
  55. /** Heap dump. */
  56. HEAP_DUMP("systeminfo"),
  57. /** Purge le contenu de tous les caches (ie, for ALL_CACHE_MANAGERS {cacheManager.clearAll()}). */
  58. CLEAR_CACHES("caches"),
  59. /** Purge le contenu d'un cache. */
  60. CLEAR_CACHE("caches"),
  61. /** Tue un thread java. */
  62. KILL_THREAD("threads"),
  63. /** Met un job quartz en pause. */
  64. PAUSE_JOB("jobs"),
  65. /** Enlève la pause d'un job quartz. */
  66. RESUME_JOB("jobs"),
  67. /** Réinitialisation des hotspots. */
  68. CLEAR_HOTSPOTS(""),
  69. /** Purge les fichiers .rrd et .ser.gz obsolètes. */
  70. PURGE_OBSOLETE_FILES("bottom");
  71. static final String JAVA_VENDOR = System.getProperty("java.vendor");
  72. /**
  73. * Booléen selon que l'action 'Garbage collector' est possible.
  74. */
  75. static final boolean GC_ENABLED = !ManagementFactory.getRuntimeMXBean().getInputArguments()
  76. .contains("-XX:+DisableExplicitGC");
  77. private static final String ALL = "all";
  78. /**
  79. * Nom du contexte dans lequel est exécutée l'action
  80. * (servira dans l'url pour replacer la page html sur l'anchor de même nom)
  81. */
  82. private final String contextName;
  83. Action(String contextName) {
  84. this.contextName = contextName;
  85. }
  86. String getContextName(String counterName) {
  87. if (this == CLEAR_COUNTER && !ALL.equalsIgnoreCase(counterName)) {
  88. return counterName;
  89. }
  90. return contextName;
  91. }
  92. /**
  93. * Convertit le code d'une action en énumération de l'action.
  94. * @param action String
  95. * @return Action
  96. */
  97. static Action valueOfIgnoreCase(String action) {
  98. return valueOf(action.toUpperCase(Locale.ENGLISH).trim());
  99. }
  100. /**
  101. * Vérifie que le paramètre pour activer les actions systèmes est positionné.
  102. */
  103. static void checkSystemActionsEnabled() {
  104. if (!Parameters.isSystemActionsEnabled()) {
  105. throw new IllegalStateException(I18N.getString("Actions_non_activees"));
  106. }
  107. }
  108. // méthode conservée pour compatibilité ascendante
  109. // CHECKSTYLE:OFF
  110. String execute(Collector collector, CollectorServer collectorServer, String counterName, // NOPMD
  111. String sessionId, String threadId, String jobId, String cacheId) throws IOException {
  112. // CHECKSTYLE:ON
  113. return execute(collector, collectorServer, null, counterName, sessionId, threadId, jobId,
  114. cacheId);
  115. }
  116. /**
  117. * Exécute l'action.
  118. * @param collector Collector pour une réinitialisation et test de mail
  119. * @param collectorServer Serveur de collecte pour test de mail (null s'il n'y en a pas)
  120. * @param currentSession session http de l'utilisateur exécutant l'action (null sinon)
  121. * @param counterName Nom du compteur pour une réinitialisation
  122. * @param sessionId Identifiant de session pour invalidation (null sinon)
  123. * @param threadId Identifiant du thread sous la forme pid_ip_id
  124. * @param jobId Identifiant du job sous la forme pid_ip_id
  125. * @param cacheId Identifiant du cache à vider
  126. * @return Message de résultat
  127. * @throws IOException e
  128. * @since 1.49
  129. */
  130. // CHECKSTYLE:OFF
  131. String execute(Collector collector, CollectorServer collectorServer, HttpSession currentSession, // NOPMD
  132. String counterName, String sessionId, String threadId, String jobId, String cacheId)
  133. throws IOException {
  134. // CHECKSTYLE:ON
  135. final String messageForReport;
  136. switch (this) {
  137. case CLEAR_COUNTER:
  138. assert collector != null;
  139. assert counterName != null;
  140. messageForReport = clearCounter(collector, counterName);
  141. break;
  142. case MAIL_TEST:
  143. assert collector != null;
  144. messageForReport = mailTest(collector, collectorServer);
  145. break;
  146. case GC:
  147. if (GC_ENABLED) {
  148. // garbage collector
  149. final long kbFreed = gc();
  150. final long stillUsed = (Runtime.getRuntime().totalMemory()
  151. - Runtime.getRuntime().freeMemory()) / 1024;
  152. messageForReport = I18N.getFormattedString("ramasse_miette_execute", kbFreed,
  153. stillUsed);
  154. } else {
  155. messageForReport = I18N.getString("ramasse_miette_desactive");
  156. }
  157. break;
  158. case HEAP_DUMP:
  159. if (JAVA_VENDOR.contains("IBM")) {
  160. ibmHeapDump();
  161. messageForReport = I18N.getString("heap_dump_genere_ibm");
  162. } else {
  163. // heap dump à générer dans le répertoire temporaire sur le serveur
  164. // avec un suffixe contenant le host, la date et l'heure et avec une extension hprof
  165. // (utiliser jvisualvm du jdk ou MAT d'eclipse en standalone ou en plugin)
  166. final String heapDumpPath = heapDump().getPath();
  167. messageForReport = I18N.getFormattedString("heap_dump_genere",
  168. heapDumpPath.replace('\\', '/'));
  169. }
  170. break;
  171. case INVALIDATE_SESSIONS:
  172. // invalidation des sessions http
  173. SessionListener.invalidateAllSessionsExceptCurrentSession(currentSession);
  174. messageForReport = I18N.getString("sessions_http_invalidees");
  175. break;
  176. case INVALIDATE_SESSION:
  177. // invalidation d'une session http
  178. assert sessionId != null;
  179. SessionListener.invalidateSession(sessionId);
  180. messageForReport = I18N.getString("session_http_invalidee");
  181. break;
  182. case LOGOUT:
  183. // invalidation de la session http courante
  184. if (currentSession != null) {
  185. SessionListener.invalidateSession(currentSession.getId());
  186. }
  187. messageForReport = I18N.getString("logged_out");
  188. break;
  189. case CLEAR_CACHES:
  190. clearCaches();
  191. messageForReport = I18N.getString("caches_purges");
  192. break;
  193. case CLEAR_CACHE:
  194. clearCache(cacheId);
  195. messageForReport = I18N.getFormattedString("cache_purge", cacheId);
  196. break;
  197. case KILL_THREAD:
  198. assert threadId != null;
  199. messageForReport = killThread(threadId);
  200. break;
  201. case PAUSE_JOB:
  202. assert jobId != null;
  203. messageForReport = pauseJob(jobId);
  204. break;
  205. case RESUME_JOB:
  206. assert jobId != null;
  207. messageForReport = resumeJob(jobId);
  208. break;
  209. case CLEAR_HOTSPOTS:
  210. assert collector.getSamplingProfiler() != null;
  211. collector.getSamplingProfiler().clear();
  212. messageForReport = I18N.getString("hotspots_cleared");
  213. break;
  214. case PURGE_OBSOLETE_FILES:
  215. assert collector != null;
  216. collector.deleteObsoleteFiles();
  217. messageForReport = I18N.getString("fichiers_obsoletes_purges") + '\n'
  218. + I18N.getString("Usage_disque") + ": "
  219. + (collector.getDiskUsage() / 1024 / 1024 + 1) + ' ' + I18N.getString("Mo");
  220. break;
  221. default:
  222. throw new IllegalStateException(toString());
  223. }
  224. if (messageForReport != null) {
  225. // log pour information en debug
  226. LOG.debug("Action '" + this + "' executed. Result: "
  227. + messageForReport.replace('\n', ' '));
  228. }
  229. return messageForReport;
  230. }
  231. private String clearCounter(Collector collector, String counterName) {
  232. final String messageForReport;
  233. if (ALL.equalsIgnoreCase(counterName)) {
  234. for (final Counter counter : collector.getCounters()) {
  235. collector.clearCounter(counter.getName());
  236. }
  237. messageForReport = I18N.getFormattedString("Toutes_statistiques_reinitialisees",
  238. counterName);
  239. } else {
  240. // l'action Réinitialiser a été appelée pour un compteur
  241. collector.clearCounter(counterName);
  242. messageForReport = I18N.getFormattedString("Statistiques_reinitialisees", counterName);
  243. }
  244. return messageForReport;
  245. }
  246. private String mailTest(Collector collector, CollectorServer collectorServer) {
  247. // note: a priori, inutile de traduire cela
  248. if (!HtmlAbstractReport.isPdfEnabled()) {
  249. throw new IllegalStateException("itext classes not found: add the itext dependency");
  250. }
  251. if (Parameters.getParameter(Parameter.MAIL_SESSION) == null) {
  252. throw new IllegalStateException(
  253. "mail-session has no value: add the mail-session parameter");
  254. }
  255. if (Parameters.getParameter(Parameter.ADMIN_EMAILS) == null) {
  256. throw new IllegalStateException(
  257. "admin-emails has no value: add the admin-emails parameter");
  258. }
  259. try {
  260. if (collectorServer == null) {
  261. // serveur local
  262. new MailReport().sendReportMailForLocalServer(collector, Period.JOUR);
  263. } else {
  264. // serveur de collecte
  265. new MailReport().sendReportMail(collector, true, collectorServer
  266. .getJavaInformationsByApplication(collector.getApplication()), Period.JOUR);
  267. }
  268. } catch (final Exception e) {
  269. throw new RuntimeException(e); // NOPMD
  270. }
  271. return "Mail sent with pdf report for the day to admins";
  272. }
  273. private File heapDump() throws IOException {
  274. final boolean gcBeforeHeapDump = true;
  275. try {
  276. final MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer();
  277. final ObjectInstance instance = platformMBeanServer
  278. .getObjectInstance(new ObjectName("com.sun.management:type=HotSpotDiagnostic"));
  279. final Object mxBean = platformMBeanServer.instantiate(instance.getClassName());
  280. final Object vmOption = ((com.sun.management.HotSpotDiagnosticMXBean) mxBean)
  281. .getVMOption("HeapDumpPath");
  282. final String heapDumpPath;
  283. if (vmOption == null) {
  284. heapDumpPath = null;
  285. } else {
  286. heapDumpPath = ((com.sun.management.VMOption) vmOption).getValue();
  287. }
  288. final String path;
  289. if (heapDumpPath == null || heapDumpPath.length() == 0) {
  290. path = Parameters.TEMPORARY_DIRECTORY.getPath();
  291. } else {
  292. // -XX:HeapDumpPath=/tmp par exemple a été spécifié comme paramètre de VM.
  293. // Dans ce cas, on prend en compte ce paramètre "standard" de la JVM Hotspot
  294. final File file = new File(heapDumpPath);
  295. if (file.exists()) {
  296. if (file.isDirectory()) {
  297. path = heapDumpPath;
  298. } else {
  299. path = file.getParent();
  300. }
  301. } else {
  302. if (!file.mkdirs()) {
  303. throw new IllegalStateException("Can't create directory " + file.getPath());
  304. }
  305. path = heapDumpPath;
  306. }
  307. }
  308. final DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss",
  309. Locale.getDefault());
  310. final File heapDumpFile = new File(path, "heapdump-" + Parameters.getHostName() + '-'
  311. + PID.getPID() + '-' + dateFormat.format(new Date()) + ".hprof");
  312. if (heapDumpFile.exists()) {
  313. try {
  314. // si le fichier existe déjà, un heap dump a déjà été généré dans la même seconde
  315. // donc on attends 1 seconde pour créer le fichier avec un nom différent
  316. Thread.sleep(1000);
  317. } catch (final InterruptedException e) {
  318. throw new IllegalStateException(e);
  319. }
  320. return heapDump();
  321. }
  322. ((com.sun.management.HotSpotDiagnosticMXBean) mxBean).dumpHeap(heapDumpFile.getPath(),
  323. gcBeforeHeapDump);
  324. return heapDumpFile;
  325. } catch (final JMException e) {
  326. throw new IllegalStateException(e);
  327. }
  328. }
  329. private void ibmHeapDump() {
  330. try {
  331. final Class<?> dumpClass = getClass().getClassLoader().loadClass("com.ibm.jvm.Dump"); // NOPMD
  332. final Class<?>[] argTypes = null;
  333. final Method dump = dumpClass.getMethod("HeapDump", argTypes);
  334. final Object[] args = null;
  335. dump.invoke(null, args);
  336. } catch (final Exception e) {
  337. throw new IllegalStateException(e);
  338. }
  339. }
  340. // cette méthode doit s'appeler "gc" pour que findbugs ne fasse pas de warning
  341. @SuppressWarnings("all")
  342. private long gc() {
  343. final long before = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
  344. Runtime.getRuntime().gc();
  345. final long after = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
  346. return (before - after) / 1024;
  347. }
  348. @SuppressWarnings("unchecked")
  349. private void clearCaches() {
  350. final List<CacheManager> allCacheManagers = CacheManager.ALL_CACHE_MANAGERS;
  351. for (final CacheManager cacheManager : allCacheManagers) {
  352. cacheManager.clearAll();
  353. }
  354. }
  355. @SuppressWarnings("unchecked")
  356. private void clearCache(String cacheId) {
  357. final List<CacheManager> allCacheManagers = CacheManager.ALL_CACHE_MANAGERS;
  358. for (final CacheManager cacheManager : allCacheManagers) {
  359. final Cache cache = cacheManager.getCache(cacheId);
  360. if (cache != null) {
  361. cache.removeAll();
  362. }
  363. }
  364. }
  365. private String killThread(String threadId) {
  366. final String[] values = threadId.split("_");
  367. if (values.length != 3) {
  368. throw new IllegalArgumentException(threadId);
  369. }
  370. // rq : la syntaxe vérifiée ici doit être conforme à ThreadInformations.buildGlobalThreadId
  371. if (values[0].equals(PID.getPID()) && values[1].equals(Parameters.getHostAddress())) {
  372. final long myThreadId = Long.parseLong(values[2]);
  373. final List<Thread> threads = JavaInformations.getThreadsFromThreadGroups();
  374. for (final Thread thread : threads) {
  375. if (thread.getId() == myThreadId) {
  376. stopThread(thread);
  377. return I18N.getFormattedString("Thread_tue", thread.getName());
  378. }
  379. }
  380. return I18N.getString("Thread_non_trouve");
  381. }
  382. // cette action ne concernait pas cette JVM, donc on ne fait rien
  383. return null;
  384. }
  385. @SuppressWarnings("deprecation")
  386. private void stopThread(Thread thread) {
  387. // I know that it is unsafe and the user has been warned
  388. thread.stop();
  389. }
  390. private String pauseJob(String jobId) {
  391. if (ALL.equalsIgnoreCase(jobId)) {
  392. pauseAllJobs();
  393. return I18N.getString("all_jobs_paused");
  394. }
  395. final String[] values = jobId.split("_");
  396. if (values.length != 3) {
  397. throw new IllegalArgumentException(jobId);
  398. }
  399. // rq : la syntaxe vérifiée ici doit être conforme à JobInformations.buildGlobalJobId
  400. if (values[0].equals(PID.getPID()) && values[1].equals(Parameters.getHostAddress())) {
  401. if (pauseJobById(Integer.parseInt(values[2]))) {
  402. return I18N.getString("job_paused");
  403. }
  404. return I18N.getString("job_notfound");
  405. }
  406. // cette action ne concernait pas cette JVM, donc on ne fait rien
  407. return null;
  408. }
  409. private boolean pauseJobById(int myJobId) {
  410. try {
  411. for (final Scheduler scheduler : JobInformations.getAllSchedulers()) {
  412. for (final JobDetail jobDetail : JobInformations.getAllJobsOfScheduler(scheduler)) {
  413. if (QuartzAdapter.getSingleton().getJobFullName(jobDetail)
  414. .hashCode() == myJobId) {
  415. QuartzAdapter.getSingleton().pauseJob(jobDetail, scheduler);
  416. return true;
  417. }
  418. }
  419. }
  420. return false;
  421. } catch (final Exception e) {
  422. throw new IllegalStateException(e);
  423. }
  424. }
  425. private void pauseAllJobs() {
  426. try {
  427. for (final Scheduler scheduler : JobInformations.getAllSchedulers()) {
  428. scheduler.pauseAll();
  429. }
  430. } catch (final Exception e) {
  431. throw new IllegalStateException(e);
  432. }
  433. }
  434. private String resumeJob(String jobId) {
  435. if (ALL.equalsIgnoreCase(jobId)) {
  436. resumeAllJobs();
  437. return I18N.getString("all_jobs_resumed");
  438. }
  439. final String[] values = jobId.split("_");
  440. if (values.length != 3) {
  441. throw new IllegalArgumentException(jobId);
  442. }
  443. // rq : la syntaxe vérifiée ici doit être conforme à JobInformations.buildGlobalJobId
  444. if (values[0].equals(PID.getPID()) && values[1].equals(Parameters.getHostAddress())) {
  445. if (resumeJobById(Integer.parseInt(values[2]))) {
  446. return I18N.getString("job_resumed");
  447. }
  448. return I18N.getString("job_notfound");
  449. }
  450. // cette action ne concernait pas cette JVM, donc on ne fait rien
  451. return null;
  452. }
  453. private boolean resumeJobById(int myJobId) {
  454. try {
  455. for (final Scheduler scheduler : JobInformations.getAllSchedulers()) {
  456. for (final JobDetail jobDetail : JobInformations.getAllJobsOfScheduler(scheduler)) {
  457. if (QuartzAdapter.getSingleton().getJobFullName(jobDetail)
  458. .hashCode() == myJobId) {
  459. QuartzAdapter.getSingleton().resumeJob(jobDetail, scheduler);
  460. return true;
  461. }
  462. }
  463. }
  464. return false;
  465. } catch (final Exception e) {
  466. throw new IllegalStateException(e);
  467. }
  468. }
  469. private void resumeAllJobs() {
  470. try {
  471. for (final Scheduler scheduler : JobInformations.getAllSchedulers()) {
  472. scheduler.resumeAll();
  473. }
  474. } catch (final Exception e) {
  475. throw new IllegalStateException(e);
  476. }
  477. }
  478. }