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.InputStream;
22 import java.io.Writer;
23 import java.net.URL;
24 import java.nio.charset.StandardCharsets;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Map;
30
31 import net.bull.javamelody.Parameter;
32 import net.bull.javamelody.SpringContext;
33 import net.bull.javamelody.internal.common.I18N;
34 import net.bull.javamelody.internal.common.InputOutput;
35 import net.bull.javamelody.internal.common.Parameters;
36 import net.bull.javamelody.internal.model.CacheInformations;
37 import net.bull.javamelody.internal.model.Collector;
38 import net.bull.javamelody.internal.model.CollectorServer;
39 import net.bull.javamelody.internal.model.ConnectionInformations;
40 import net.bull.javamelody.internal.model.Counter;
41 import net.bull.javamelody.internal.model.CounterRequestContext;
42 import net.bull.javamelody.internal.model.DatabaseInformations;
43 import net.bull.javamelody.internal.model.HeapHistogram;
44 import net.bull.javamelody.internal.model.HsErrPid;
45 import net.bull.javamelody.internal.model.JCacheInformations;
46 import net.bull.javamelody.internal.model.JavaInformations;
47 import net.bull.javamelody.internal.model.JndiBinding;
48 import net.bull.javamelody.internal.model.MBeanNode;
49 import net.bull.javamelody.internal.model.MavenArtifact;
50 import net.bull.javamelody.internal.model.Period;
51 import net.bull.javamelody.internal.model.ProcessInformations;
52 import net.bull.javamelody.internal.model.Range;
53 import net.bull.javamelody.internal.model.SamplingProfiler.SampledMethod;
54 import net.bull.javamelody.internal.model.SessionInformations;
55
56 /**
57  * Rapport html.
58  * @author Emeric Vernat
59  */

