/* * 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-2010. * 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.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 java.util.Iterator; import java.util.LinkedHashMap; import java.util.zip.Deflater; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.sun.media.jai.codec.ImageCodec; import com.sun.media.jai.codec.ImageEncoder; 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.MultiChoiceParameter; 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 in HTML form. The resulting files will be * packaged together in a zip archive. * * * @author Neil A. Wilson */ public class HTMLReportGenerator implements ReportGenerator { /** * The end-of-line string that will be used. */ public static final String EOL = Constants.EOL; /** * The name of the configuration parameter that specifies the level of * compression to use when generating the zip archive. */ public static final String PARAM_COMPRESSION_LEVEL = "compression_level"; /** * The name of the configuration parameter that indicates whether to include * detailed statistical information in the report. */ public static final String PARAM_INCLUDE_DETAILED_STATISTICS = "include_detailed_stats"; /** * 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 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 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 compression options that will be presented to the end user. */ public static final String[] COMPRESSION_OPTIONS = new String[] { "Default Compression Level", "No Compression", "Fastest Compression", "Best Compression" }; // 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 detailed statistics for the jobs included in // the report. private boolean includeDetailedStats; // 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 only provide information for individual iterations of // an optimizing job. private boolean includeOptimizingIterations; // 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; // The decimal format that will be used to format floating-point values. private DecimalFormat decimalFormat; // The level of compression to use for the generated zip archive. private int compressionLevel; // 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 HTMLReportGenerator() { jobList = new ArrayList<Job>(); optimizingJobList = new ArrayList<OptimizingJob>(); dateFormat = new SimpleDateFormat(Constants.DISPLAY_DATE_FORMAT); decimalFormat = new DecimalFormat("0.000"); compressionLevel = Deflater.DEFAULT_COMPRESSION; includeScheduleConfig = true; includeJobConfig = true; includeStats = true; includeMonitorStats = true; includeDetailedStats = false; includeGraphs = true; requireStats = true; includeOptimizingIterations = true; } /** * 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 "HTML 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 HTMLReportGenerator(); } /** * 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 includeDetailParameter = new BooleanParameter(PARAM_INCLUDE_DETAILED_STATISTICS, "Include Detailed Statistical Information", "Indicates whether to include a detailed " + "report of the statistics collected.", includeDetailedStats); 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 the " + "individual iterations of an optimizing job.", includeOptimizingIterations); String compressionLevelStr = null; switch (compressionLevel) { case Deflater.NO_COMPRESSION: compressionLevelStr = COMPRESSION_OPTIONS[1]; break; case Deflater.BEST_SPEED: compressionLevelStr = COMPRESSION_OPTIONS[2]; break; case Deflater.BEST_COMPRESSION: compressionLevelStr = COMPRESSION_OPTIONS[3]; break; default: compressionLevelStr = COMPRESSION_OPTIONS[0]; break; } MultiChoiceParameter compressionLevelParameter = new MultiChoiceParameter(PARAM_COMPRESSION_LEVEL, "Level of Compression", "The level of compression to use when " + "generating the zip archive", COMPRESSION_OPTIONS, compressionLevelStr); Parameter[] parameters = new Parameter[] { includeScheduleConfigParameter, includeJobConfigParameter, includeStatsParameter, includeMonitorParameter, includeGraphsParameter, includeDetailParameter, requireStatsParameter, includeOptimizingParameter, compressionLevelParameter }; 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_INCLUDE_DETAILED_STATISTICS); if (bp != null) { includeDetailedStats = 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(); } MultiChoiceParameter mp = reportParameters.getMultiChoiceParameter(PARAM_COMPRESSION_LEVEL); if (mp != null) { String levelStr = mp.getStringValue(); if (levelStr.equals(COMPRESSION_OPTIONS[0])) { compressionLevel = Deflater.DEFAULT_COMPRESSION; } else if (levelStr.equals(COMPRESSION_OPTIONS[1])) { compressionLevel = Deflater.NO_COMPRESSION; } else if (levelStr.equals(COMPRESSION_OPTIONS[2])) { compressionLevel = Deflater.BEST_SPEED; } else if (levelStr.equals(COMPRESSION_OPTIONS[3])) { compressionLevel = Deflater.BEST_COMPRESSION; } else { compressionLevel = Deflater.DEFAULT_COMPRESSION; } } } /** * 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); // Actually generate the report and send it to the user. HttpServletResponse response = requestInfo.getResponse(); response.setContentType("application/zip"); response.addHeader("Content-Disposition", "filename=\"slamd_data_report.zip\""); try { ZipOutputStream zipStream = new ZipOutputStream(response.getOutputStream()); zipStream.setLevel(compressionLevel); createIndexPage(requestInfo, zipStream); for (int i=0; i < reportJobs.length; i++) { try { createJobPage(requestInfo, reportJobs[i], zipStream); } catch (Exception e) { e.printStackTrace(); } } for (int i=0; i < reportOptimizingJobs.length; i++) { try { createOptimizingJobPage(requestInfo, reportOptimizingJobs[i], zipStream); } catch (Exception e) { e.printStackTrace(); } } zipStream.flush(); zipStream.close(); } catch (IOException ioe) { // Not much we can do about this. System.err.println("Unable to generate report: " + ioe); } } /** * Writes the index page into the provided zip output stream. * * @param requestInfo State information about the request being processed. * @param zipStream The zip output stream to which the data should be * written. * * @throws IOException If a problem occurs while writing to the zip output * stream. */ private void createIndexPage(RequestInfo requestInfo, ZipOutputStream zipStream) throws IOException { StringBuilder buffer = new StringBuilder(); writePageHeader(requestInfo, buffer, true); buffer.append("<SPAN CLASS=\"" + Constants.STYLE_MAIN_HEADER + "\">SLAMD Generated Report</SPAN>" + EOL); buffer.append("<BR><BR>" + EOL); buffer.append("<TABLE BORDER=\"0\">" + EOL); buffer.append(" <TR>" + EOL); buffer.append(" <TD>Generation Date</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + dateFormat.format(new Date()) + "</TD>" + EOL); buffer.append(" </TR>" + EOL); HttpServletRequest request = requestInfo.getRequest(); String serverURL = request.getScheme() + "://" + request.getServerName() + ':' + request.getServerPort() + requestInfo.getServletBaseURI(); buffer.append(" <TR>" + EOL); buffer.append(" <TD>SLAMD Server URL</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD><A HREF=\"" + serverURL + "\">" + serverURL + "</A></TD>" + EOL); buffer.append(" </TR>" + EOL); buffer.append("</TABLE>" + EOL); buffer.append(EOL); if (reportJobs.length > 0) { buffer.append("<BR><BR>" + EOL); buffer.append("<SPAN CLASS=\"" + Constants.STYLE_MAIN_HEADER + "\">Job Data</SPAN>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); buffer.append(" <TR>" + EOL); buffer.append(" <TD><B>Job ID</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD><B>Description</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD><B>Job Type</TD>" + EOL); buffer.append(" </TR>" + EOL); for (int i=0; i < reportJobs.length; i++) { Job job = reportJobs[i]; if ((i % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } String id = job.getJobID(); buffer.append(" <TD><A HREF=\"jobs/job_" + id + ".html\">" + id + "</A></TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); String description = job.getJobDescription(); if ((description == null) || (description.length() == 0)) { description = " "; } buffer.append(" <TD>" + description + "</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + job.getJobClass().getJobName() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); } buffer.append("</TABLE>" + EOL); buffer.append(EOL); } if (reportOptimizingJobs.length > 0) { buffer.append("<BR><BR>" + EOL); buffer.append("<SPAN CLASS=\"" + Constants.STYLE_MAIN_HEADER + "\">Optimizing Job Data</SPAN>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); buffer.append(" <TR>" + EOL); buffer.append(" <TD><B>Optimizing Job ID</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD><B>Description</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD><B>Job Type</TD>" + EOL); buffer.append(" </TR>" + EOL); for (int i=0; i < reportOptimizingJobs.length; i++) { OptimizingJob job = reportOptimizingJobs[i]; if ((i % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } String id = job.getOptimizingJobID(); buffer.append(" <TD><A HREF=\"jobs/optimizing_job_" + id + ".html\">" + id + "</A></TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); String description = job.getDescription(); if ((description == null) || (description.length() == 0)) { description = " "; } buffer.append(" <TD>" + description + "</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + job.getJobClass().getJobName() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); } buffer.append("</TABLE>" + EOL); buffer.append(EOL); } writePageFooter(requestInfo, buffer); zipStream.putNextEntry(new ZipEntry("index.html")); zipStream.write(buffer.toString().getBytes()); zipStream.closeEntry(); } /** * Writes a page for the given job to the provided zip output stream. * * @param requestInfo State information about the request being processed. * @param job The job to be written to the zip output stream. * @param zipStream The zip output stream to which the job data should be * written. * * @throws IOException If a problem occurs while writing to the zip output * stream. */ private void createJobPage(RequestInfo requestInfo, Job job, ZipOutputStream zipStream) throws IOException { StringBuilder buffer = new StringBuilder(); writePageHeader(requestInfo, buffer, false); buffer.append("<SPAN CLASS=\"" + Constants.STYLE_MAIN_HEADER + "\">Information for Job " + job.getJobID() + "</SPAN>" + EOL); buffer.append("<BR><BR>" + EOL); int i=0; buffer.append("<B>General Information</B>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Job ID</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + job.getJobID() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); String optimizingJobID = job.getOptimizingJobID(); if ((optimizingJobID != null) && (optimizingJobID.length() > 0)) { // See if we have information about the optimizing job for this job. boolean generateLink = false; for (int j=0; j < reportOptimizingJobs.length; j++) { if (optimizingJobID.equalsIgnoreCase( reportOptimizingJobs[j].getOptimizingJobID())) { generateLink = true; break; } } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Optimizing Job ID</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); if (generateLink) { buffer.append(" <TD><A HREF=\"optimizing_job_" + optimizingJobID + ".html\">" + optimizingJobID + "</A></TD>" + EOL); } else { buffer.append(" <TD>" + optimizingJobID + "</TD>" + EOL); } buffer.append(" </TR>" + EOL); } String description = job.getJobDescription(); if ((description == null) || (description.length() == 0)) { description = "(Not Specified)"; } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Job Description</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + description + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Job Type</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + job.getJobClass().getJobName() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Job Class</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + job.getJobClassName() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Job State</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + Constants.jobStateToString(job.getJobState()) + "</TD>" + EOL); buffer.append(" </TR>" + EOL); buffer.append("</TABLE>" + EOL); // Write information to the buffer about the schedule configuration. if (includeScheduleConfig) { i=0; buffer.append("<BR>" + EOL); buffer.append("<B>Schedule Information</B>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); Date startTime = job.getStartTime(); String startTimeStr; if (startTime == null) { startTimeStr = "(Not Available)"; } else { startTimeStr = dateFormat.format(startTime); } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Scheduled Start Time</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + startTimeStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); Date stopTime = job.getStopTime(); String stopTimeStr; if (stopTime == null) { stopTimeStr = "(Not Specified)"; } else { stopTimeStr = dateFormat.format(stopTime); } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Scheduled Stop Time</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + stopTimeStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); int duration = job.getDuration(); String durationStr; if (duration > 0) { durationStr = duration + " seconds"; } else { durationStr = "(Not Specified)"; } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Scheduled Duration</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + durationStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Number of Clients</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + job.getNumberOfClients() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); String[] requestedClients = job.getRequestedClients(); if ((requestedClients != null) && (requestedClients.length > 0)) { if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Requested Clients</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + EOL); buffer.append(" " + requestedClients[0]); for (int j=1; j < requestedClients.length; j++) { buffer.append("<BR>" + EOL); buffer.append(" " + requestedClients[j]); } buffer.append(EOL); buffer.append(" </TD>" + EOL); buffer.append(" </TR>" + EOL); } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Threads Per Client</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + job.getThreadsPerClient() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Thread Startup Delay</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + job.getThreadStartupDelay() + " milliseconds</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Statistics Collection Interval</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + job.getCollectionInterval() + " seconds</TD>" + EOL); buffer.append(" </TR>" + EOL); buffer.append("</TABLE>" + EOL); } // Write information to the buffer about the job-specific configuration. if (includeJobConfig) { i=0; buffer.append("<BR>" + EOL); buffer.append("<B>Parameter Information</B>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); Parameter[] params = job.getParameterList().getParameters(); for (int j=0; j < params.length; j++) { if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>" + params[j].getDisplayName() + "</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + params[j].getHTMLDisplayValue() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); } buffer.append("</TABLE>" + EOL); } // Write information to the buffer about the job statistics. LinkedHashMap<String,BufferedImage> graphMap = new LinkedHashMap<String,BufferedImage>(); if (includeStats && job.hasStats()) { i=0; buffer.append("<BR>" + EOL); buffer.append("<B>General Execution Data</B>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); Date actualStartTime = job.getActualStartTime(); String startTimeStr; if (actualStartTime == null) { startTimeStr = "(Not Available)"; } else { startTimeStr = dateFormat.format(actualStartTime); } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Actual Start Time</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + startTimeStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); Date actualStopTime = job.getActualStopTime(); String stopTimeStr; if (actualStopTime == null) { stopTimeStr = "(Not Available)"; } else { stopTimeStr = dateFormat.format(actualStopTime); } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Actual Stop Time</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + stopTimeStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); int actualDuration = job.getActualDuration(); String durationStr; if (actualDuration > 0) { durationStr = actualDuration + " seconds"; } else { durationStr = "(Not Available)"; } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Actual Duration</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + durationStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); String[] clientsUsed = job.getStatTrackerClientIDs(); if ((clientsUsed != null) && (clientsUsed.length > 0)) { if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Clients Used</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + EOL); buffer.append(" " + clientsUsed[0]); for (int j=1; j < clientsUsed.length; j++) { buffer.append("<BR>" + EOL); buffer.append(" " + clientsUsed[j]); } buffer.append(EOL); buffer.append(" </TD>" + EOL); } buffer.append("</TABLE>" + EOL); String[] statNames = job.getStatTrackerNames(); for (int j=0; j < statNames.length; j++) { StatTracker[] trackers = job.getStatTrackers(statNames[j]); if ((trackers != null) && (trackers.length > 0)) { StatTracker tracker = trackers[0].newInstance(); tracker.aggregate(trackers); buffer.append("<BR><BR>" + EOL); buffer.append("<B>" + tracker.getDisplayName() + "</B>" + EOL); buffer.append("<BR>" + EOL); if (includeDetailedStats) { buffer.append(tracker.getDetailHTML()); } else { buffer.append(tracker.getSummaryHTML()); } if (includeGraphs) { String filename = "images/job_" + job.getJobID() + "_graph_" + j + ".png"; buffer.append("<IMG SRC=\"../" + filename + "\" ALT=\"Graph of Results for " + statNames[j] + "\">" + EOL); buffer.append("<BR>" + EOL); ParameterList params = tracker.getGraphParameterStubs(job); BufferedImage image = tracker.createGraph(job, Constants.DEFAULT_GRAPH_WIDTH, Constants.DEFAULT_GRAPH_HEIGHT, params); graphMap.put(filename, image); } } } } // Write information about the resource monitor statistics. if (includeMonitorStats && job.hasResourceStats()) { String[] trackerNames = job.getResourceStatTrackerNames(); for (int j=0; ((trackerNames != null) && (j < trackerNames.length)); j++) { StatTracker[] trackers = job.getResourceStatTrackers(trackerNames[j]); if ((trackers == null) || (trackers.length == 0)) { continue; } StatTracker tracker = trackers[0].newInstance(); tracker.aggregate(trackers); buffer.append("<BR><BR>" + EOL); buffer.append("<B>" + tracker.getDisplayName() + "</B>" + EOL); buffer.append("<BR>" + EOL); if (includeDetailedStats) { buffer.append(tracker.getDetailHTML()); } else { buffer.append(tracker.getSummaryHTML()); } if (includeGraphs) { String filename = "images/job_" + job.getJobID() + "_monitor_graph_" + j + ".png"; buffer.append("<IMG SRC=\"../" + filename + "\" ALT=\"Graph of Results for " + tracker.getDisplayName() + "\">" + EOL); buffer.append("<BR>" + EOL); ParameterList params = tracker.getMonitorGraphParameterStubs(job); BufferedImage image = tracker.createMonitorGraph(job, Constants.DEFAULT_GRAPH_WIDTH, Constants.DEFAULT_MONITOR_GRAPH_HEIGHT, params); graphMap.put(filename, image); } } } writePageFooter(requestInfo, buffer); String filename = "jobs/job_" + job.getJobID() + ".html"; zipStream.putNextEntry(new ZipEntry(filename)); zipStream.write(buffer.toString().getBytes()); zipStream.closeEntry(); Iterator iterator = graphMap.keySet().iterator(); while (iterator.hasNext()) { String imageName = (String) iterator.next(); BufferedImage graph = graphMap.get(imageName); zipStream.putNextEntry(new ZipEntry(imageName)); try { zipStream.write(imageToByteArray(graph)); } catch (IOException ioe) {} zipStream.closeEntry(); } } /** * Writes a page for the given optimizing job to the provided zip output * stream. * * @param requestInfo State information about the request being processed. * @param optimizingJob The optimizing job to be written to the zip output * stream. * @param zipStream The zip output stream to which the job data should * be written. * * @throws IOException If a problem occurs while writing to the zip output * stream. */ private void createOptimizingJobPage(RequestInfo requestInfo, OptimizingJob optimizingJob, ZipOutputStream zipStream) throws IOException { // Get the optimization algorithm and set of parameters. OptimizationAlgorithm optimizationAlgorithm = optimizingJob.getOptimizationAlgorithm(); ParameterList paramList = optimizationAlgorithm.getOptimizationAlgorithmParameters(); Parameter[] optimizationParams = paramList.getParameters(); StringBuilder buffer = new StringBuilder(); writePageHeader(requestInfo, buffer, false); buffer.append("<SPAN CLASS=\"" + Constants.STYLE_MAIN_HEADER + "\">Information for Optimizing Job " + optimizingJob.getOptimizingJobID() + "</SPAN>" + EOL); buffer.append("<BR><BR>" + EOL); int i=0; buffer.append("<B>General Information</B>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Optimizing Job ID</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + optimizingJob.getOptimizingJobID() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Job Type</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + optimizingJob.getJobClass().getJobName() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); String description = optimizingJob.getDescription(); if ((description == null) || (description.length() == 0)) { description = "(Not Specified)"; } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Base Description</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + description + "</TD>" + EOL); buffer.append(" </TR>" + EOL); boolean includeThreadCount = optimizingJob.includeThreadsInDescription(); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Include Thread Count in Description</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + String.valueOf(includeThreadCount) + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Optimizing Job State</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + Constants.jobStateToString(optimizingJob.getJobState()) + "</TD>" + EOL); buffer.append(" </TR>" + EOL); String stopReason = optimizingJob.getStopReason(); if ((stopReason == null) || (stopReason.length() == 0)) { stopReason = "(Not Available)"; } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Stop Reason</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + stopReason + "</TD>" + EOL); buffer.append(" </TR>" + EOL); buffer.append("</TABLE>" + EOL); // Write the schedule configuration to the report. if (includeScheduleConfig) { i=0; buffer.append("<BR>" + EOL); buffer.append("<B>Schedule Information</B>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); Date startTime = optimizingJob.getStartTime(); String startTimeStr; if (startTime == null) { startTimeStr = "(Not Available)"; } else { startTimeStr = dateFormat.format(startTime); } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Scheduled Start Time</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + startTimeStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); int duration = optimizingJob.getDuration(); String durationStr; if (duration > 0) { durationStr = duration + " seconds"; } else { durationStr = "(Not Specified)"; } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Job Duration</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + durationStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Delay Between Iterations</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + optimizingJob.getDelayBetweenIterations() + " seconds</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Number of Clients</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + optimizingJob.getNumClients() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); String[] requestedClients = optimizingJob.getRequestedClients(); if ((requestedClients != null) && (requestedClients.length > 0)) { if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Requested Clients</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + EOL); buffer.append(" " + requestedClients[0]); for (int j=1; j < requestedClients.length; j++) { buffer.append("<BR>" + EOL); buffer.append(" " + requestedClients[j]); } buffer.append(EOL); buffer.append(" </TD>" + EOL); buffer.append(" </TR>" + EOL); } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Minimum Number of Threads</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + optimizingJob.getMinThreads() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); int maxThreads = optimizingJob.getMaxThreads(); String maxThreadStr; if (maxThreads > 0) { maxThreadStr = String.valueOf(maxThreads); } else { maxThreadStr = "(Not Specified)"; } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Maximum Number of Threads</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + maxThreadStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Thread Increment Between Iterations</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + optimizingJob.getThreadIncrement() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Statistics Collection Interval</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + optimizingJob.getCollectionInterval() + " seconds</TD>" + EOL); buffer.append(" </TR>" + EOL); buffer.append("</TABLE>" + EOL); // Write the optimization settings to the report. i=0; buffer.append("<BR>" + EOL); buffer.append("<B>Optimization Settings</B>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); for (int j=0; j < optimizationParams.length; j++) { if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>" + optimizationParams[j].getDisplayName() + "</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + optimizationParams[j].getHTMLDisplayValue() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Maximum Consecutive Non-Improving " + "Iterations</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + optimizingJob.getMaxNonImproving() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Re-Run Best Iteration</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + String.valueOf(optimizingJob.reRunBestIteration()) + "</TD>" + EOL); buffer.append(" </TR>" + EOL); int reRunDuration = optimizingJob.getReRunDuration(); if (reRunDuration > 0) { durationStr = reRunDuration + " seconds"; } else { durationStr = "(Not Specified)"; } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Re-Run Duration</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + durationStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); buffer.append("</TABLE>" + EOL); } // Write the job-specific configuration to the report. if (includeJobConfig) { i=0; buffer.append("<BR>" + EOL); buffer.append("<B>Parameter Information</B>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); Parameter[] params = optimizingJob.getParameters().getParameters(); for (int j=0; j < params.length; j++) { if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>" + params[j].getDisplayName() + "</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + params[j].getHTMLDisplayValue() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); } buffer.append("</TABLE>" + EOL); } // Write the statistical information to the report. LinkedHashMap<String,BufferedImage> graphMap = new LinkedHashMap<String,BufferedImage>(); if (includeStats && optimizingJob.hasStats()) { i=0; buffer.append("<BR>" + EOL); buffer.append("<B>Execution Data</B>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); Date actualStartTime = optimizingJob.getActualStartTime(); String startTimeStr; if (actualStartTime == null) { startTimeStr = "(Not Available)"; } else { startTimeStr = dateFormat.format(actualStartTime); } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Actual Start Time</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + startTimeStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); Date actualStopTime = optimizingJob.getActualStopTime(); String stopTimeStr; if (actualStopTime == null) { stopTimeStr = "(Not Available)"; } else { stopTimeStr = dateFormat.format(actualStopTime); } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Actual Stop Time</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + stopTimeStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); Job[] iterations = optimizingJob.getAssociatedJobs(); if ((iterations != null) && (iterations.length > 0)) { if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Job Iterations Completed</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + iterations.length + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Optimal Thread Count</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + optimizingJob.getOptimalThreadCount() + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Optimal Value</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + decimalFormat.format(optimizingJob.getOptimalValue()) + "</TD>" + EOL); buffer.append(" </TR>" + EOL); String optimalID = optimizingJob.getOptimalJobID(); if ((optimalID == null) || (optimalID.length() == 0)) { optimalID = "(Not Available)"; } else if (includeOptimizingIterations) { optimalID = "<A HREF=\"job_" + optimalID + ".html\">" + optimalID + "</A>"; } if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Optimal Job Iteration</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + optimalID + "</TD>" + EOL); buffer.append(" </TR>" + EOL); Job reRunIteration = optimizingJob.getReRunIteration(); if (reRunIteration != null) { if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } buffer.append(" <TD>Re-Run Iteration</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); if (includeOptimizingIterations) { buffer.append(" <TD><A HREF=\"job_" + reRunIteration.getJobID() + ".html\">" + reRunIteration.getJobID() + "</A></TD>" + EOL); } else { buffer.append(" <TD>" + reRunIteration.getJobID() + "</TD>" + EOL); } buffer.append(" </TR>" + EOL); String valueStr; try { double iterationValue = optimizationAlgorithm.getIterationOptimizationValue( reRunIteration); valueStr = decimalFormat.format(iterationValue); } catch (Exception e) { valueStr = "N/A"; } buffer.append(" <TD>Re-Run Iteration Value</TD>" + EOL); buffer.append(" <TD> </TD>" + EOL); buffer.append(" <TD>" + valueStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if (includeOptimizingIterations) { createJobPage(requestInfo, reRunIteration, zipStream); } } } buffer.append("</TABLE>" + EOL); if ((iterations != null) && (iterations.length > 0)) { i=0; buffer.append("<BR>" + EOL); buffer.append("<B>Job Iterations</B>" + EOL); buffer.append("<TABLE BORDER=\"0\" CELLSPACING=\"0\">" + EOL); for (int j=0; j < iterations.length; j++) { if ((i++ % 2) == 0) { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_A + "\">" + EOL); } else { buffer.append(" <TR CLASS=\"" + Constants.STYLE_JOB_SUMMARY_LINE_B + "\">" + EOL); } if (includeOptimizingIterations) { buffer.append(" <TD><A HREF=\"job_" + iterations[j].getJobID() + ".html\">" + iterations[j].getJobID() + "</A></TD>" + EOL); } else { buffer.append(" <TD>" + iterations[j].getJobID() + "</TD>" + EOL); } buffer.append(" <TD> </TD>" + EOL); String valueStr; try { double value = optimizationAlgorithm.getIterationOptimizationValue( iterations[j]); valueStr = decimalFormat.format(value); } catch (Exception e) { valueStr = "N/A"; } buffer.append(" <TD>" + valueStr + "</TD>" + EOL); buffer.append(" </TR>" + EOL); if (includeOptimizingIterations) { createJobPage(requestInfo, iterations[j], zipStream); } } buffer.append("</TABLE>" + EOL); if (includeGraphs) { String[] statNames = iterations[0].getStatTrackerNames(); for (int j=0; j < statNames.length; j++) { buffer.append("<BR><BR>" + EOL); StatTracker[] trackers = iterations[0].getStatTrackers(statNames[j]); if ((trackers != null) && (trackers.length > 0)) { StatTracker tracker = trackers[0].newInstance(); tracker.aggregate(trackers); String filename = "images/optimizing_job_" + optimizingJob.getOptimizingJobID() + "_graph_" + j + ".png"; buffer.append("<IMG SRC=\"../" + filename + "\" ALT=\"Comparison of Results for " + tracker.getDisplayName() + "\">" + EOL); ParameterList params = tracker.getGraphParameterStubs(iterations); BufferedImage image = tracker.createGraph(iterations, Constants.DEFAULT_GRAPH_WIDTH, Constants.DEFAULT_GRAPH_HEIGHT, params); graphMap.put(filename, image); } } } } } writePageFooter(requestInfo, buffer); String filename = "jobs/optimizing_job_" + optimizingJob.getOptimizingJobID() + ".html"; zipStream.putNextEntry(new ZipEntry(filename)); zipStream.write(buffer.toString().getBytes()); zipStream.closeEntry(); Iterator<String> iterator = graphMap.keySet().iterator(); while (iterator.hasNext()) { String imageName = iterator.next(); BufferedImage graph = graphMap.get(imageName); zipStream.putNextEntry(new ZipEntry(imageName)); try { zipStream.write(imageToByteArray(graph)); } catch (IOException ioe) {} zipStream.closeEntry(); } } /** * Writes the standard HTML page header into the provided buffer. * * @param requestInfo State information about the request being processed. * @param buffer The buffer into which the header should be written. * @param indexPage Indicates whether the header is being written for the * index page or some other page. */ private void writePageHeader(RequestInfo requestInfo, StringBuilder buffer, boolean indexPage) { buffer.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 " + "Transitional//EN\">" + EOL); buffer.append(EOL); buffer.append("<HTML>" + EOL); buffer.append(" <HEAD>" + EOL); buffer.append(" <TITLE>SLAMD Generated Report</TITLE>" + EOL); buffer.append(" <META HTTP-EQUIV=\"Content-Type\" " + "CONTENT=\"text/html; charset=utf-8\">" + EOL); buffer.append(Constants.STYLE_SHEET_DATA); buffer.append(" </HEAD>" + EOL); buffer.append(EOL); buffer.append(" <BODY>" + EOL); buffer.append(" <TABLE WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"10\">" + EOL); buffer.append(" <TR>" + EOL); buffer.append(" <TD CLASS=\"blue_background\" ALIGN=\"LEFT\" " + "WIDTH=\"33%\"> <BR> </TD>" + EOL); buffer.append(" <TD CLASS=\"yellow_background\" ALIGN=\"CENTER\" " + "WIDTH=\"34%\">" + EOL); buffer.append(" SLAMD Generated Report" + EOL); buffer.append(" </TD>" + EOL); buffer.append(" <TD CLASS=\"red_background\" ALIGN=\"RIGHT\" " + "WIDTH=\"33%\">" + EOL); buffer.append(" Version " + DynamicConstants.SLAMD_VERSION + EOL); if (! DynamicConstants.OFFICIAL_BUILD) { buffer.append(" <BR>"); buffer.append(" <FONT SIZE=\"-2\">Unofficial Build ID " + DynamicConstants.BUILD_DATE + "</FONT>" + EOL); } buffer.append(" </TD>" + EOL); buffer.append(" </TR>" + EOL); buffer.append(" </TABLE>" + EOL); if (indexPage) { buffer.append(" <BR>" + EOL); } else { buffer.append(" <A HREF=\"../index.html\">Index Page</A>" + EOL); buffer.append(" <BR><BR>" + EOL); } buffer.append(EOL); } /** * Writes the standard HTML page footer into the provided buffer. * * @param requestInfo State information about the request being processed. * @param buffer The buffer into which the footer should be written. */ private void writePageFooter(RequestInfo requestInfo, StringBuilder buffer) { buffer.append(EOL); buffer.append(" </BODY>" + EOL); buffer.append("</HTML" + EOL); } /** * 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 converting the image to a byte * 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(); } }