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.DateFormat;
23 import java.util.Arrays;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27
28 import net.bull.javamelody.internal.common.I18N;
29 import net.bull.javamelody.internal.model.ConnectionInformations;
30
31 /**
32  * Partie du rapport html pour les connections jdbc ouvertes.
33  * @author Emeric Vernat
34  */

35 class HtmlConnectionInformationsReport extends HtmlAbstractReport {
36     private final List<ConnectionInformations> connectionsInformations;
37     private final DateFormat dateTimeFormat = I18N.createDateAndTimeFormat();
38     private final Map<Long, Thread> threadsById;
39     private final Map<Thread, StackTraceElement[]> stackTracesByThread;
40
41     HtmlConnectionInformationsReport(List<ConnectionInformations> connectionsInformations,
42             Writer writer) {
43         super(writer);
44         assert connectionsInformations != null;
45         this.connectionsInformations = connectionsInformations;
46         // rq: cette partie du rapport n'est pas exécutée sur le serveur de collecte
47         // donc les threads sont ok
48         this.stackTracesByThread = Thread.getAllStackTraces();
49         this.threadsById = new HashMap<>(stackTracesByThread.size());
50         for (final Thread thread : stackTracesByThread.keySet()) {
51             this.threadsById.put(thread.getId(), thread);
52         }
53         // avant, si java < 1.6.0_01 :
54         //        {
55         //            this.stackTracesByThread = Collections.emptyMap();
56         //            final List<Thread> threads = JavaInformations.getThreadsFromThreadGroups();
57         //            this.threadsById = new HashMap<Long, Thread>(threads.size());
58         //            for (final Thread thread : threads) {
59         //                this.threadsById.put(thread.getId(), thread);
60         //            }
61         //        }
62     }
63
64     @Override
65     void toHtml() throws IOException {
66         writeBackAndRefreshLinks();
67         writeln("<br/>");
68
69         writeTitle("db.png", getString("Connexions_jdbc_ouvertes"));
70         writeln("<br/>#connexions_intro#<br/><br/>");
71         writeConnections();
72     }
73
74     void writeConnections() throws IOException {
75         if (connectionsInformations.isEmpty()) {
76             writeln("#Aucune_connexion_jdbc_ouverte#");
77             return;
78         }
79         final HtmlTable table = new HtmlTable();
80         table.beginTable(getString("Connexions_jdbc_ouvertes"));
81         write("<th class='sorttable_date'>#Date_et_stack_trace_ouverture#</th>");
82         write("<th>#Thread_et_stack_trace_actuelle#</th>");
83         for (final ConnectionInformations connection : connectionsInformations) {
84             table.nextRow();
85             writeConnection(connection);
86         }
87         table.endTable();
88         final int nbConnections = connectionsInformations.size();
89         writeln("<div align='right'>" + getFormattedString("nb_connexions_ouvertes", nbConnections)
90                 + "</div>");
91     }
92
93     private void writeBackAndRefreshLinks() throws IOException {
94         writeln("<div class='noPrint'>");
95         writeln("<a class='back' href=''><img src='?resource=action_back.png' alt='#Retour#'/> #Retour#</a>");
96         writeln("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
97         writeln("<a href='?part=connections'>");
98         writeln("<img src='?resource=action_refresh.png' alt='#Actualiser#'/> #Actualiser#</a>");
99         writeln("</div>");
100     }
101
102     private void writeConnection(ConnectionInformations connection) throws IOException {
103         write("<td align='right'>");
104         writeTextWithStackTrace(dateTimeFormat.format(connection.getOpeningDate()),
105                 connection.getOpeningStackTrace());
106         write("</td><td>");
107         final Thread thread = threadsById.get(connection.getThreadId());
108         if (thread == null) {
109             write("&nbsp;");
110         } else {
111             final StackTraceElement[] stackTrace = stackTracesByThread.get(thread);
112             writeTextWithStackTrace(thread.getName(),
113                     stackTrace != null ? Arrays.asList(stackTrace) : null);
114         }
115         write("</td>");
116     }
117
118     private void writeTextWithStackTrace(String text, List<StackTraceElement> stackTrace)
119             throws IOException {
120         final String encodedText = htmlEncode(text);
121         if (stackTrace != null && !stackTrace.isEmpty()) {
122             // même si stackTraceEnabled, ce thread n'a pas forcément de stack-trace
123             writeln("<div class='tooltip'>");
124             writeln("<em>");
125             // writeDirectly pour ne pas gérer de traductions si le texte contient '#'
126             writeDirectly(encodedText);
127             writeln("<br/>");
128             for (final StackTraceElement stackTraceElement : stackTrace) {
129                 writeDirectly(
130                         HtmlSourceReport.htmlEncodeStackTraceElement(stackTraceElement.toString()));
131                 writeDirectly("<br/>");
132             }
133             writeln("</em>");
134             writeDirectly(encodedText);
135             writeln("</div>");
136         } else {
137             // writeDirectly pour ne pas gérer de traductions si le texte contient '#'
138             writeDirectly(encodedText);
139         }
140     }
141 }
142