1
18 package net.bull.javamelody.internal.web.pdf;
19
20 import java.io.IOException;
21 import java.text.DecimalFormat;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Locale;
27 import java.util.Map;
28
29 import com.lowagie.text.Chunk;
30 import com.lowagie.text.Document;
31 import com.lowagie.text.DocumentException;
32 import com.lowagie.text.Element;
33 import com.lowagie.text.Font;
34 import com.lowagie.text.FontFactory;
35 import com.lowagie.text.Image;
36 import com.lowagie.text.Paragraph;
37 import com.lowagie.text.Phrase;
38 import com.lowagie.text.pdf.PdfPCell;
39 import com.lowagie.text.pdf.PdfPTable;
40
41 import net.bull.javamelody.JdbcWrapper;
42 import net.bull.javamelody.internal.common.I18N;
43 import net.bull.javamelody.internal.model.Collector;
44 import net.bull.javamelody.internal.model.CollectorServer;
45 import net.bull.javamelody.internal.model.Counter;
46 import net.bull.javamelody.internal.model.CounterRequest;
47 import net.bull.javamelody.internal.model.CounterRequestRumData;
48 import net.bull.javamelody.internal.model.DatabaseInformations;
49 import net.bull.javamelody.internal.model.JRobin;
50 import net.bull.javamelody.internal.model.Range;
51 import net.bull.javamelody.internal.web.html.HtmlCounterReport;
52
53
57 public class PdfRequestAndGraphDetailReport extends PdfAbstractTableReport {
58 private final Collector collector;
59 private final CollectorServer collectorServer;
60 private final Range range;
61 private final List<Counter> counters;
62 private final String graphName;
63 private final CounterRequest request;
64 private final Map<String, CounterRequest> requestsById;
65 private final PdfDocumentFactory pdfDocumentFactory;
66 private final DecimalFormat systemErrorFormat = I18N.createPercentFormat();
67 private final DecimalFormat nbExecutionsFormat = I18N.createPercentFormat();
68 private final DecimalFormat integerFormat = I18N.createIntegerFormat();
69 private final Font cellFont = PdfFonts.TABLE_CELL.getFont();
70 private final Font boldFont = PdfFonts.BOLD.getFont();
71 private final Font courierFont = FontFactory.getFont(FontFactory.COURIER, 5.5f, Font.NORMAL);
72
73 PdfRequestAndGraphDetailReport(Collector collector, CollectorServer collectorServer,
74 Range range, String graphName, PdfDocumentFactory pdfDocumentFactory, Document document)
75 throws IOException {
76 super(document);
77 assert collector != null;
78 assert range != null;
79 assert graphName != null;
80 assert pdfDocumentFactory != null;
81 this.collector = collector;
82 this.collectorServer = collectorServer;
83 this.range = range;
84 this.graphName = graphName;
85 this.counters = collector.getRangeCounters(range);
86 this.requestsById = mapAllRequestsById();
87 this.request = requestsById.get(graphName);
88 this.pdfDocumentFactory = pdfDocumentFactory;
89 }
90
91 private Map<String, CounterRequest> mapAllRequestsById() {
92 final Map<String, CounterRequest> result = new HashMap<>();
93 for (final Counter counter : counters) {
94 for (final CounterRequest aRequest : counter.getRequests()) {
95 result.put(aRequest.getId(), aRequest);
96 }
97 }
98 return result;
99 }
100
101 @Override
102 void toPdf() throws DocumentException, IOException {
103 if (request != null) {
104 if (request.getRumData() != null && request.getRumData().getHits() != 0) {
105 writeRequestRumData();
106 }
107
108 writeHeader();
109
110 writeRequests();
111
112 addTableToDocument();
113
114 if (JdbcWrapper.SINGLETON.getSqlCounter().isRequestIdFromThisCounter(request.getId())
115 && !request.getName().toLowerCase(Locale.ENGLISH).startsWith("alter ")) {
116
117
118 writeSqlRequestExplainPlan();
119 }
120 }
121
122 if (isGraphDisplayed()) {
123 writeGraph();
124 }
125
126 if (request != null && request.getStackTrace() != null) {
127 final Paragraph paragraph = new Paragraph("\n", cellFont);
128 paragraph.setIndentationLeft(20);
129 paragraph.setIndentationRight(20);
130 paragraph.add(new Phrase("Stack-trace\n", boldFont));
131 paragraph.add(new Phrase(request.getStackTrace().replace("\t", " "), cellFont));
132 addToDocument(paragraph);
133 }
134 }
135
136 private void writeRequestRumData() throws DocumentException {
137 final CounterRequestRumData rumData = request.getRumData();
138 final DecimalFormat percentFormat = I18N.createPercentFormat();
139 final int networkTimeMean = rumData.getNetworkTimeMean();
140 final int serverMean = request.getMean();
141 final int domProcessingMean = rumData.getDomProcessingMean();
142 final int pageRenderingMean = rumData.getPageRenderingMean();
143 final int totalTime = networkTimeMean + serverMean + domProcessingMean + pageRenderingMean;
144 final double networkPercent = 100d * networkTimeMean / totalTime;
145 final double serverPercent = 100d * serverMean / totalTime;
146 final double domProcessingPercent = 100d * domProcessingMean / totalTime;
147 final double pageRenderingPercent = 100d * pageRenderingMean / totalTime;
148
149 final PdfPTable table = new PdfPTable(2);
150 table.setHorizontalAlignment(Element.ALIGN_LEFT);
151 table.setWidthPercentage(25);
152 table.getDefaultCell().setBorderWidth(0);
153 table.addCell(new Phrase(I18N.getString("Network"), cellFont));
154 table.addCell(new Phrase(integerFormat.format(networkTimeMean) + " ms ("
155 + percentFormat.format(networkPercent) + "%)", cellFont));
156 table.addCell(new Phrase(I18N.getString("Server"), cellFont));
157 table.addCell(new Phrase(integerFormat.format(serverMean) + " ms ("
158 + percentFormat.format(serverPercent) + "%)", cellFont));
159 table.addCell(new Phrase(I18N.getString("DOM_processing"), cellFont));
160 table.addCell(new Phrase(integerFormat.format(domProcessingMean) + " ms ("
161 + percentFormat.format(domProcessingPercent) + "%)", cellFont));
162 table.addCell(new Phrase(I18N.getString("Page_rendering"), cellFont));
163 table.addCell(new Phrase(integerFormat.format(pageRenderingMean) + " ms ("
164 + percentFormat.format(pageRenderingPercent) + "%)", cellFont));
165 addToDocument(table);
166 addToDocument(new Phrase("\n", cellFont));
167 }
168
169 private void writeHeader() throws DocumentException {
170 final List<String> headers = createHeaders();
171 final int[] relativeWidths = new int[headers.size()];
172 Arrays.fill(relativeWidths, 0, headers.size(), 1);
173 relativeWidths[0] = 8;
174
175 initTable(headers, relativeWidths);
176 }
177
178 private List<String> createHeaders() {
179 final List<String> headers = new ArrayList<>();
180 headers.add(getString("Requete"));
181 final boolean hasChildren = !request.getChildRequestsExecutionsByRequestId().isEmpty();
182 if (hasChildren) {
183 headers.add(getString("Hits_par_requete"));
184 }
185 headers.add(getString("Temps_moyen"));
186 headers.add(getString("Temps_max"));
187 headers.add(getString("Ecart_type"));
188 headers.add(getString("Temps_cpu_moyen"));
189 if (isAllocatedKBytesDisplayed()) {
190 headers.add(getString("Ko_alloues_moyens"));
191 }
192 headers.add(getString("erreur_systeme"));
193 final Counter parentCounter = getCounterByRequestId(request);
194 final boolean allChildHitsDisplayed = parentCounter != null
195 && parentCounter.getChildCounterName() != null && request.hasChildHits();
196 if (allChildHitsDisplayed) {
197 final String childCounterName = parentCounter.getChildCounterName();
198 headers.add(getFormattedString("hits_fils_moyens", childCounterName));
199 headers.add(getFormattedString("temps_fils_moyen", childCounterName));
200 }
201 return headers;
202 }
203
204 private void writeRequests() throws IOException, DocumentException {
205 final Map<String, Long> childRequests = request.getChildRequestsExecutionsByRequestId();
206 final boolean hasChildren = !childRequests.isEmpty();
207 final Counter parentCounter = getCounterByRequestId(request);
208 final boolean allChildHitsDisplayed = parentCounter != null
209 && parentCounter.getChildCounterName() != null && request.hasChildHits();
210
211 final PdfPCell defaultCell = getDefaultCell();
212 defaultCell.setLeading(2, 1);
213 defaultCell.setPaddingTop(0);
214
215 nextRow();
216 writeRequest(request, -1, allChildHitsDisplayed);
217
218 if (hasChildren) {
219 writeChildRequests(childRequests, allChildHitsDisplayed);
220 }
221 }
222
223 private void writeChildRequests(Map<String, Long> childRequests, boolean allChildHitsDisplayed)
224 throws IOException, DocumentException {
225 for (final Map.Entry<String, Long> entry : childRequests.entrySet()) {
226 final CounterRequest childRequest = requestsById.get(entry.getKey());
227 if (childRequest != null) {
228 nextRow();
229 final Long nbExecutions = entry.getValue();
230 final float executionsByRequest = (float) nbExecutions / request.getHits();
231 writeRequest(childRequest, executionsByRequest, allChildHitsDisplayed);
232 }
233 }
234 }
235
236 private void writeRequest(CounterRequest childRequest, float executionsByRequest,
237 boolean allChildHitsDisplayed) throws IOException, DocumentException {
238 final PdfPCell defaultCell = getDefaultCell();
239 defaultCell.setHorizontalAlignment(Element.ALIGN_LEFT);
240 final Paragraph paragraph = new Paragraph(defaultCell.getLeading() + cellFont.getSize());
241 if (executionsByRequest != -1) {
242 paragraph.setIndentationLeft(5);
243 }
244 final Counter parentCounter = getCounterByRequestId(childRequest);
245 if (parentCounter != null && parentCounter.getIconName() != null) {
246 paragraph.add(new Chunk(getSmallImage(parentCounter.getIconName()), 0, -1));
247 }
248 paragraph.add(new Phrase(childRequest.getName(), cellFont));
249 final PdfPCell requestCell = new PdfPCell();
250 requestCell.addElement(paragraph);
251 requestCell.setGrayFill(defaultCell.getGrayFill());
252 requestCell.setPaddingTop(defaultCell.getPaddingTop());
253 addCell(requestCell);
254
255 defaultCell.setHorizontalAlignment(Element.ALIGN_RIGHT);
256 if (executionsByRequest != -1) {
257 addCell(nbExecutionsFormat.format(executionsByRequest));
258 } else {
259 final boolean hasChildren = !request.getChildRequestsExecutionsByRequestId().isEmpty();
260 if (hasChildren) {
261 addCell("");
262 }
263 }
264 writeRequestValues(childRequest, allChildHitsDisplayed);
265 }
266
267 private void writeRequestValues(CounterRequest aRequest, boolean allChildHitsDisplayed) {
268 final PdfPCell defaultCell = getDefaultCell();
269 defaultCell.setHorizontalAlignment(Element.ALIGN_RIGHT);
270 addCell(integerFormat.format(aRequest.getMean()));
271 addCell(integerFormat.format(aRequest.getMaximum()));
272 addCell(integerFormat.format(aRequest.getStandardDeviation()));
273 if (aRequest.getCpuTimeMean() >= 0) {
274 addCell(integerFormat.format(aRequest.getCpuTimeMean()));
275 } else {
276 addCell("");
277 }
278 if (isAllocatedKBytesDisplayed()) {
279 if (aRequest.getAllocatedKBytesMean() >= 0) {
280 addCell(integerFormat.format(aRequest.getAllocatedKBytesMean()));
281 } else {
282 addCell("");
283 }
284 }
285 addCell(systemErrorFormat.format(aRequest.getSystemErrorPercentage()));
286 if (allChildHitsDisplayed) {
287 final boolean childHitsDisplayed = aRequest.hasChildHits();
288 if (childHitsDisplayed) {
289 addCell(integerFormat.format(aRequest.getChildHitsMean()));
290 } else {
291 addCell("");
292 }
293 if (childHitsDisplayed) {
294 addCell(integerFormat.format(aRequest.getChildDurationsMean()));
295 } else {
296 addCell("");
297 }
298 }
299 }
300
301 private boolean isAllocatedKBytesDisplayed() {
302 return request.getAllocatedKBytesMean() >= 0;
303 }
304
305 private void writeGraph() throws IOException, DocumentException {
306 final JRobin jrobin = collector.getJRobin(graphName);
307 if (jrobin != null) {
308 final byte[] img = jrobin.graph(range, 960, 400);
309 final Image image = Image.getInstance(img);
310 image.scalePercent(50);
311
312 final PdfPTable table = new PdfPTable(1);
313 table.setHorizontalAlignment(Element.ALIGN_CENTER);
314 table.setWidthPercentage(100);
315 table.getDefaultCell().setBorder(0);
316 table.addCell("\n");
317 table.addCell(image);
318 table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT);
319 table.addCell(new Phrase(getString("graph_units"), cellFont));
320 addToDocument(table);
321 } else {
322
323 addToDocument(new Phrase("\n", cellFont));
324 }
325 }
326
327 private void writeSqlRequestExplainPlan() throws DocumentException {
328 try {
329 final String explainPlan;
330 if (collectorServer == null) {
331 explainPlan = DatabaseInformations.explainPlanFor(request.getName());
332 } else {
333 explainPlan = collectorServer.collectSqlRequestExplainPlan(
334 collector.getApplication(), request.getName());
335 }
336 if (explainPlan != null) {
337 final Paragraph paragraph = new Paragraph("", cellFont);
338 paragraph.add(new Phrase('\n' + getString("Plan_d_execution") + '\n', boldFont));
339 paragraph.add(new Phrase(explainPlan, courierFont));
340 addToDocument(paragraph);
341 }
342 } catch (final Exception e) {
343 final Paragraph paragraph = new Paragraph("", cellFont);
344 paragraph.add(new Phrase('\n' + getString("Plan_d_execution") + '\n', boldFont));
345 paragraph.add(new Phrase(e.toString(), cellFont));
346 addToDocument(paragraph);
347 }
348 }
349
350 private Counter getCounterByRequestId(CounterRequest aRequest) {
351 final String myRequestId = aRequest.getId();
352 for (final Counter counter : counters) {
353 if (counter.isRequestIdFromThisCounter(myRequestId)) {
354 return counter;
355 }
356 }
357 return null;
358 }
359
360 private boolean isGraphDisplayed() throws IOException {
361 return request == null || getCounterByRequestId(request) != null
362 && HtmlCounterReport.isRequestGraphDisplayed(getCounterByRequestId(request))
363
364
365 && collector.getJRobin(request.getId()) != null;
366 }
367
368 private Image getSmallImage(String resourceFileName) throws DocumentException, IOException {
369 return pdfDocumentFactory.getSmallImage(resourceFileName);
370 }
371 }
372