1 /*
2  * Copyright 2008-2019 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.internal.web.html;
19
20 import java.io.IOException;
21 import java.io.Writer;
22 import java.text.DecimalFormat;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import net.bull.javamelody.internal.common.I18N;
27 import net.bull.javamelody.internal.common.Parameters;
28 import net.bull.javamelody.internal.model.ThreadInformations;
29
30 /**
31  * Partie du rapport html pour les threads sur le serveur.
32  * @author Emeric Vernat
33  */

34 public class HtmlThreadInformationsReport extends HtmlAbstractReport {
35     private final List<ThreadInformations> threadInformationsList;
36     private final DecimalFormat integerFormat = I18N.createIntegerFormat();
37     private final boolean stackTraceEnabled;
38     private final boolean cpuTimeEnabled;
39     private final boolean systemActionsEnabled = Parameters.isSystemActionsEnabled();
40
41     public HtmlThreadInformationsReport(List<ThreadInformations> threadInformationsList,
42             boolean stackTraceEnabled, Writer writer) {
43         super(writer);
44         assert threadInformationsList != null;
45
46         this.threadInformationsList = threadInformationsList;
47         this.stackTraceEnabled = stackTraceEnabled;
48         this.cpuTimeEnabled = !threadInformationsList.isEmpty()
49                 && threadInformationsList.get(0).getCpuTimeMillis() != -1;
50     }
51
52     @Override
53     void toHtml() throws IOException {
54         final HtmlTable table = new HtmlTable();
55         table.beginTable(getString("Threads"));
56         write("<th>#Thread#</th>");
57         write("<th>#Demon#</th><th class='sorttable_numeric'>#Priorite#</th><th>#Etat#</th>");
58         if (stackTraceEnabled) {
59             write("<th>#Methode_executee#</th>");
60         }
61         if (cpuTimeEnabled) {
62             write("<th class='sorttable_numeric'>#Temps_cpu#</th><th class='sorttable_numeric'>#Temps_user#</th>");
63         }
64         if (systemActionsEnabled) {
65             writeln("<th class='noPrint'>#Interrupt#</th>");
66             writeln("<th class='noPrint'>#Tuer#</th>");
67         }
68         for (final ThreadInformations threadInformations : threadInformationsList) {
69             table.nextRow();
70             writeThreadInformations(threadInformations);
71         }
72         table.endTable();
73         writeln("<div align='right'>");
74         writeln("#Temps_threads#");
75         writeln("</div>");
76     }
77
78     void writeDeadlocks() throws IOException {
79         final List<ThreadInformations> deadlockedThreads = getDeadLockedThreads();
80         if (!deadlockedThreads.isEmpty()) {
81             write("<div class='severe'>#Threads_deadlocks#");
82             String separator = " ";
83             for (final ThreadInformations thread : deadlockedThreads) {
84                 writeDirectly(separator);
85                 writeDirectly(htmlEncode(thread.getName()));
86                 separator = ", ";
87             }
88             write("</div>");
89         }
90     }
91
92     public void writeThreadsDump() throws IOException {
93         final List<ThreadInformations> deadlockedThreads = getDeadLockedThreads();
94         if (!deadlockedThreads.isEmpty()) {
95             write("#Threads_deadlocks#");
96             String separator = " ";
97             for (final ThreadInformations thread : deadlockedThreads) {
98                 writeDirectly(separator);
99                 writeDirectly(thread.getName());
100                 separator = ", ";
101             }
102             writeDirectly("\n\n");
103         }
104         if (stackTraceEnabled) {
105             for (final ThreadInformations threadInformations : threadInformationsList) {
106                 writeDirectly("\"");
107                 writeDirectly(threadInformations.getName());
108                 writeDirectly("\"");
109                 if (threadInformations.isDaemon()) {
110                     writeDirectly(" daemon");
111                 }
112                 writeDirectly(" prio=");
113                 writeDirectly(String.valueOf(threadInformations.getPriority()));
114                 writeDirectly(" ");
115                 writeDirectly(String.valueOf(threadInformations.getState()));
116                 final List<StackTraceElement> stackTrace = threadInformations.getStackTrace();
117                 if (stackTrace != null && !stackTrace.isEmpty()) {
118                     for (final StackTraceElement element : stackTrace) {
119                         writeDirectly("\n\t");
120                         writeDirectly(element.toString());
121                     }
122                 }
123                 writeDirectly("\n\n");
124             }
125             writeDirectly("\n");
126         }
127     }
128
129     private List<ThreadInformations> getDeadLockedThreads() {
130         final List<ThreadInformations> deadlockedThreads = new ArrayList<>();
131         for (final ThreadInformations thread : threadInformationsList) {
132             if (thread.isDeadlocked()) {
133                 deadlockedThreads.add(thread);
134             }
135         }
136         return deadlockedThreads;
137     }
138
139     private void writeThreadInformations(ThreadInformations threadInformations) throws IOException {
140         write("<td>");
141         writeThreadWithStackTrace(threadInformations);
142         write("</td> <td align='center'>");
143         if (threadInformations.isDaemon()) {
144             write("#oui#");
145         } else {
146             write("#non#");
147         }
148         write("</td> <td align='right'>");
149         write(integerFormat.format(threadInformations.getPriority()));
150         write("</td> <td>");
151         write("<img src='?resource=bullets/");
152         write(getStateIcon(threadInformations));
153         write("' alt='");
154         write(String.valueOf(threadInformations.getState()));
155         write("'/>");
156         write(String.valueOf(threadInformations.getState()));
157         if (stackTraceEnabled) {
158             write("</td> <td>");
159             writeExecutedMethod(threadInformations);
160         }
161         if (cpuTimeEnabled) {
162             write("</td> <td align='right'>");
163             write(integerFormat.format(threadInformations.getCpuTimeMillis()));
164             write("</td> <td align='right'>");
165             write(integerFormat.format(threadInformations.getUserTimeMillis()));
166         }
167         writeSendThreadInterrupt(threadInformations);
168         writeKillThread(threadInformations);
169         write("</td>");
170     }
171
172     public static String getStateIcon(ThreadInformations threadInformations) {
173         switch (threadInformations.getState()) {
174         case RUNNABLE:
175             return "green.png";
176         case WAITING:
177             return "yellow.png";
178         case TIMED_WAITING:
179             if (isSleeping(threadInformations)) {
180                 return "blue.png";
181             }
182             return "yellow.png";
183         case BLOCKED:
184             return "red.png";
185         case NEW:
186         case TERMINATED:
187             return "gray.png";
188         default:
189             throw new IllegalArgumentException("state inconnu" + threadInformations.getState());
190         }
191     }
192
193     private static boolean isSleeping(ThreadInformations threadInformations) {
194         final List<StackTraceElement> stackTrace = threadInformations.getStackTrace();
195         return stackTrace != null && !stackTrace.isEmpty()
196                 && "sleep".equals(stackTrace.get(0).getMethodName())
197                 && "java.lang.Thread".equals(stackTrace.get(0).getClassName());
198     }
199
200     void writeThreadWithStackTrace(ThreadInformations threadInformations) throws IOException {
201         final List<StackTraceElement> stackTrace = threadInformations.getStackTrace();
202         final String encodedName = htmlEncode(threadInformations.getName());
203         if (stackTrace != null && !stackTrace.isEmpty()) {
204             // même si stackTraceEnabled, ce thread n'a pas forcément de stack-trace
205             writeln("<div class='tooltip'>");
206             writeln("<em>");
207             // writeDirectly pour ne pas gérer de traductions si le nom contient '#'
208             writeDirectly(encodedName);
209             writeln("<br/>");
210             for (final StackTraceElement stackTraceElement : stackTrace) {
211                 writeDirectly(
212                         HtmlSourceReport.htmlEncodeStackTraceElement(stackTraceElement.toString()));
213                 writeDirectly("<br/>");
214             }
215             writeln("</em>");
216             writeDirectly(encodedName);
217             writeln("</div>");
218         } else {
219             // writeDirectly pour ne pas gérer de traductions si le nom contient '#'
220             writeDirectly(encodedName);
221         }
222     }
223
224     void writeExecutedMethod(ThreadInformations threadInformations) throws IOException {
225         final String executedMethod = threadInformations.getExecutedMethod();
226         if (executedMethod != null && !executedMethod.isEmpty()) {
227             writeDirectly(HtmlSourceReport
228                     .htmlEncodeStackTraceElement(threadInformations.getExecutedMethod()));
229         } else {
230             writeDirectly("&nbsp;");
231         }
232     }
233
234     void writeSendThreadInterrupt(ThreadInformations threadInformations) throws IOException {
235         if (systemActionsEnabled) {
236             write("</td> <td align='center' class='noPrint'>");
237             write("<a href='?action=send_thread_interrupt&amp;threadId=");
238             write(threadInformations.getGlobalThreadId());
239             write(getCsrfTokenUrlPart());
240             final String confirmSendThreadInterrupt = htmlEncodeButNotSpaceAndNewLine(
241                     getFormattedString("confirm_send_thread_interrupt",
242                             threadInformations.getName()));
243             // writeDirectly pour ne pas gérer de traductions si le nom contient '#'
244             writeDirectly("' class='confirm' data-confirm='" + confirmSendThreadInterrupt + "'>");
245             final String title = htmlEncodeButNotSpaceAndNewLine(
246                     getFormattedString("send_thread_interrupt", threadInformations.getName()));
247             writeDirectly("<img width='16' height='16' src='?resource=action_interrupt.png' alt='"
248                     + title + "' title='" + title + "' />");
249             write("</a>");
250         }
251     }
252
253     void writeKillThread(ThreadInformations threadInformations) throws IOException {
254         if (systemActionsEnabled) {
255             write("</td> <td align='center' class='noPrint'>");
256             write("<a href='?action=kill_thread&amp;threadId=");
257             write(threadInformations.getGlobalThreadId());
258             write(getCsrfTokenUrlPart());
259             final String confirmKillThread = htmlEncodeButNotSpaceAndNewLine(
260                     getFormattedString("confirm_kill_thread", threadInformations.getName()));
261             // writeDirectly pour ne pas gérer de traductions si le nom contient '#'
262             writeDirectly("' class='confirm' data-confirm='" + confirmKillThread + "'>");
263             final String title = htmlEncode(
264                     getFormattedString("kill_thread", threadInformations.getName()));
265             writeDirectly("<img width='16' height='16' src='?resource=stop.png' alt='" + title
266                     + "' title='" + title + "' />");
267             write("</a>");
268         }
269     }
270 }
271