Java tutorial
/* * Sun Public License * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is available at http://www.sun.com/ * * The Original Code is the SLAMD Distributed Load Generation Engine. * The Initial Developer of the Original Code is Neil A. Wilson. * Portions created by Neil A. Wilson are Copyright (C) 2004-2019. * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): Neil A. Wilson */ package com.slamd.report; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import javax.servlet.http.HttpServletResponse; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageEncoder; import com.lowagie.text.Anchor; import com.lowagie.text.Cell; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.Font; import com.lowagie.text.FontFactory; import com.lowagie.text.Image; import com.lowagie.text.PageSize; import com.lowagie.text.Paragraph; import com.lowagie.text.Phrase; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.PdfPCell; import com.lowagie.text.pdf.PdfPTable; import com.lowagie.text.pdf.PdfPageEvent; import com.lowagie.text.pdf.PdfWriter; import com.slamd.admin.RequestInfo; import com.slamd.common.Constants; import com.slamd.common.DynamicConstants; import com.slamd.job.Job; import com.slamd.job.OptimizationAlgorithm; import com.slamd.job.OptimizingJob; import com.slamd.parameter.BooleanParameter; import com.slamd.parameter.Parameter; import com.slamd.parameter.ParameterList; import com.slamd.stat.StatTracker; /** * This class provides an implementation of a SLAMD report generator that will * write the report information to a PDF document. * * * @author Neil A. Wilson */ public class PDFReportGenerator implements ReportGenerator, PdfPageEvent { /** * The name of the configuration parameter that indicates whether to include * graphs in the report. */ public static final String PARAM_INCLUDE_GRAPHS = "include_graphs"; /** * The name of the configuration parameter that indicates whether to include * resource monitor statistics in the report. */ public static final String PARAM_INCLUDE_MONITOR_STATS = "include_monitor_config"; /** * The name of the configuration parameter that indicates whether to include * the job-specific configuration in the report. */ public static final String PARAM_INCLUDE_JOB_CONFIG = "include_job_config"; /** * The name of the configuration parameter that indicates whether to include * the schedule configuration in the report. */ public static final String PARAM_INCLUDE_SCHEDULE_CONFIG = "include_schedule_config"; /** * The name of the configuration parameter that indicates whether to include * job statistics in the report. */ public static final String PARAM_INCLUDE_STATS = "include_stats"; /** * The name of the configuration parameter that indicates whether to only * include jobs that contain statistics in the generated report. */ public static final String PARAM_REQUIRE_STATS = "require_stats"; /** * The name of the configuration parameter that indicates whether to * include the individual iterations of an optimizing job. */ public static final String PARAM_INCLUDE_OPTIMIZING_ITERATIONS = "include_optimizing_iterations"; /** * The name of the configuration parameter that indicates whether to view the * resulting PDF document in the browser (if supported) or save to disk. */ public static final String PARAM_VIEW_IN_BROWSER = "view_in_browser"; // The list used to hold the jobs that will be included in the generated // report. private ArrayList<Job> jobList; // The list used to hold the optimizing jobs that will be included in the // generated report. private ArrayList<OptimizingJob> optimizingJobList; // Indicates whether to include graphs in the generated results. private boolean includeGraphs; // Indicates whether to include the job-specific configuration in the // generated report. private boolean includeJobConfig; // Indicates whether to include resource monitor statistics in the generated // report. private boolean includeMonitorStats; // Indicates whether to include the schedule configuration in the generated // report. private boolean includeScheduleConfig; // Indicates whether to include job statistics in the generated report. private boolean includeStats; // Indicates whether to only include jobs that have statistics. private boolean requireStats; // Indicates whether to include information for individual iterations of an // optimizing job. private boolean includeOptimizingIterations; // Indicates whether to view the resulting PDF in a browser or save it to // disk. private boolean viewInBrowser; // The decimal format that will be used to format floating-point values. private DecimalFormat decimalFormat; // The set of jobs that will actually be included in the report. private Job[] reportJobs; // The set of optimizing jobs that will actually be included in the report. private OptimizingJob[] reportOptimizingJobs; // The date formatter that will be used when writing out dates. private SimpleDateFormat dateFormat; /** * Creates a new text report generator. */ public PDFReportGenerator() { jobList = new ArrayList<Job>(); optimizingJobList = new ArrayList<OptimizingJob>(); dateFormat = new SimpleDateFormat(Constants.DISPLAY_DATE_FORMAT); decimalFormat = new DecimalFormat("0.000"); includeScheduleConfig = true; includeJobConfig = true; includeStats = true; includeMonitorStats = true; includeGraphs = true; requireStats = true; includeOptimizingIterations = true; viewInBrowser = false; } /** * Retrieves a user-friendly name that can be used to indicate the type of * report that will be generated. * * @return The user-friendly name that can be used to indicate the type of * report that will be generated. */ public String getReportGeneratorName() { return "PDF Report Generator"; } /** * Retrieves a new instance of this report generator initialized with the * default configuration. * * @return A new instance of this report generator initialized with the * default configuration. */ public ReportGenerator newInstance() { return new PDFReportGenerator(); } /** * Retrieves a set of parameters that can be used to allow the user to * configure the way that the report is generated. * * @return A set of parameters that can be used to allow the user to * configure the way that the report is generated. */ public ParameterList getReportParameterStubs() { BooleanParameter includeScheduleConfigParameter = new BooleanParameter(PARAM_INCLUDE_SCHEDULE_CONFIG, "Include Schedule Configuration", "Indicates whether the schedule configuration " + "information should be included in the report.", includeScheduleConfig); BooleanParameter includeJobConfigParameter = new BooleanParameter(PARAM_INCLUDE_JOB_CONFIG, "Include Job-Specific Configuration", "Indicates whether the job-specific " + "configuration information should be included " + "in the report.", includeJobConfig); BooleanParameter includeStatsParameter = new BooleanParameter(PARAM_INCLUDE_STATS, "Include Job Statistics", "Indicates whether the statistics collected " + "from job execution should be included in the " + "report.", includeStats); BooleanParameter includeMonitorParameter = new BooleanParameter(PARAM_INCLUDE_MONITOR_STATS, "Include Resource Monitor Statistics", "Indicates whether the statistics collected " + "from job execution should be included in the " + "report.", includeMonitorStats); BooleanParameter includeGraphsParameter = new BooleanParameter(PARAM_INCLUDE_GRAPHS, "Include Graphs of Statistics", "Indicates whether graphs of statistical " + "information should be included in the report.", includeGraphs); BooleanParameter requireStatsParameter = new BooleanParameter(PARAM_REQUIRE_STATS, "Only Include Jobs with Statistics", "Indicates whether to only include jobs that " + "have statistics available.", requireStats); BooleanParameter includeOptimizingParameter = new BooleanParameter(PARAM_INCLUDE_OPTIMIZING_ITERATIONS, "Include Optimizing Job Iterations", "Indicates whether to include data for " + "optimizing job iterations.", includeOptimizingIterations); BooleanParameter viewInBrowserParameter = new BooleanParameter(PARAM_VIEW_IN_BROWSER, "View PDF in Browser", "Indicates whether the resulting PDF file " + "should be viewed in the browser or save it to " + "disk.", viewInBrowser); Parameter[] parameters = new Parameter[] { includeScheduleConfigParameter, includeJobConfigParameter, includeStatsParameter, includeMonitorParameter, includeGraphsParameter, requireStatsParameter, includeOptimizingParameter, viewInBrowserParameter }; return new ParameterList(parameters); } /** * Initializes this reporter based on the parameters customized by the end * user. * * @param reportParameters The set of parameters provided by the end user * that should be used to customize the report. */ public void initializeReporter(ParameterList reportParameters) { BooleanParameter bp = reportParameters.getBooleanParameter(PARAM_INCLUDE_SCHEDULE_CONFIG); if (bp != null) { includeScheduleConfig = bp.getBooleanValue(); } bp = reportParameters.getBooleanParameter(PARAM_INCLUDE_JOB_CONFIG); if (bp != null) { includeJobConfig = bp.getBooleanValue(); } bp = reportParameters.getBooleanParameter(PARAM_INCLUDE_STATS); if (bp != null) { includeStats = bp.getBooleanValue(); } bp = reportParameters.getBooleanParameter(PARAM_INCLUDE_MONITOR_STATS); if (bp != null) { includeMonitorStats = bp.getBooleanValue(); } bp = reportParameters.getBooleanParameter(PARAM_INCLUDE_GRAPHS); if (bp != null) { includeGraphs = bp.getBooleanValue(); } bp = reportParameters.getBooleanParameter(PARAM_REQUIRE_STATS); if (bp != null) { requireStats = bp.getBooleanValue(); } bp = reportParameters.getBooleanParameter(PARAM_INCLUDE_OPTIMIZING_ITERATIONS); if (bp != null) { includeOptimizingIterations = bp.getBooleanValue(); } bp = reportParameters.getBooleanParameter(PARAM_VIEW_IN_BROWSER); if (bp != null) { viewInBrowser = bp.getBooleanValue(); } } /** * Indicates that information about the provided job should be included in the * report. * * @param job The job about which to include information in the report. */ public void addJobReport(Job job) { if (requireStats && (!job.hasStats())) { return; } jobList.add(job); } /** * Indicates that information about the provided optimizing job should be * included in the report. * * @param optimizingJob The optimizing job about which to include * information in the report. */ public void addOptimizingJobReport(OptimizingJob optimizingJob) { if (requireStats && (!optimizingJob.hasStats())) { return; } optimizingJobList.add(optimizingJob); } /** * Generates the report and sends it to the user over the provided servlet * response. * * @param requestInfo State information about the request being processed. */ public void generateReport(RequestInfo requestInfo) { // Determine exactly what to include in the report. We will want to strip // out any individual jobs that are part of an optimizing job that is also // to be included in the report. reportOptimizingJobs = new OptimizingJob[optimizingJobList.size()]; optimizingJobList.toArray(reportOptimizingJobs); ArrayList<Job> tmpList = new ArrayList<Job>(jobList.size()); for (int i = 0; i < jobList.size(); i++) { Job job = jobList.get(i); String optimizingJobID = job.getOptimizingJobID(); if ((optimizingJobID != null) && (optimizingJobID.length() > 0)) { boolean matchFound = false; for (int j = 0; j < reportOptimizingJobs.length; j++) { if (optimizingJobID.equalsIgnoreCase(reportOptimizingJobs[j].getOptimizingJobID())) { matchFound = true; break; } } if (matchFound) { continue; } } tmpList.add(job); } reportJobs = new Job[tmpList.size()]; tmpList.toArray(reportJobs); // Prepare to actually generate the report and send it to the user. HttpServletResponse response = requestInfo.getResponse(); if (viewInBrowser) { response.setContentType("application/pdf"); } else { response.setContentType("application/x-slamd-report-pdf"); } response.addHeader("Content-Disposition", "filename=\"slamd_data_report.pdf\""); try { // Create the PDF document and associate it with the response to send to // the client. Document document = new Document(PageSize.LETTER); PdfWriter writer = PdfWriter.getInstance(document, response.getOutputStream()); document.addTitle("SLAMD Generated Report"); document.addCreationDate(); document.addCreator("SLAMD Distributed Load Generator"); writer.setPageEvent(this); // Open the document and add the table of contents. document.open(); boolean needNewPage = writeContents(document); // Write the regular job information. for (int i = 0; i < reportJobs.length; i++) { if (needNewPage) { document.newPage(); } writeJob(document, reportJobs[i]); needNewPage = true; } // Write the optimizing job information. for (int i = 0; i < reportOptimizingJobs.length; i++) { if (needNewPage) { document.newPage(); } writeOptimizingJob(document, reportOptimizingJobs[i]); needNewPage = true; } // Close the document. document.close(); } catch (Exception e) { // Not much we can do about this. e.printStackTrace(); return; } } /** * Writes the table of contents to the document. * * @param document The document to which the contents are to be written. * * @return {@code true} if the contents information was written to the * PDF document, or {@code false} if not. * * @throws DocumentException If a problem occurs while writing the contents. */ private boolean writeContents(Document document) throws DocumentException { // First, make sure that there is actually something to write. If we're // only going to write information for a single job or optimizing job, then // there is no reason to have a contents section. if (((reportJobs.length == 1) && (reportOptimizingJobs.length == 0)) || ((reportJobs.length == 0) && (reportOptimizingJobs.length == 1))) { return false; } if (reportJobs.length > 0) { // Write the job data header. Paragraph p = new Paragraph("Job Data", FontFactory.getFont(FontFactory.HELVETICA, 18, Font.BOLD, Color.BLACK)); document.add(p); // Create a table with the list of jobs. PdfPTable table = new PdfPTable(3); table.setWidthPercentage(100); writeTableHeaderCell(table, "Job ID"); writeTableHeaderCell(table, "Description"); writeTableHeaderCell(table, "Job Type"); for (int i = 0; i < reportJobs.length; i++) { Job job = reportJobs[i]; Anchor anchor = new Anchor(job.getJobID(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.UNDERLINE, Color.BLUE)); anchor.setReference('#' + job.getJobID()); table.addCell(new PdfPCell(anchor)); String descriptionStr = job.getJobDescription(); if ((descriptionStr == null) || (descriptionStr.length() == 0)) { descriptionStr = "(Not Specified)"; } writeTableCell(table, descriptionStr); writeTableCell(table, job.getJobClass().getJobName()); } document.add(table); // Write a blank line between the job data and optimizing job data. document.add(new Paragraph(" ")); } if (reportOptimizingJobs.length > 0) { // Write the optimizing job data header. Paragraph p = new Paragraph("Optimizing Job Data", FontFactory.getFont(FontFactory.HELVETICA, 18, Font.BOLD, Color.BLACK)); document.add(p); // Create a table with the list of jobs. PdfPTable table = new PdfPTable(3); table.setWidthPercentage(100); writeTableHeaderCell(table, "Optimizing Job ID"); writeTableHeaderCell(table, "Description"); writeTableHeaderCell(table, "Job Type"); for (int i = 0; i < reportOptimizingJobs.length; i++) { OptimizingJob optimizingJob = reportOptimizingJobs[i]; Anchor anchor = new Anchor(optimizingJob.getOptimizingJobID(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.UNDERLINE, Color.BLUE)); anchor.setReference('#' + optimizingJob.getOptimizingJobID()); table.addCell(new PdfPCell(anchor)); String descriptionStr = optimizingJob.getDescription(); if ((descriptionStr == null) || (descriptionStr.length() == 0)) { descriptionStr = "(Not Specified)"; } writeTableCell(table, descriptionStr); writeTableCell(table, optimizingJob.getJobClass().getJobName()); } document.add(table); } return true; } /** * Writes information about the provided job to the document. * * @param document The document to which the job information should be * written. * @param job The job to include in the document. * * @throws DocumentException If a problem occurs while writing the contents. */ private void writeJob(Document document, Job job) throws DocumentException { Anchor anchor = new Anchor("Job " + job.getJobID(), FontFactory.getFont(FontFactory.HELVETICA, 18, Font.BOLD, Color.BLACK)); anchor.setName(job.getJobID()); Paragraph p = new Paragraph(anchor); document.add(p); // Write the general information to the document. p = new Paragraph("General Information", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK)); document.add(p); PdfPTable table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 30, 70 }); writeTableCell(table, "Job ID"); writeTableCell(table, job.getJobID()); String optimizingJobID = job.getOptimizingJobID(); if ((optimizingJobID != null) && (optimizingJobID.length() > 0)) { writeTableCell(table, "Optimizing Job ID"); writeTableCell(table, optimizingJobID); } String descriptionStr = job.getJobDescription(); if ((descriptionStr == null) || (descriptionStr.length() == 0)) { descriptionStr = "(Not Specified)"; } writeTableCell(table, "Job Description"); writeTableCell(table, descriptionStr); writeTableCell(table, "Job Type"); writeTableCell(table, job.getJobClassName()); writeTableCell(table, "Job Class"); writeTableCell(table, job.getJobClass().getClass().getName()); writeTableCell(table, "Job State"); writeTableCell(table, job.getJobStateString()); document.add(table); // Write the schedule config if appropriate. if (includeScheduleConfig) { document.add(new Paragraph(" ")); p = new Paragraph("Schedule Information", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK)); document.add(p); table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 30, 70 }); Date startTime = job.getStartTime(); String startStr; if (startTime == null) { startStr = "(Not Available)"; } else { startStr = dateFormat.format(startTime); } writeTableCell(table, "Scheduled Start Time"); writeTableCell(table, startStr); Date stopTime = job.getStopTime(); String stopStr; if (stopTime == null) { stopStr = "(Not Specified)"; } else { stopStr = dateFormat.format(stopTime); } writeTableCell(table, "Scheduled Stop Time"); writeTableCell(table, stopStr); int duration = job.getDuration(); String durationStr; if (duration > 0) { durationStr = duration + " seconds"; } else { durationStr = "(Not Specified)"; } writeTableCell(table, "Scheduled Duration"); writeTableCell(table, durationStr); writeTableCell(table, "Number of Clients"); writeTableCell(table, String.valueOf(job.getNumberOfClients())); String[] requestedClients = job.getRequestedClients(); if ((requestedClients != null) && (requestedClients.length > 0)) { PdfPTable clientTable = new PdfPTable(1); for (int i = 0; i < requestedClients.length; i++) { PdfPCell clientCell = new PdfPCell(new Phrase(requestedClients[i])); clientCell.setBorder(0); clientTable.addCell(clientCell); } writeTableCell(table, "Requested Clients"); table.addCell(clientTable); } String[] monitorClients = job.getResourceMonitorClients(); if ((monitorClients != null) && (monitorClients.length > 0)) { PdfPTable clientTable = new PdfPTable(1); for (int i = 0; i < monitorClients.length; i++) { PdfPCell clientCell = new PdfPCell(new Phrase(monitorClients[i])); clientCell.setBorder(0); clientTable.addCell(clientCell); } writeTableCell(table, "Resource Monitor Clients"); table.addCell(clientTable); } writeTableCell(table, "Threads per Client"); writeTableCell(table, String.valueOf(job.getThreadsPerClient())); writeTableCell(table, "Thread Startup Delay"); writeTableCell(table, job.getThreadStartupDelay() + " milliseconds"); writeTableCell(table, "Statistics Collection Interval"); writeTableCell(table, job.getCollectionInterval() + " seconds"); document.add(table); } // Write the job-specific parameter information if appropriate. if (includeJobConfig) { document.add(new Paragraph(" ")); p = new Paragraph("Parameter Information", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK)); document.add(p); table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 30, 70 }); Parameter[] params = job.getParameterList().getParameters(); for (int i = 0; i < params.length; i++) { writeTableCell(table, params[i].getDisplayName()); writeTableCell(table, params[i].getDisplayValue()); } document.add(table); } // Write the statistical data if appropriate. if (includeStats && job.hasStats()) { document.add(new Paragraph(" ")); p = new Paragraph("General Execution Data", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK)); document.add(p); table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 30, 70 }); Date actualStartTime = job.getActualStartTime(); String startStr; if (actualStartTime == null) { startStr = "(Not Available)"; } else { startStr = dateFormat.format(actualStartTime); } writeTableCell(table, "Actual Start Time"); writeTableCell(table, startStr); Date actualStopTime = job.getActualStopTime(); String stopStr; if (actualStopTime == null) { stopStr = "(Not Available)"; } else { stopStr = dateFormat.format(actualStopTime); } writeTableCell(table, "Actual Stop Time"); writeTableCell(table, stopStr); int actualDuration = job.getActualDuration(); String durationStr; if (actualDuration > 0) { durationStr = actualDuration + " seconds"; } else { durationStr = "(Not Available)"; } writeTableCell(table, "Actual Duration"); writeTableCell(table, durationStr); String[] clients = job.getStatTrackerClientIDs(); if ((clients != null) && (clients.length > 0)) { PdfPTable clientTable = new PdfPTable(1); for (int i = 0; i < clients.length; i++) { PdfPCell clientCell = new PdfPCell(new Phrase(clients[i])); clientCell.setBorder(0); clientTable.addCell(clientCell); } writeTableCell(table, "Clients Used"); table.addCell(clientTable); } document.add(table); String[] trackerNames = job.getStatTrackerNames(); for (int i = 0; i < trackerNames.length; i++) { StatTracker[] trackers = job.getStatTrackers(trackerNames[i]); if ((trackers != null) && (trackers.length > 0)) { document.newPage(); StatTracker tracker = trackers[0].newInstance(); tracker.aggregate(trackers); document.add(new Paragraph(" ")); document.add(new Paragraph(trackerNames[i], FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK))); String[] summaryNames = tracker.getSummaryLabels(); String[] summaryValues = tracker.getSummaryData(); table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 50, 50 }); for (int j = 0; j < summaryNames.length; j++) { writeTableCell(table, summaryNames[j]); writeTableCell(table, summaryValues[j]); } document.add(table); if (includeGraphs) { try { ParameterList params = tracker.getGraphParameterStubs(job); BufferedImage graphImage = tracker.createGraph(job, Constants.DEFAULT_GRAPH_WIDTH, Constants.DEFAULT_GRAPH_HEIGHT, params); Image image = Image.getInstance(imageToByteArray(graphImage)); image.scaleToFit(inchesToPoints(5.5), inchesToPoints(4.5)); document.add(image); } catch (Exception e) { } } } } } // Write the resource monitor data if appropriate. if (includeMonitorStats && job.hasResourceStats()) { String[] trackerNames = job.getResourceStatTrackerNames(); for (int i = 0; i < trackerNames.length; i++) { StatTracker[] trackers = job.getResourceStatTrackers(trackerNames[i]); if ((trackers != null) && (trackers.length > 0)) { document.newPage(); StatTracker tracker = trackers[0].newInstance(); tracker.aggregate(trackers); document.add(new Paragraph(" ")); document.add(new Paragraph(trackerNames[i], FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK))); String[] summaryNames = tracker.getSummaryLabels(); String[] summaryValues = tracker.getSummaryData(); table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 50, 50 }); for (int j = 0; j < summaryNames.length; j++) { writeTableCell(table, summaryNames[j]); writeTableCell(table, summaryValues[j]); } document.add(table); if (includeGraphs) { try { ParameterList params = tracker.getGraphParameterStubs(job); BufferedImage graphImage = tracker.createMonitorGraph(job, Constants.DEFAULT_GRAPH_WIDTH, Constants.DEFAULT_MONITOR_GRAPH_HEIGHT, params); Image image = Image.getInstance(imageToByteArray(graphImage)); image.scaleToFit(inchesToPoints(5.5), inchesToPoints(4.5)); document.add(image); } catch (Exception e) { } } } } } } /** * Writes information about the provided optimizing job to the document. * * @param document The document to which the job information should be * written. * @param optimizingJob The optimizing job to include in the document. * * @throws DocumentException If a problem occurs while writing the contents. */ private void writeOptimizingJob(Document document, OptimizingJob optimizingJob) throws DocumentException { Anchor anchor = new Anchor("Optimizing Job " + optimizingJob.getOptimizingJobID(), FontFactory.getFont(FontFactory.HELVETICA, 18, Font.BOLD, Color.BLACK)); anchor.setName(optimizingJob.getOptimizingJobID()); Paragraph p = new Paragraph(anchor); document.add(p); // Write the general information to the document. p = new Paragraph("General Information", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK)); document.add(p); PdfPTable table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 30, 70 }); writeTableCell(table, "Optimizing Job ID"); writeTableCell(table, optimizingJob.getOptimizingJobID()); writeTableCell(table, "Job Type"); writeTableCell(table, optimizingJob.getJobClassName()); String descriptionStr = optimizingJob.getDescription(); if ((descriptionStr == null) || (descriptionStr.length() == 0)) { descriptionStr = "(Not Specified)"; } writeTableCell(table, "Base Description"); writeTableCell(table, descriptionStr); writeTableCell(table, "Include Thread Count in Description"); writeTableCell(table, String.valueOf(optimizingJob.includeThreadsInDescription())); writeTableCell(table, "Job State"); writeTableCell(table, Constants.jobStateToString(optimizingJob.getJobState())); document.add(table); // Write the schedule config to the document if appropriate. if (includeScheduleConfig) { document.add(new Paragraph(" ")); p = new Paragraph("Schedule Information", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK)); document.add(p); table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 30, 70 }); Date startTime = optimizingJob.getStartTime(); String startStr; if (startTime == null) { startStr = "(Not Available)"; } else { startStr = dateFormat.format(startTime); } writeTableCell(table, "Scheduled Start Time"); writeTableCell(table, startStr); int duration = optimizingJob.getDuration(); String durationStr; if (duration > 0) { durationStr = duration + " seconds"; } else { durationStr = "(Not Specified)"; } writeTableCell(table, "Job Duration"); writeTableCell(table, durationStr); writeTableCell(table, "Delay Between Iterations"); writeTableCell(table, optimizingJob.getDelayBetweenIterations() + " seconds"); writeTableCell(table, "Number of Clients"); writeTableCell(table, String.valueOf(optimizingJob.getNumClients())); String[] requestedClients = optimizingJob.getRequestedClients(); if ((requestedClients != null) && (requestedClients.length > 0)) { PdfPTable clientTable = new PdfPTable(1); for (int i = 0; i < requestedClients.length; i++) { PdfPCell clientCell = new PdfPCell(new Phrase(requestedClients[i])); clientCell.setBorder(0); clientTable.addCell(clientCell); } writeTableCell(table, "Requested Clients"); table.addCell(clientTable); } String[] monitorClients = optimizingJob.getResourceMonitorClients(); if ((monitorClients != null) && (monitorClients.length > 0)) { PdfPTable clientTable = new PdfPTable(1); for (int i = 0; i < monitorClients.length; i++) { PdfPCell clientCell = new PdfPCell(new Phrase(monitorClients[i])); clientCell.setBorder(0); clientTable.addCell(clientCell); } writeTableCell(table, "Resource Monitor Clients"); table.addCell(clientTable); } writeTableCell(table, "Minimum Number of Threads"); writeTableCell(table, String.valueOf(optimizingJob.getMinThreads())); int maxThreads = optimizingJob.getMaxThreads(); String maxThreadsStr; if (maxThreads > 0) { maxThreadsStr = String.valueOf(maxThreads); } else { maxThreadsStr = "(Not Specified)"; } writeTableCell(table, "Maximum Number of Threads"); writeTableCell(table, maxThreadsStr); writeTableCell(table, "Thread Increment Between Iterations"); writeTableCell(table, String.valueOf(optimizingJob.getThreadIncrement())); writeTableCell(table, "Statistics Collection Interval"); writeTableCell(table, optimizingJob.getCollectionInterval() + " seconds"); document.add(table); } // Get the optimization algorithm used. OptimizationAlgorithm optimizationAlgorithm = optimizingJob.getOptimizationAlgorithm(); ParameterList paramList = optimizationAlgorithm.getOptimizationAlgorithmParameters(); Parameter[] optimizationParams = paramList.getParameters(); // Write the optimizing config to the document if appropriate. if (includeScheduleConfig) { document.add(new Paragraph(" ")); p = new Paragraph("Optimization Settings", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK)); document.add(p); table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 30, 70 }); for (int i = 0; i < optimizationParams.length; i++) { writeTableCell(table, optimizationParams[i].getDisplayName()); writeTableCell(table, optimizationParams[i].getDisplayValue()); } writeTableCell(table, "Maximum Consecutive Non-Improving Iterations"); writeTableCell(table, String.valueOf(optimizingJob.getMaxNonImproving())); writeTableCell(table, "Re-Run Best Iteration"); writeTableCell(table, String.valueOf(optimizingJob.reRunBestIteration())); int reRunDuration = optimizingJob.getReRunDuration(); String durationStr; if (reRunDuration > 0) { durationStr = reRunDuration + " seconds"; } else { durationStr = "(Not Specified)"; } writeTableCell(table, "Re-Run Duration"); writeTableCell(table, durationStr); document.add(table); } // Write the job-specific config to the document if appropriate. if (includeJobConfig) { document.add(new Paragraph(" ")); p = new Paragraph("Parameter Information", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK)); document.add(p); table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 30, 70 }); Parameter[] params = optimizingJob.getParameters().getParameters(); for (int i = 0; i < params.length; i++) { writeTableCell(table, params[i].getDisplayName()); writeTableCell(table, params[i].getDisplayValue()); } document.add(table); } // Write the statistical data to the document if appropriate. if (includeStats && optimizingJob.hasStats()) { document.add(new Paragraph(" ")); p = new Paragraph("Execution Data", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK)); document.add(p); table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 30, 70 }); Date actualStartTime = optimizingJob.getActualStartTime(); String startTimeStr; if (actualStartTime == null) { startTimeStr = "(Not Available)"; } else { startTimeStr = dateFormat.format(actualStartTime); } writeTableCell(table, "Actual Start Time"); writeTableCell(table, startTimeStr); Date actualStopTime = optimizingJob.getActualStopTime(); String stopTimeStr; if (actualStopTime == null) { stopTimeStr = "(Not Available)"; } else { stopTimeStr = dateFormat.format(actualStopTime); } writeTableCell(table, "Actual Stop Time"); writeTableCell(table, stopTimeStr); Job[] iterations = optimizingJob.getAssociatedJobs(); if ((iterations != null) && (iterations.length > 0)) { writeTableCell(table, "Job Iterations Completed"); writeTableCell(table, String.valueOf(iterations.length)); int optimalThreadCount = optimizingJob.getOptimalThreadCount(); String threadStr; if (optimalThreadCount > 0) { threadStr = String.valueOf(optimalThreadCount); } else { threadStr = "(Not Available)"; } writeTableCell(table, "Optimal Thread Count"); writeTableCell(table, threadStr); double optimalValue = optimizingJob.getOptimalValue(); String valueStr; if (optimalThreadCount > 0) { valueStr = decimalFormat.format(optimalValue); } else { valueStr = "(Not Available)"; } writeTableCell(table, "Optimal Value"); writeTableCell(table, valueStr); String optimalID = optimizingJob.getOptimalJobID(); writeTableCell(table, "Optimal Job Iteration"); if ((optimalID == null) || (optimalID.length() == 0)) { writeTableCell(table, "(Not Available)"); } else if (includeOptimizingIterations) { anchor = new Anchor(optimalID, FontFactory.getFont(FontFactory.HELVETICA, 12, Font.UNDERLINE, Color.BLUE)); anchor.setReference('#' + optimalID); table.addCell(new PdfPCell(anchor)); } else { writeTableCell(table, optimalID); } } Job reRunIteration = optimizingJob.getReRunIteration(); if (reRunIteration != null) { writeTableCell(table, "Re-Run Iteration"); if (includeOptimizingIterations) { anchor = new Anchor(reRunIteration.getJobID(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.UNDERLINE, Color.BLUE)); anchor.setReference('#' + reRunIteration.getJobID()); table.addCell(new PdfPCell(anchor)); } else { writeTableCell(table, reRunIteration.getJobID()); } String valueStr; try { double iterationValue = optimizationAlgorithm.getIterationOptimizationValue(reRunIteration); valueStr = decimalFormat.format(iterationValue); } catch (Exception e) { valueStr = "N/A"; } writeTableCell(table, "Re-Run Iteration Value"); writeTableCell(table, valueStr); } document.add(table); if (includeOptimizingIterations && (iterations != null) && (iterations.length > 0)) { document.add(new Paragraph(" ")); p = new Paragraph("Job Iterations", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK)); document.add(p); table = new PdfPTable(2); table.setWidthPercentage(100); table.setWidths(new int[] { 50, 50 }); for (int i = 0; i < iterations.length; i++) { String valueStr; try { double iterationValue = optimizationAlgorithm.getIterationOptimizationValue(iterations[i]); valueStr = decimalFormat.format(iterationValue); } catch (Exception e) { valueStr = "N/A"; } anchor = new Anchor(iterations[i].getJobID(), FontFactory.getFont(FontFactory.HELVETICA, 12, Font.UNDERLINE, Color.BLUE)); anchor.setReference('#' + iterations[i].getJobID()); table.addCell(new PdfPCell(anchor)); writeTableCell(table, valueStr); } document.add(table); } if (includeGraphs && (iterations != null) && (iterations.length > 0)) { String[] statNames = iterations[0].getStatTrackerNames(); for (int j = 0; j < statNames.length; j++) { StatTracker[] trackers = iterations[0].getStatTrackers(statNames[j]); if ((trackers != null) && (trackers.length > 0)) { StatTracker tracker = trackers[0].newInstance(); tracker.aggregate(trackers); try { document.newPage(); ParameterList params = tracker.getGraphParameterStubs(iterations); BufferedImage graphImage = tracker.createGraph(iterations, Constants.DEFAULT_GRAPH_WIDTH, Constants.DEFAULT_GRAPH_HEIGHT, params); Image image = Image.getInstance(imageToByteArray(graphImage)); image.scaleToFit(inchesToPoints(5.5), inchesToPoints(4.5)); document.add(image); } catch (Exception e) { } } } } if (includeOptimizingIterations && (iterations != null) && (iterations.length > 0)) { for (int i = 0; i < iterations.length; i++) { document.newPage(); writeJob(document, iterations[i]); } } if (includeOptimizingIterations && (reRunIteration != null)) { document.newPage(); writeJob(document, reRunIteration); } } } /** * Writes the specified text to the provided table as a header cell. * * @param table The table to which the header cell should be written. * @param text The text to write to the header cell. */ private void writeTableHeaderCell(PdfPTable table, String text) { Phrase phrase = new Phrase(text, FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, Color.BLACK)); table.addCell(new PdfPCell(phrase)); } /** * Writes the specified text to the provided table as a normal cell. * * @param table The table to which the cell should be written. * @param text The text to write to the cell. */ private void writeTableCell(PdfPTable table, String text) { table.addCell(new PdfPCell(new Phrase(text))); } /** * Converts the specified number of inches into points (there are 72 points * per inch). The number of inches provided does not need to be an integer. * * @param numInches The number of inches to be converted to points. * * @return The number of points corresponding to the provided number of * inches. */ public static int inchesToPoints(double numInches) { return (int) Math.round(numInches * 72); } /** * Converts the provided image to a byte array containing data for the PNG * representation of the image. * * @param image The image to be converted to a byte array. * * @return The byte array containing the image data. * * @throws IOException If a problem occurs while creating the image array. */ private byte[] imageToByteArray(BufferedImage image) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8192); ImageEncoder encoder = ImageCodec.createImageEncoder("png", outputStream, null); encoder.encode(image); return outputStream.toByteArray(); } /** * Performs the appropriate action necessary when starting a new page. In * this case, we will write the SLAMD header to the top of the page. * * @param writer The writer used to write the PDF document. * @param document The PDF document being written. */ public void onStartPage(PdfWriter writer, Document document) { try { PdfPTable table = new PdfPTable(3); table.setWidthPercentage(100); PdfPCell blueCell = new PdfPCell(new Phrase(" \n ")); blueCell.setHorizontalAlignment(PdfPCell.ALIGN_LEFT); blueCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); blueCell.setBackgroundColor(new Color(0x59, 0x4F, 0xBF)); blueCell.setBorderWidth(inchesToPoints(1.0 / 16)); blueCell.setBorderColor(new Color(0xFF, 0xFF, 0xFF)); blueCell.setPadding(inchesToPoints(1.0 / 16)); table.addCell(blueCell); Phrase titlePhrase = new Phrase("SLAMD Generated Report", FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, new Color(0x59, 0x4F, 0xBF))); PdfPCell yellowCell = new PdfPCell(titlePhrase); yellowCell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER); yellowCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE); yellowCell.setBackgroundColor(new Color(0xFB, 0xE2, 0x49)); yellowCell.setBorderWidth(inchesToPoints(1.0 / 16)); yellowCell.setBorderColor(new Color(0xFF, 0xFF, 0xFF)); yellowCell.setPadding(inchesToPoints(1.0 / 16)); table.addCell(yellowCell); Phrase versionPhrase = new Phrase("Version " + DynamicConstants.SLAMD_VERSION, FontFactory.getFont(FontFactory.HELVETICA, 12, Font.BOLD, new Color(0xFF, 0xFF, 0xFF))); PdfPCell redCell = new PdfPCell(versionPhrase); redCell.setHorizontalAlignment(Cell.ALIGN_RIGHT); redCell.setVerticalAlignment(Cell.ALIGN_MIDDLE); redCell.setBackgroundColor(new Color(0xD1, 0x21, 0x24)); redCell.setBorderWidth(inchesToPoints(1.0 / 16)); redCell.setBorderColor(new Color(0xFF, 0xFF, 0xFF)); redCell.setPadding(inchesToPoints(1.0 / 16)); table.addCell(redCell); document.add(table); } catch (Exception e) { e.printStackTrace(); } } /** * Performs the appropriate action necessary when ending a page. In this * case, no action is required. * * @param writer The writer used to write the PDF document. * @param document The PDF document being written. */ public void onEndPage(PdfWriter writer, Document document) { // No action necessary, but this method is required by the PdfPageEvent // interface. } /** * Performs the appropriate action necessary when opening a document. In this * case, no action is required. * * @param writer The writer used to write the PDF document. * @param document The PDF document being written. */ public void onOpenDocument(PdfWriter writer, Document document) { // No action necessary, but this method is required by the PdfPageEvent // interface. } /** * Performs the appropriate action necessary when opening a document. In this * case, no action is required. * * @param writer The writer used to write the PDF document. * @param document The PDF document being written. */ public void onCloseDocument(PdfWriter writer, Document document) { // No action necessary, but this method is required by the PdfPageEvent // interface. } /** * Performs the appropriate action necessary when starting a new paragraph. * In this case, no action is required. * * @param writer The writer used to write the PDF document. * @param document The PDF document being written. * @param paragraphPos The position of the beginning of the paragraph. */ public void onParagraph(PdfWriter writer, Document document, float paragraphPos) { // No action necessary, but this method is required by the PdfPageEvent // interface. } /** * Performs the appropriate action necessary when ending a paragraph. In this * case, no action is required. * * @param writer The writer used to write the PDF document. * @param document The PDF document being written. * @param paragraphEndPos The position of the end of the paragraph. */ public void onParagraphEnd(PdfWriter writer, Document document, float paragraphEndPos) { // No action necessary, but this method is required by the PdfPageEvent // interface. } /** * Performs the appropriate action necessary when starting a new chapter. In * this case, no action is required. * * @param writer The writer used to write the PDF document. * @param document The PDF document being written. * @param chapterPos The position at which the beginning of the chapter will * be written. * @param title The title to use for the chapter. */ public void onChapter(PdfWriter writer, Document document, float chapterPos, Paragraph title) { // No action necessary, but this method is required by the PdfPageEvent // interface. } /** * Performs the appropriate action necessary when ending a chapter. In this * case, no action is required. * * @param writer The writer used to write the PDF document. * @param document The PDF document being written. * @param chapterEndPos The position at which the end of the chapter will be * written. */ public void onChapterEnd(PdfWriter writer, Document document, float chapterEndPos) { // No action necessary, but this method is required by the PdfPageEvent // interface. } /** * Performs the appropriate action necessary when beginning a new section. In * this case, no action is required. * * @param writer The writer used to write the PDF document. * @param document The PDF document being written. * @param sectionPos The position at which the beginning of the section * will be written. * @param depth The depth for the section. * @param sectionTitle The title to use for the section. */ public void onSection(PdfWriter writer, Document document, float sectionPos, int depth, Paragraph sectionTitle) { // No action necessary, but this method is required by the PdfPageEvent // interface. } /** * Performs the appropriate action necessary when ending a section. In this * case, no action is required. * * @param writer The writer used to write the PDF document. * @param document The PDF document being written. * @param sectionEndPos The position at which the end of the section will be * written. */ public void onSectionEnd(PdfWriter writer, Document document, float sectionEndPos) { // No action necessary, but this method is required by the PdfPageEvent // interface. } /** * Performs the appropriate action necessary when writing a generic tag. In * this case, no action is required. * * @param writer The writer used to write the PDF document. * @param document The PDF document being written. * @param rectangle The rectangle containing the chunk with the generic tag. * @param text The text of the tag. */ public void onGenericTag(PdfWriter writer, Document document, Rectangle rectangle, String text) { // No action necessary, but this method is required by the PdfPageEvent // interface. } }