60 public class HtmlReport extends HtmlAbstractReport {
61     private static final URL THEMED_MONITORING_CSS = HtmlReport.class
62             .getResource("/net/bull/javamelody/resource/themedMonitoring.css");
63     private static final URL THEMED_MONITORING_JS = HtmlReport.class
64             .getResource("/net/bull/javamelody/resource/themedMonitoring.js");
65
66     private final Collector collector;
67     private final CollectorServer collectorServer;
68     private final List<JavaInformations> javaInformationsList;
69     private final Range range;
70     private final HtmlCoreReport htmlCoreReport;
71
72     public HtmlReport(Collector collector, CollectorServer collectorServer,
73             List<JavaInformations> javaInformationsList, Range range, Writer writer) {
74         super(writer);
75         assert collector != null;
76         assert javaInformationsList != null && !javaInformationsList.isEmpty();
77         assert range != null;
78
79         this.collector = collector;
80         this.collectorServer = collectorServer;
81         this.javaInformationsList = javaInformationsList;
82         this.range = range;
83         this.htmlCoreReport = new HtmlCoreReport(collector, collectorServer, javaInformationsList,
84                 range, writer);
85     }
86
87     public HtmlReport(Collector collector, CollectorServer collectorServer,
88             List<JavaInformations> javaInformationsList, Period period, Writer writer) {
89         this(collector, collectorServer, javaInformationsList, period.getRange(), writer);
90     }
91
92     @Override
93     void toHtml() throws IOException {
94         writeHtmlHeader();
95         htmlCoreReport.toHtml();
96         writeHtmlFooter();
97     }
98
99     public void toHtml(String message, String anchorNameForRedirect) throws IOException {
100         writeHtmlHeader();
101         htmlCoreReport.toHtml(message, anchorNameForRedirect);
102         writeHtmlFooter();
103     }
104
105     public void writeLastShutdown() throws IOException {
106         writeHtmlHeader(falsetrue);
107         htmlCoreReport.toHtml();
108         writeHtmlFooter();
109     }
110
111     public static void writeAddAndRemoveApplicationLinks(String currentApplication,
112             Collection<String> applications, Writer writer) throws IOException {
113         HtmlCoreReport.writeAddAndRemoveApplicationLinks(currentApplication, applications, writer);
114     }
115
116     public void writeAllCurrentRequestsAsPart() throws IOException {
117         writeHtmlHeader();
118         final List<Counter> counters = collector.getRangeCountersToBeDisplayed(range);
119         final Map<JavaInformations, List<CounterRequestContext>> currentRequests;
120         if (collectorServer == null) {
121             assert javaInformationsList.size() == 1;
122             final JavaInformations javaInformations = javaInformationsList.get(0);
123             final List<CounterRequestContext> rootCurrentContexts = collector
124                     .getRootCurrentContexts(counters);
125             currentRequests = Collections.singletonMap(javaInformations, rootCurrentContexts);
126         } else {
127             currentRequests = collectorServer.collectCurrentRequests(collector.getApplication());
128             final List<CounterRequestContext> allCurrentRequests = new ArrayList<>();
129             for (final List<CounterRequestContext> rootCurrentContexts : currentRequests.values()) {
130                 allCurrentRequests.addAll(rootCurrentContexts);
131             }
132             CounterRequestContext.replaceParentCounters(allCurrentRequests, counters);
133         }
134         htmlCoreReport.writeAllCurrentRequestsAsPart(currentRequests);
135         writeHtmlFooter();
136     }
137
138     public void writeAllThreadsAsPart() throws IOException {
139         writeHtmlHeader();
140         htmlCoreReport.writeAllThreadsAsPart();
141         writeHtmlFooter();
142     }
143
144     public void writeThreadsDump() throws IOException {
145         htmlCoreReport.writeThreadsDump();
146     }
147
148     public void writeCounterSummaryPerClass(String counterName, String requestId)
149             throws IOException {
150         writeHtmlHeader();
151         htmlCoreReport.writeCounterSummaryPerClass(counterName, requestId);
152         writeHtmlFooter();
153     }
154
155     public void writeSource(String className) throws IOException {
156         assert className != null;
157         writeHtmlHeader();
158         new HtmlSourceReport(className, getWriter()).toHtml();
159         writeHtmlFooter();
160     }
161
162     public void writeDependencies() throws IOException {
163         writeHtmlHeader();
164         final Map<String, MavenArtifact> dependencies = MavenArtifact.getWebappDependencies();
165         new HtmlDependenciesReport(dependencies, getWriter()).toHtml();
166         writeHtmlFooter();
167     }
168
169     public void writeSpringContext() throws IOException {
170         writeHtmlHeader();
171         final SpringContext springContext = SpringContext.getSingleton();
172         new HtmlSpringContextReport(springContext, getWriter()).toHtml();
173         writeHtmlFooter();
174     }
175
176     public void writeHtmlHeader() throws IOException {
177         writeHtmlHeader(falsefalse);
178     }
179
180     private void writeHtmlHeader(boolean includeSlider, boolean includeCssAndJsInline)
181             throws IOException {
182         writeln("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
183         writeDirectly("<html lang='" + I18N.getCurrentLocale().getLanguage() + "'><head><title>"
184                 + getFormattedString("Monitoring_sur", collector.getApplication()) + "</title>");
185         writeln("");
186         if (includeCssAndJsInline) {
187             writeln("<style type='text/css'>");
188             try (InputStream in = getClass()
189                     .getResourceAsStream(Parameters.getResourcePath("monitoring.css"))) {
190                 final String monitoringCss = InputOutput.pumpToString(in, StandardCharsets.UTF_8);
191                 writeDirectly(monitoringCss);
192             }
193             writeln("</style>");
194             writeln("<script type='text/javascript'>");
195             try (InputStream in = getClass()
196                     .getResourceAsStream(Parameters.getResourcePath("prototype.js"))) {
197                 final String monitoringJs = InputOutput.pumpToString(in, StandardCharsets.UTF_8);
198                 writeDirectly(monitoringJs);
199             }
200             try (InputStream in = getClass()
201                     .getResourceAsStream(Parameters.getResourcePath("monitoring.js"))) {
202                 final String monitoringJs = InputOutput.pumpToString(in, StandardCharsets.UTF_8);
203                 writeDirectly(monitoringJs);
204             }
205             writeln("</script>");
206         } else {
207             writeln("<link rel='stylesheet' href='?resource=monitoring.css' type='text/css'/>");
208             // prototype.js nécessaire pour monitoring.js, effects.js et slider.js
209             writeln("<script type='text/javascript' src='?resource=prototype.js'></script>");
210             writeln("<script type='text/javascript' src='?resource=monitoring.js'></script>");
211             if (THEMED_MONITORING_CSS != null) {
212                 writeln("<link rel='stylesheet' href='?resource=themedMonitoring.css' type='text/css'/>");
213             }
214             writeln("<link rel='stylesheet' href='?resource=customizableMonitoring.css' type='text/css'/>");
215         }
216         writeln("<link type='image/png' rel='shortcut icon' href='?resource=systemmonitor.png' />");
217         writeln("<script type='text/javascript' src='?resource=resizable_tables.js'></script>");
218         writeln("<script type='text/javascript' src='?resource=sorttable.js'></script>");
219         // Effect slidedown/slideup décrit ici http://madrobby.github.com/scriptaculous/effect-slidedown/
220         writeln("<script type='text/javascript' src='?resource=effects.js'></script>");
221         // open dialog (for java sources), http://www.p51labs.com/lightwindow/
222         writeln("<script type='text/javascript' src='?resource=lightwindow.js'></script>");
223         if (includeSlider) {
224             writeln("<script type='text/javascript' src='?resource=slider.js'></script>");
225         }
226         if (THEMED_MONITORING_JS != null) {
227             writeln("<script type='text/javascript' src='?resource=themedMonitoring.js'></script>");
228         }
229         writeln("<script type='text/javascript' src='?resource=customizableMonitoring.js'></script>");
230         writeln("</head><body>");
231     }
232
233     public void writeHtmlFooter() throws IOException {
234         final String analyticsId = Parameter.ANALYTICS_ID.getValue();
235         if (analyticsId != null && !"disabled".equals(analyticsId)) {
236             writeDirectly(
237                     "<script type=\"text/javascript\" src=\"https://ssl.google-analytics.com/ga.js\" async=\"true\" id=\"ga-js\" data-analytics-id=\""
238                             + htmlEncodeButNotSpace(analyticsId) + "\"></script>");
239         }
240         writeln("</body></html>");
241     }
242
243     public void writeMessageIfNotNull(String message, String partToRedirectTo) throws IOException {
244         htmlCoreReport.writeMessageIfNotNull(message, partToRedirectTo, null);
245     }
246
247     public void writeRequestAndGraphDetail(String graphName) throws IOException {
248         writeHtmlHeader(truefalse);
249
250         writeln("<div align='center'>");
251         htmlCoreReport.writeRefreshAndPeriodLinks(graphName, "graph");
252         writeln("</div>");
253
254         new HtmlCounterRequestGraphReport(range, getWriter()).writeRequestAndGraphDetail(collector,
255                 collectorServer, graphName);
256
257         writeHtmlFooter();
258     }
259
260     public void writeRequestUsages(String graphName) throws IOException {
261         writeHtmlHeader(truefalse);
262
263         writeln("<div align='center'>");
264         htmlCoreReport.writeRefreshAndPeriodLinks(graphName, "usages");
265         writeln("</div>");
266
267         new HtmlCounterRequestGraphReport(range, getWriter()).writeRequestUsages(collector,
268                 graphName);
269
270         writeHtmlFooter();
271     }
272
273     public void writeSessions(List<SessionInformations> sessionsInformations, String message,
274             String sessionsPart) throws IOException {
275         assert sessionsInformations != null;
276         writeHtmlHeader();
277         writeMessageIfNotNull(message, sessionsPart);
278         new HtmlSessionInformationsReport(sessionsInformations, getWriter()).toHtml();
279         writeHtmlFooter();
280     }
281
282     public void writeSessionDetail(String sessionId, SessionInformations sessionInformations)
283             throws IOException {
284         assert sessionId != null;
285         writeHtmlHeader();
286         new HtmlSessionInformationsReport(null, getWriter()).writeSessionDetails(sessionId,
287                 sessionInformations);
288         writeHtmlFooter();
289     }
290
291     public void writeHotspots(List<SampledMethod> hotspots) throws IOException {
292         writeHtmlHeader();
293         new HtmlHotspotsReport(hotspots, getWriter()).toHtml();
294         writeHtmlFooter();
295     }
296
297     public void writeHeapHistogram(HeapHistogram heapHistogram, String message,
298             String heapHistoPart) throws IOException {
299         assert heapHistogram != null;
300         writeHtmlHeader();
301         writeMessageIfNotNull(message, heapHistoPart);
302         new HtmlHeapHistogramReport(heapHistogram, getWriter()).toHtml();
303         writeHtmlFooter();
304     }
305
306     public void writeProcesses(List<ProcessInformations> processInformationsList)
307             throws IOException {
308         assert processInformationsList != null;
309         writeHtmlHeader();
310         new HtmlProcessInformationsReport(processInformationsList, getWriter()).toHtml();
311         writeHtmlFooter();
312     }
313
314     public void writeProcesses(Map<String, List<ProcessInformations>> processesByTitle)
315             throws IOException {
316         assert processesByTitle != null;
317         writeHtmlHeader();
318         new HtmlProcessInformationsReport(new ArrayList<ProcessInformations>(), getWriter())
319                 .writeLinks();
320         for (final Map.Entry<String, List<ProcessInformations>> entry : processesByTitle
321                 .entrySet()) {
322             final String title = entry.getKey();
323             final List<ProcessInformations> processes = entry.getValue();
324             writeDirectly("<h3 class='chapterTitle'><img src='?resource=processes.png' alt='"
325                     + title + "'/>&nbsp;" + title + "</h3>");
326
327             new HtmlProcessInformationsReport(processes, getWriter()).writeTable();
328         }
329         writeHtmlFooter();
330     }
331
332     public void writeDatabase(DatabaseInformations databaseInformations) throws IOException {
333         assert databaseInformations != null;
334         writeHtmlHeader();
335         new HtmlDatabaseInformationsReport(databaseInformations, getWriter()).toHtml();
336         writeHtmlFooter();
337     }
338
339     public void writeConnections(List<ConnectionInformations> connectionInformationsList,
340             boolean withoutHeaders) throws IOException {
341         assert connectionInformationsList != null;
342         final HtmlConnectionInformationsReport htmlConnectionInformationsReport = new HtmlConnectionInformationsReport(
343                 connectionInformationsList, getWriter());
344         if (withoutHeaders) {
345             // pour affichage dans serveur de collecte
346             htmlConnectionInformationsReport.writeConnections();
347         } else {
348             writeHtmlHeader();
349             htmlConnectionInformationsReport.toHtml();
350             writeHtmlFooter();
351         }
352     }
353
354     public void writeJndi(List<JndiBinding> jndiBindings, String path) throws IOException {
355         assert jndiBindings != null;
356         writeHtmlHeader();
357         new HtmlJndiTreeReport(jndiBindings, path, getWriter()).toHtml();
358         writeHtmlFooter();
359     }
360
361     public void writeMBeans(List<MBeanNode> mbeans) throws IOException {
362         assert mbeans != null;
363         writeHtmlHeader();
364         new HtmlMBeansReport(mbeans, getWriter()).toHtml();
365         writeHtmlFooter();
366     }
367
368     public void writeMBeans(Map<String, List<MBeanNode>> mbeansByTitle) throws IOException {
369         assert mbeansByTitle != null;
370         writeHtmlHeader();
371         new HtmlMBeansReport(new ArrayList<MBeanNode>(), getWriter()).writeLinks();
372         for (final Map.Entry<String, List<MBeanNode>> entry : mbeansByTitle.entrySet()) {
373             final String title = entry.getKey();
374             final List<MBeanNode> nodes = entry.getValue();
375             writeDirectly("<h3 class='chapterTitle'><img src='?resource=mbeans.png' alt='" + title
376                     + "'/>&nbsp;" + title + "</h3>");
377
378             new HtmlMBeansReport(nodes, getWriter()).writeTree();
379         }
380         writeHtmlFooter();
381     }
382
383     public void writeCacheWithKeys(String cacheId, CacheInformations cacheInformations,
384             String message, String cacheKeyPart, boolean withoutHeaders) throws IOException {
385         assert cacheId != null;
386         assert cacheInformations != null;
387         final HtmlCacheInformationsReport htmlCacheInformationsReport = new HtmlCacheInformationsReport(
388                 Collections.singletonList(cacheInformations), getWriter());
389         if (withoutHeaders) {
390             htmlCacheInformationsReport.writeCacheWithKeys(cacheId, withoutHeaders);
391         } else {
392             writeHtmlHeader();
393             htmlCacheInformationsReport.writeCacheWithKeys(cacheId, withoutHeaders);
394             writeHtmlFooter();
395             writeMessageIfNotNull(message, cacheKeyPart);
396         }
397     }
398
399     public void writeJCacheWithKeys(String cacheId, JCacheInformations jcacheInformations,
400             String message, String cacheKeyPart, boolean withoutHeaders) throws IOException {
401         assert cacheId != null;
402         assert jcacheInformations != null;
403         final HtmlJCacheInformationsReport htmlJCacheInformationsReport = new HtmlJCacheInformationsReport(
404                 Collections.singletonList(jcacheInformations), getWriter());
405         if (withoutHeaders) {
406             htmlJCacheInformationsReport.writeJCacheWithKeys(cacheId, withoutHeaders);
407         } else {
408             writeHtmlHeader();
409             htmlJCacheInformationsReport.writeJCacheWithKeys(cacheId, withoutHeaders);
410             writeHtmlFooter();
411             writeMessageIfNotNull(message, cacheKeyPart);
412         }
413     }
414
415     public void writeCrashes() throws IOException {
416         writeHtmlHeader();
417         final List<HsErrPid> hsErrPidList = htmlCoreReport.getHsErrPidList();
418         final HtmlHsErrPidReport htmlHsErrPidReport = new HtmlHsErrPidReport(hsErrPidList,
419                 getWriter());
420         htmlHsErrPidReport.toHtml();
421         writeHtmlFooter();
422     }
423
424     public void writeHashPassword(String algorithm, String password) throws IOException {
425         writeHtmlHeader();
426         final HtmlHashPasswordReport htmlHashPasswordReport = new HtmlHashPasswordReport(
427                 getWriter());
428         htmlHashPasswordReport.writeHashPassword(algorithm, password);
429         writeHtmlFooter();
430     }
431 }
432