/* * 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.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import com.lowagie.text.Chunk; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.Element; import com.lowagie.text.Font; import com.lowagie.text.FontFactory; import com.lowagie.text.Image; import com.lowagie.text.Paragraph; import com.lowagie.text.Phrase; import com.lowagie.text.pdf.PdfPTable; /** * Rapport pdf principal. * @author Emeric Vernat */ class PdfCoreReport extends PdfAbstractReport { static final int SMALL_GRAPH_WIDTH = 200; static final int SMALL_GRAPH_HEIGHT = 50; static final int LARGE_GRAPH_WIDTH = 960; static final int LARGE_GRAPH_HEIGHT = 370; private final Collector collector; private final List<JavaInformations> javaInformationsList; private final Range range; private Range counterRange; private List<CounterRequestContext> currentRequests; private final boolean collectorServer; private final PdfDocumentFactory pdfDocumentFactory; private final Font normalFont = PdfFonts.NORMAL.getFont(); private final Font cellFont = PdfFonts.TABLE_CELL.getFont(); private final Font boldFont = PdfFonts.BOLD.getFont(); private final long start = System.currentTimeMillis(); private Map<String, byte[]> smallGraphs; private Map<String, byte[]> smallOtherGraphs; private Map<String, byte[]> largeGraphs; PdfCoreReport(Collector collector, boolean collectorServer, List<JavaInformations> javaInformationsList, Range range, PdfDocumentFactory pdfDocumentFactory, Document document) { super(document); assert collector != null; assert javaInformationsList != null && !javaInformationsList.isEmpty(); assert range != null; assert pdfDocumentFactory != null; this.collector = collector; this.collectorServer = collectorServer; this.javaInformationsList = javaInformationsList; this.range = range; this.counterRange = range; // par défaut, c'est le même range this.pdfDocumentFactory = pdfDocumentFactory; } // cette méthode est utilisée dans l'ihm Swing void preInitGraphs(Map<String, byte[]> newSmallGraphs, Map<String, byte[]> newSmallOtherGraphs, Map<String, byte[]> newLargeGraphs) { this.smallGraphs = newSmallGraphs; this.smallOtherGraphs = newSmallOtherGraphs; this.largeGraphs = newLargeGraphs; } // cette méthode est utilisée dans l'ihm Swing void setCounterRange(Range counterRange) { this.counterRange = counterRange; } // cette méthode est utilisée dans l'ihm Swing void setCurrentRequests(List<CounterRequestContext> currentRequests) { this.currentRequests = currentRequests; } @Override void toPdf() throws IOException, DocumentException { addParagraph(buildSummary(), "systemmonitor.png"); writeGraphs(collector.getDisplayedCounterJRobins(), smallGraphs); final List<Counter> counters = collector.getRangeCountersToBeDisplayed(counterRange); final List<PdfCounterReport> pdfCounterReports = writeCounters(counters); final List<PdfCounterRequestContextReport> pdfCounterRequestContextReports = new ArrayList<PdfCounterRequestContextReport>(); if (!collectorServer) { addParagraph(getString("Requetes_en_cours"), "hourglass.png"); // si on n'est pas sur le serveur de collecte il n'y a qu'un javaInformations pdfCounterRequestContextReports.addAll(writeCurrentRequests( javaInformationsList.get(0), counters, pdfCounterReports)); } addToDocument(new Phrase("\n", normalFont)); addParagraph(getString("Informations_systemes"), "systeminfo.png"); new PdfJavaInformationsReport(javaInformationsList, getDocument()).toPdf(); addParagraph(getString("Threads"), "threads.png"); writeThreads(false); PdfCounterReport pdfJobCounterReport = null; Counter rangeJobCounter = null; if (isJobEnabled()) { rangeJobCounter = collector.getRangeCounter(counterRange, Counter.JOB_COUNTER_NAME); addToDocument(new Phrase("\n", normalFont)); addParagraph(getString("Jobs"), "jobs.png"); writeJobs(rangeJobCounter, false); pdfJobCounterReport = writeCounter(rangeJobCounter); } if (isCacheEnabled()) { addToDocument(new Phrase("\n", normalFont)); addParagraph(getString("Caches"), "caches.png"); writeCaches(false); } newPage(); addParagraph(getString("Statistiques_detaillees"), "systemmonitor.png"); writeGraphs(collector.getDisplayedOtherJRobins(), smallOtherGraphs); writeGraphDetails(); writeCountersDetails(pdfCounterReports); if (!collectorServer) { addParagraph(getString("Requetes_en_cours_detaillees"), "hourglass.png"); // si on n'est pas sur le serveur de collecte il n'y a qu'un javaInformations writeCurrentRequestsDetails(pdfCounterRequestContextReports); } addParagraph(getString("Informations_systemes_detaillees"), "systeminfo.png"); new PdfJavaInformationsReport(javaInformationsList, getDocument()) .writeInformationsDetails(); addParagraph(getString("Threads_detailles"), "threads.png"); writeThreads(true); if (isJobEnabled()) { addToDocument(new Phrase("\n", normalFont)); addParagraph(getString("Jobs_detailles"), "jobs.png"); writeJobs(rangeJobCounter, true); writeCounterDetails(pdfJobCounterReport); } if (isCacheEnabled()) { addToDocument(new Phrase("\n", normalFont)); addParagraph(getString("Caches_detailles"), "caches.png"); writeCaches(true); } writeDurationAndOverhead(); } private String buildSummary() { final String tmp; if (range.getPeriod() == Period.TOUT) { final String startDate = I18N.createDateAndTimeFormat().format( collector.getCounters().get(0).getStartDate()); tmp = getFormattedString("Statistiques", "JavaMelody", I18N.getCurrentDateAndTime(), startDate, collector.getApplication()); } else { tmp = getFormattedString("Statistiques_sans_depuis", "JavaMelody", I18N.getCurrentDateAndTime(), collector.getApplication()); } if (javaInformationsList.get(0).getContextDisplayName() != null) { return tmp + " (" + javaInformationsList.get(0).getContextDisplayName() + ')'; } return tmp; } private void writeGraphs(Collection<JRobin> jrobins, Map<String, byte[]> mySmallGraphs) throws IOException, DocumentException { if (collector.isStopped()) { // pas de graphs, ils seraient en erreur sans timer // mais un message d'avertissement à la place final String message = getString("collect_server_misusage"); final Paragraph jrobinParagraph = new Paragraph(message, PdfFonts.BOLD.getFont()); jrobinParagraph.setAlignment(Element.ALIGN_CENTER); addToDocument(jrobinParagraph); return; } final Paragraph jrobinParagraph = new Paragraph("", FontFactory.getFont( FontFactory.HELVETICA, 9f, Font.NORMAL)); jrobinParagraph.setAlignment(Element.ALIGN_CENTER); jrobinParagraph.add(new Phrase("\n\n\n\n")); int i = 0; if (mySmallGraphs != null) { // si les graphiques ont été préinitialisés (en Swing) alors on les utilise for (final byte[] imageData : mySmallGraphs.values()) { if (i % 3 == 0 && i != 0) { // un retour après httpSessions et avant activeThreads pour l'alignement jrobinParagraph.add(new Phrase("\n\n\n\n\n")); } final Image image = Image.getInstance(imageData); image.scalePercent(50); jrobinParagraph.add(new Phrase(new Chunk(image, 0, 0))); jrobinParagraph.add(new Phrase(" ")); i++; } } else { if (jrobins.isEmpty()) { return; } for (final JRobin jrobin : jrobins) { if (i % 3 == 0 && i != 0) { // un retour après httpSessions et avant activeThreads pour l'alignement jrobinParagraph.add(new Phrase("\n\n\n\n\n")); } final Image image = Image.getInstance(jrobin.graph(range, SMALL_GRAPH_WIDTH, SMALL_GRAPH_HEIGHT)); image.scalePercent(50); jrobinParagraph.add(new Phrase(new Chunk(image, 0, 0))); jrobinParagraph.add(new Phrase(" ")); i++; } } jrobinParagraph.add(new Phrase("\n")); addToDocument(jrobinParagraph); } private void writeGraphDetails() throws IOException, DocumentException { if (collector.isStopped()) { return; } final PdfPTable jrobinTable = new PdfPTable(1); jrobinTable.setHorizontalAlignment(Element.ALIGN_CENTER); jrobinTable.setWidthPercentage(100); jrobinTable.getDefaultCell().setBorder(0); if (largeGraphs != null) { // si les graphiques ont été préinitialisés (en Swing) alors on les utilise for (final byte[] imageData : largeGraphs.values()) { final Image image = Image.getInstance(imageData); jrobinTable.addCell(image); } } else { final Collection<JRobin> counterJRobins = collector.getDisplayedCounterJRobins(); if (counterJRobins.isEmpty()) { return; } for (final JRobin jrobin : counterJRobins) { // la hauteur de l'image est prévue pour qu'il n'y ait pas de graph seul sur une page final Image image = Image.getInstance(jrobin.graph(range, LARGE_GRAPH_WIDTH, LARGE_GRAPH_HEIGHT)); jrobinTable.addCell(image); } } newPage(); addToDocument(jrobinTable); newPage(); } private List<PdfCounterReport> writeCounters(List<Counter> counters) throws IOException, DocumentException { final List<PdfCounterReport> pdfCounterReports = new ArrayList<PdfCounterReport>(); for (final Counter counter : counters) { pdfCounterReports.add(writeCounter(counter)); } return pdfCounterReports; } private PdfCounterReport writeCounter(Counter counter) throws DocumentException, IOException { final String counterLabel = getString(counter.getName() + "Label"); addParagraph( getFormattedString("Statistiques_compteur", counterLabel) + " - " + range.getLabel(), counter.getIconName()); final PdfCounterReport pdfCounterReport = new PdfCounterReport(collector, counter, range, false, getDocument()); pdfCounterReport.toPdf(); return pdfCounterReport; } private void writeCountersDetails(List<PdfCounterReport> pdfCounterReports) throws DocumentException, IOException { for (final PdfCounterReport pdfCounterReport : pdfCounterReports) { writeCounterDetails(pdfCounterReport); } } private void writeCounterDetails(PdfCounterReport pdfCounterReport) throws DocumentException, IOException { final String counterLabel = getString(pdfCounterReport.getCounterName() + "Label"); addParagraph(getFormattedString("Statistiques_compteur_detaillees", counterLabel) + " - " + range.getLabel(), pdfCounterReport.getCounterIconName()); pdfCounterReport.writeRequestDetails(); if (pdfCounterReport.isErrorCounter()) { addParagraph(getString(pdfCounterReport.getCounterName() + "ErrorLabel") + " - " + range.getLabel(), pdfCounterReport.getCounterIconName()); pdfCounterReport.writeErrorDetails(); } } private List<PdfCounterRequestContextReport> writeCurrentRequests( JavaInformations javaInformations, List<Counter> counters, List<PdfCounterReport> pdfCounterReports) throws IOException, DocumentException { final List<PdfCounterRequestContextReport> pdfCounterRequestContextReports = new ArrayList<PdfCounterRequestContextReport>(); final List<CounterRequestContext> rootCurrentContexts; if (currentRequests == null) { rootCurrentContexts = collector.getRootCurrentContexts(counters); } else { rootCurrentContexts = currentRequests; } if (rootCurrentContexts.isEmpty()) { addToDocument(new Phrase(getString("Aucune_requete_en_cours"), normalFont)); } else { final PdfCounterRequestContextReport pdfCounterRequestContextReport = new PdfCounterRequestContextReport( rootCurrentContexts, pdfCounterReports, javaInformations.getThreadInformationsList(), javaInformations.isStackTraceEnabled(), pdfDocumentFactory, getDocument()); pdfCounterRequestContextReport.toPdf(); pdfCounterRequestContextReports.add(pdfCounterRequestContextReport); } return pdfCounterRequestContextReports; } private void writeCurrentRequestsDetails( List<PdfCounterRequestContextReport> pdfCounterRequestContextReports) throws IOException, DocumentException { for (final PdfCounterRequestContextReport pdfCounterRequestContextReport : pdfCounterRequestContextReports) { pdfCounterRequestContextReport.writeContextDetails(); } if (pdfCounterRequestContextReports.isEmpty()) { addToDocument(new Phrase(getString("Aucune_requete_en_cours"), normalFont)); } } private void writeThreads(boolean includeDetails) throws DocumentException, IOException { String eol = ""; for (final JavaInformations javaInformations : javaInformationsList) { addToDocument(new Phrase(eol + getFormattedString("Threads_sur", javaInformations.getHost()) + ": ", boldFont)); addToDocument(new Phrase(getFormattedString("thread_count", javaInformations.getThreadCount(), javaInformations.getPeakThreadCount(), javaInformations.getTotalStartedThreadCount()), normalFont)); final PdfThreadInformationsReport pdfThreadInformationsReport = new PdfThreadInformationsReport( javaInformations.getThreadInformationsList(), javaInformations.isStackTraceEnabled(), pdfDocumentFactory, getDocument()); pdfThreadInformationsReport.writeDeadlocks(); if (includeDetails) { pdfThreadInformationsReport.toPdf(); } eol = "\n"; } } private void writeCaches(boolean includeDetails) throws DocumentException { String eol = ""; for (final JavaInformations javaInformations : javaInformationsList) { if (!javaInformations.isCacheEnabled()) { continue; } final List<CacheInformations> cacheInformationsList = javaInformations .getCacheInformationsList(); final String msg = getFormattedString("caches_sur", cacheInformationsList.size(), javaInformations.getHost(), javaInformations.getCurrentlyExecutingJobCount()); addToDocument(new Phrase(eol + msg, boldFont)); if (includeDetails) { new PdfCacheInformationsReport(cacheInformationsList, getDocument()).toPdf(); } eol = "\n"; } } private boolean isCacheEnabled() { for (final JavaInformations javaInformations : javaInformationsList) { if (javaInformations.isCacheEnabled()) { return true; } } return false; } private void writeJobs(Counter rangeJobCounter, boolean includeDetails) throws DocumentException, IOException { String eol = ""; for (final JavaInformations javaInformations : javaInformationsList) { if (!javaInformations.isJobEnabled()) { continue; } final List<JobInformations> jobInformationsList = javaInformations .getJobInformationsList(); final String msg = getFormattedString("jobs_sur", jobInformationsList.size(), javaInformations.getHost(), javaInformations.getCurrentlyExecutingJobCount()); addToDocument(new Phrase(eol + msg, boldFont)); if (includeDetails) { new PdfJobInformationsReport(jobInformationsList, rangeJobCounter, getDocument()) .toPdf(); } eol = "\n"; } } private boolean isJobEnabled() { for (final JavaInformations javaInformations : javaInformationsList) { if (javaInformations.isJobEnabled()) { return true; } } return false; } private void writeDurationAndOverhead() throws DocumentException { final long displayDuration = System.currentTimeMillis() - start; final String tmp = "\n\n" + getString("temps_derniere_collecte") + ": " + collector.getLastCollectDuration() + ' ' + getString("ms") + '\n' + getString("temps_affichage") + ": " + displayDuration + ' ' + getString("ms") + '\n' + getString("Estimation_overhead_memoire") + ": < " + (collector.getEstimatedMemorySize() / 1024 / 1024 + 1) + ' ' + getString("Mo") + '\n' + getString("Usage_disque") + ": " + (collector.getDiskUsage() / 1024 / 1024 + 1) + ' ' + getString("Mo"); final String string; if (Parameters.JAVAMELODY_VERSION != null) { string = tmp + "\n\n" + "JavaMelody " + Parameters.JAVAMELODY_VERSION; } else { string = tmp; } addToDocument(new Phrase(string, cellFont)); } private void addParagraph(String paragraphTitle, String iconName) throws DocumentException, IOException { addToDocument(pdfDocumentFactory.createParagraphElement(paragraphTitle, iconName)); } }