/* * Copyright 2008-2014 by Emeric Vernat * * This file is part of Java Melody. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.bull.javamelody; import java.io.IOException; import java.io.Writer; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; /** * Partie du rapport html pour les threads sur le serveur. * @author Emeric Vernat */ class HtmlThreadInformationsReport extends HtmlAbstractReport { private final List<ThreadInformations> threadInformationsList; private final DecimalFormat integerFormat = I18N.createIntegerFormat(); private final boolean stackTraceEnabled; private final boolean cpuTimeEnabled; private final boolean systemActionsEnabled = Parameters.isSystemActionsEnabled(); HtmlThreadInformationsReport(List<ThreadInformations> threadInformationsList, boolean stackTraceEnabled, Writer writer) { super(writer); assert threadInformationsList != null; this.threadInformationsList = threadInformationsList; this.stackTraceEnabled = stackTraceEnabled; this.cpuTimeEnabled = !threadInformationsList.isEmpty() && threadInformationsList.get(0).getCpuTimeMillis() != -1; } @Override void toHtml() throws IOException { final HtmlTable table = new HtmlTable(); table.beginTable(getString("Threads")); write("<th>#Thread#</th>"); write("<th>#Demon#</th><th class='sorttable_numeric'>#Priorite#</th><th>#Etat#</th>"); if (stackTraceEnabled) { write("<th>#Methode_executee#</th>"); } if (cpuTimeEnabled) { write("<th class='sorttable_numeric'>#Temps_cpu#</th><th class='sorttable_numeric'>#Temps_user#</th>"); } if (systemActionsEnabled) { writeln("<th class='noPrint'>#Tuer#</th>"); } for (final ThreadInformations threadInformations : threadInformationsList) { table.nextRow(); writeThreadInformations(threadInformations); } table.endTable(); writeln("<div align='right'>"); writeln("#Temps_threads#"); writeln("</div>"); } void writeDeadlocks() throws IOException { final List<ThreadInformations> deadlockedThreads = getDeadLockedThreads(); if (!deadlockedThreads.isEmpty()) { write("<div class='severe'>#Threads_deadlocks#"); String separator = " "; for (final ThreadInformations thread : deadlockedThreads) { writeDirectly(separator); writeDirectly(htmlEncode(thread.getName())); separator = ", "; } write("</div>"); } } void writeThreadsDump() throws IOException { final List<ThreadInformations> deadlockedThreads = getDeadLockedThreads(); if (!deadlockedThreads.isEmpty()) { write("#Threads_deadlocks#"); String separator = " "; for (final ThreadInformations thread : deadlockedThreads) { writeDirectly(separator); writeDirectly(thread.getName()); separator = ", "; } writeDirectly("\n\n"); } if (stackTraceEnabled) { for (final ThreadInformations threadInformations : threadInformationsList) { writeDirectly("\""); writeDirectly(threadInformations.getName()); writeDirectly("\""); if (threadInformations.isDaemon()) { writeDirectly(" daemon"); } writeDirectly(" prio="); writeDirectly(String.valueOf(threadInformations.getPriority())); writeDirectly(" "); writeDirectly(String.valueOf(threadInformations.getState())); final List<StackTraceElement> stackTrace = threadInformations.getStackTrace(); if (stackTrace != null && !stackTrace.isEmpty()) { for (final StackTraceElement element : stackTrace) { writeDirectly("\n\t"); writeDirectly(element.toString()); } } writeDirectly("\n\n"); } writeDirectly("\n"); } } private List<ThreadInformations> getDeadLockedThreads() { final List<ThreadInformations> deadlockedThreads = new ArrayList<ThreadInformations>(); for (final ThreadInformations thread : threadInformationsList) { if (thread.isDeadlocked()) { deadlockedThreads.add(thread); } } return deadlockedThreads; } private void writeThreadInformations(ThreadInformations threadInformations) throws IOException { write("<td>"); writeThreadWithStackTrace(threadInformations); write("</td> <td align='center'>"); if (threadInformations.isDaemon()) { write("#oui#"); } else { write("#non#"); } write("</td> <td align='right'>"); write(integerFormat.format(threadInformations.getPriority())); write("</td> <td>"); write("<img src='?resource=bullets/"); write(getStateIcon(threadInformations)); write("' alt='"); write(String.valueOf(threadInformations.getState())); write("'/>"); write(String.valueOf(threadInformations.getState())); if (stackTraceEnabled) { write("</td> <td>"); writeExecutedMethod(threadInformations); } if (cpuTimeEnabled) { write("</td> <td align='right'>"); write(integerFormat.format(threadInformations.getCpuTimeMillis())); write("</td> <td align='right'>"); write(integerFormat.format(threadInformations.getUserTimeMillis())); } writeKillThread(threadInformations); write("</td>"); } static String getStateIcon(ThreadInformations threadInformations) { switch (threadInformations.getState()) { // NOPMD case RUNNABLE: return "green.png"; case WAITING: return "yellow.png"; case TIMED_WAITING: if (isSleeping(threadInformations)) { return "blue.png"; } return "yellow.png"; case BLOCKED: return "red.png"; case NEW: case TERMINATED: return "gray.png"; default: throw new IllegalArgumentException("state inconnu" + threadInformations.getState()); } } private static boolean isSleeping(ThreadInformations threadInformations) { final List<StackTraceElement> stackTrace = threadInformations.getStackTrace(); return stackTrace != null && !stackTrace.isEmpty() && "sleep".equals(stackTrace.get(0).getMethodName()) && "java.lang.Thread".equals(stackTrace.get(0).getClassName()); } void writeThreadWithStackTrace(ThreadInformations threadInformations) throws IOException { final List<StackTraceElement> stackTrace = threadInformations.getStackTrace(); final String encodedName = htmlEncode(threadInformations.getName()); if (stackTrace != null && !stackTrace.isEmpty()) { // même si stackTraceEnabled, ce thread n'a pas forcément de stack-trace writeln("<a class='tooltip'>"); writeln("<em>"); // writeDirectly pour ne pas gérer de traductions si le nom contient '#' writeDirectly(encodedName); writeln("<br/>"); for (final StackTraceElement stackTraceElement : stackTrace) { write(htmlEncode(stackTraceElement.toString())); writeln("<br/>"); } writeln("</em>"); writeDirectly(encodedName); writeln("</a>"); } else { // writeDirectly pour ne pas gérer de traductions si le nom contient '#' writeDirectly(encodedName); } } void writeExecutedMethod(ThreadInformations threadInformations) throws IOException { final String executedMethod = threadInformations.getExecutedMethod(); if (executedMethod != null && executedMethod.length() != 0) { write(htmlEncode(executedMethod)); } else { write(" "); } } void writeKillThread(ThreadInformations threadInformations) throws IOException { if (systemActionsEnabled) { write("</td> <td align='center' class='noPrint'>"); write("<a href='?action=kill_thread&threadId="); write(threadInformations.getGlobalThreadId()); final String confirmKillThread = javascriptEncode(getFormattedString( "confirm_kill_thread", threadInformations.getName())); // writeDirectly pour ne pas gérer de traductions si le nom contient '#' writeDirectly("' onclick=\"javascript:return confirm('" + confirmKillThread + "');\">"); final String title = htmlEncode(getFormattedString("kill_thread", threadInformations.getName())); writeDirectly("<img width='16' height='16' src='?resource=stop.png' alt='" + title + "' title='" + title + "' />"); write("</a>"); } } }