/* * 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.stat; import java.awt.image.BufferedImage; import java.text.DecimalFormat; import java.util.ArrayList; import com.slamd.asn1.ASN1Element; import com.slamd.asn1.ASN1Integer; import com.slamd.asn1.ASN1Sequence; import com.slamd.client.Client; import com.slamd.common.Constants; import com.slamd.common.SLAMDException; import com.slamd.job.Job; import com.slamd.parameter.BooleanParameter; import com.slamd.parameter.MultiChoiceParameter; import com.slamd.parameter.Parameter; import com.slamd.parameter.ParameterList; /** * This class defines a simple stat tracker that can be used to count the number * of times a given event occurs. Unlike the incremental tracker, however, the * count is not reset at the beginning of each collection interval. * * * @author Neil A. Wilson */ public class AccumulatingTracker implements StatTracker { // The list that contains the data collected by this tracker, broken up into // intervals. private ArrayList<Integer> totalList; // Indicates whether to enable real-time statistics reporting. private boolean enableRealTimeStats; // Indicates whether this stat tracker has been started. private boolean hasBeenStarted; // Indicates whether this stat tracker is currently running. private boolean isRunning; // The formatter used to round off decimal values. private DecimalFormat decimalFormat; // The current interval number when reporting real-time stats. private int intervalNum; // The total number of occurrences so far since the call to startTracker. private int totalCount; // The collection interval in seconds. private int collectionInterval; // The length of time in seconds that this tracker was collecting statistics. private int duration; // The time that the current collection interval should stop. private long intervalStopTime; // The time that startTracker was called. private long startTime; // The time that stopTracker was called. private long stopTime; // The stat reporter used to report real-time stats. private RealTimeStatReporter statReporter; // The client ID of the client that used this stat tracker. private String clientID; // The display name to use for this stat tracker. private String displayName; // The thread ID of the client thread that used this stat tracker. private String threadID; /** * Creates a new accumulating tracker intended for use as a placeholder for * decoding purposes. This version of the constructor should not be used * by job classes. */ public AccumulatingTracker() { this.clientID = ""; this.threadID = ""; this.displayName = ""; this.collectionInterval = Constants.DEFAULT_COLLECTION_INTERVAL; decimalFormat = new DecimalFormat("0.000"); totalCount = 0; intervalStopTime = 0; startTime = System.currentTimeMillis(); stopTime = 0; duration = 0; totalList = new ArrayList<Integer>(); enableRealTimeStats = false; statReporter = null; intervalNum = 0; hasBeenStarted = false; isRunning = false; } /** * Creates a new accumulating tracker with the specified information. * * @param clientID The client ID of the client that used this * stat tracker. * @param threadID The thread ID of the thread that used this * stat tracker. * @param displayName The display name to use for this stat tracker. * @param collectionInterval The collection interval in seconds that * should be used for this stat tracker. */ public AccumulatingTracker(String clientID, String threadID, String displayName, int collectionInterval) { this.clientID = clientID; this.threadID = threadID; this.displayName = displayName; this.collectionInterval = collectionInterval; if (collectionInterval <= 0) { this.collectionInterval = Constants.DEFAULT_COLLECTION_INTERVAL; } decimalFormat = new DecimalFormat("0.000"); totalCount = 0; intervalStopTime = 0; startTime = System.currentTimeMillis(); stopTime = 0; duration = 0; totalList = new ArrayList<Integer>(); enableRealTimeStats = false; statReporter = null; intervalNum = 0; } /** * Creates a new instance of this stat tracker. The new instance should have * the same type, display name, client ID, thread ID, and collection interval * as the stat tracker used to create it. * * @return The new instance of this stat tracker. */ public StatTracker newInstance() { return new AccumulatingTracker(clientID, threadID, displayName, collectionInterval); } /** * Increments the counter to indicated that the event we are tracking has * occurred. */ public void increment() { long now = System.currentTimeMillis(); // We're in the same interval as the last time the counter was incremented. // Just increment it again. if (now < intervalStopTime) { totalCount++; } // The previous interval has stopped and a new one has started. Close out // the old one and start the new. Note that if this is an event that // happens infrequently, then multiple intervals could have passed, so make // sure that the appropriate number of intervals are added. else { totalList.add(totalCount); intervalStopTime += (1000 * collectionInterval); if (enableRealTimeStats) { statReporter.reportStatToAdd(this, intervalNum++, totalCount); } while (intervalStopTime < now) { totalList.add(totalCount); if (enableRealTimeStats) { statReporter.reportStatToAdd(this, intervalNum++, totalCount); } intervalStopTime += (1000 * collectionInterval); } totalCount++; } } /** * Reverts the last increment performed using this tracker. Note that this * method should not be used multiple times between calls of the * <CODE>increment()</CODE> method. */ public void undoLastIncrement() { if (totalCount > 0) { totalCount--; } } /** * Indicates that the stat tracker is to start maintaining statistics and that * it should start its internal timer. */ public void startTracker() { // If the tracker has already been started, then print an error message. // Otherwise, indicate that it is started and running. if (hasBeenStarted) { System.err.println("***** WARNING: Multiple calls to start " + "accumulating stat tracker " + displayName); } else { hasBeenStarted = true; isRunning = true; } // Just in case, reset all the counter info. totalCount = 0; totalList = new ArrayList<Integer>(); intervalNum = 0; // Register this tracker with the persistence thread. Client.registerPersistentStatistic(this); // Set the start time and the interval stop time. long now = System.currentTimeMillis(); startTime = now; intervalStopTime = now + (1000 * collectionInterval); } /** * Indicates that the stat tracker that there will not be any more statistics * collection done and that it should stop its internal timer. */ public void stopTracker() { long now = System.currentTimeMillis(); // If the tracker was never started, then print an error message. // Otherwise, indicate that it is no longer running. if (! hasBeenStarted) { System.err.println("***** WARNING: Attempting to stop accumulating " + "stat tracker " + displayName + " without having started it"); } else { isRunning = false; } // If the previous interval had passed since the last update, make sure // that we add the appropriate number of empty intervals. if (intervalStopTime < now) { totalList.add(totalCount); if (enableRealTimeStats) { statReporter.reportStatToAdd(this, intervalNum++, totalCount); } intervalStopTime += (1000 * collectionInterval); } while (intervalStopTime < now) { totalList.add(totalCount); if (enableRealTimeStats) { statReporter.reportStatToAdd(this, intervalNum++, totalCount); } intervalStopTime += (1000 * collectionInterval); } // Update the stop time to be the time that the last complete interval // ended and calculate the duration. stopTime = intervalStopTime - (1000 * collectionInterval); duration = (int) ((stopTime - startTime) / 1000); if (enableRealTimeStats) { statReporter.doneReporting(this, intervalNum++); } } /** * Indicates whether this stat tracker has been started, regardless of whether * it is currently running. * * @return <CODE>true</CODE> if this stat tracker has been started, or * <CODE>false</CODE> if it has not yet been started. */ public boolean hasBeenStarted() { return hasBeenStarted; } /** * Indicates whether this stat tracker is currently running. * * @return <CODE>true</CODE> if this stat tracker is currently running, or * <CODE>false</CODE> if not. */ public boolean isRunning() { return isRunning; } /** * Indicates that the stat tracker should enable real-time statistics * collection. Note that some stat trackers may not support real-time * statistics collection, in which case this method may be ignored. * * @param statReporter The stat-reporter that should be used to report * real-time statistics to the SLAMD server. * @param jobID The job ID of the job that will be reporting the * data. */ public void enableRealTimeStats(RealTimeStatReporter statReporter, String jobID) { if (statReporter != null) { enableRealTimeStats = true; this.statReporter = statReporter; statReporter.registerStat(jobID, this); } } /** * Retrieves the total number of times that the tracked event occurred over * the total duration. * * @return The total number of times that the tracked event occurred over the * total duration. */ public int getTotalCount() { return totalCount; } /** * Retrieves an array containing the accumulated totals by interval. * * @return An array containing the accumulated totals by interval. */ public int[] getTotalsByInterval() { int[] intValues = new int[totalList.size()]; for (int i=0; i < intValues.length; i++) { intValues[i] = totalList.get(i); } return intValues; } /** * Specifies the data for this stat tracker in the form of an array containing * the accumulated totals by interval. * * @param intervalTotals An array containing the accumulated totals by * interval. */ public void setTotalsByInterval(int[] intervalTotals) { totalCount = intervalTotals[intervalTotals.length-1]; totalList = new ArrayList<Integer>(intervalTotals.length); for (int i=0; i < intervalTotals.length; i++) { totalList.add(i); } duration = intervalTotals.length * collectionInterval; } /** * Retrieves the average number of times the tracked event occurred in a * single interval. * * @return The average number of times the tracked event occurred in a single * interval. */ public double getAverageCountPerInterval() { if (! totalList.isEmpty()) { return (1.0 * totalCount / totalList.size()); } else { return 0.0; } } /** * Retrieves the average number of times the tracked event occurred in a * single second. * * @return The average number of times the tracked event occurred in a single * second. */ public double getAverageCountPerSecond() { if (duration > 0) { return (1.0 * totalCount / duration); } else { return 0; } } /** * Retrieves the client ID of the client that used this stat tracker. * * @return The client ID of the client that used this stat tracker. */ public String getClientID() { return clientID; } /** * Specifies the client ID of the client that used this stat tracker. Note * that this should only be used when creating a new stat tracker based on * encoded data and not when using it to collect statistics. * * @param clientID The client ID of the client that used this stat tracker. */ public void setClientID(String clientID) { this.clientID = clientID; } /** * Retrieves the thread ID of the client thread that used this stat tracker. * * @return The thread ID of the client thread that used this stat tracker. */ public String getThreadID() { return threadID; } /** * Specifies the thread ID of the client thread that used this stat tracker. * Note that this should only be used when creating a new stat tracker based * on encoded data and not when using it to collect statistics. * * @param threadID The thread ID of the client thread that used this stat * tracker. */ public void setThreadID(String threadID) { this.threadID = threadID; } /** * Retrieves the user-friendly name associated with this stat tracker. * * @return The user-friendly name associated with this stat tracker. */ public String getDisplayName() { return displayName; } /** * Specifies the display name for this stat tracker. Note that this should * only be used when creating a new stat tracker based on encoded data and not * when using it to collect statistics. * * @param displayName The display name for this stat tracker. */ public void setDisplayName(String displayName) { this.displayName = displayName; } /** * Retrieves the collection interval (in seconds) that will be used for this * stat tracker. * * @return The collection interval (in seconds) that will be used for this * stat tracker. */ public int getCollectionInterval() { return collectionInterval; } /** * Specifies the collection interval in seconds to use fort this stat tracker. * This should not be used while the stat tracker is actively collecting * statistics. * * @param collectionInterval The collection interval in seconds to use for * this stat tracker. */ public void setCollectionInterval(int collectionInterval) { if (collectionInterval > 0) { this.collectionInterval = collectionInterval; } else { this.collectionInterval = Constants.DEFAULT_COLLECTION_INTERVAL; } } /** * Retrieves the total length of time in seconds that this stat tracker was * capturing statistics. * * @return The total length of time in seconds that this stat tracker was * */ public int getDuration() { return duration; } /** * Specifies the duration for this stat tracker. Note that this should only * be used when creating a new stat tracker based on encoded data and not when * using it to collect statistics. * * @param duration The duration for this stat tracker. */ public void setDuration(int duration) { this.duration = duration; } /** * Indicates whether the user may search for jobs with statistics collected by * this stat tracker. The search will be "greater than" and "less than" some * user-specified value. * * @return <CODE>true</CODE> if statistics collected by this stat tracker * should be searchable, or <CODE>false</CODE> if not. */ public boolean isSearchable() { return true; } /** * Indicates whether the value associated with this stat tracker is greater * than or equal to the provided value. This is only applicable if * <CODE>isSearchable</CODE> returns <CODE>true</CODE>, and what exactly * "the value of this stat tracker" means will be left up to those stat * trackers that are searchable. * * @param value The value against which the value of this stat tracker is to * be compared. * * @return <CODE>true</CODE> if the value of this stat tracker is greater * than or equal to the provided value, or <CODE>false</CODE> if not. */ public boolean isAtLeast(double value) { return (totalCount >= value); } /** * Indicates whether the value associated with this stat tracker is less than * or equal to the provided value. This is only applicable if * <CODE>isSearchable</CODE> returns <CODE>true</CODE>, and what exactly * "the value of this stat tracker" means will be left up to those stat * trackers that are searchable. * * @param value The value against which the value of this stat tracker is to * be compared. * * @return <CODE>true</CODE> if the value of this stat tracker is less than * or equal to the provided value, or <CODE>false</CODE> if not. */ public boolean isAtMost(double value) { return (totalCount <= value); } /** * Retrieves the value associated with this stat tracker. This is only * applicable if <CODE>isSearchable</CODE> returns <CODE>true</CODE>, and what * exactly "the value associated with this stat tracker" means will be left up * to those stat trackers that are searchable. * * @return The value associated with this stat tracker. */ public double getSummaryValue() { return totalCount; } /** * Retrieves the number of intervals for which data is available for this stat * tracker. * * @return The number of intervals for which data is available for this stat * tracker. */ public int getNumIntervals() { return totalList.size(); } /** * Aggregates the information collected by the provided set of stat trackers * into a single tracker that represents the information gathered from the * entire set of data. All of the stat trackers in the provided array must be * of the same type and have the same collection interval as the instance into * which the information will be aggregated. * * @param trackers The set of stat trackers whose data is to be aggregated. */ public void aggregate(StatTracker[] trackers) { totalCount = 0; duration = Integer.MAX_VALUE; if (trackers.length > 0) { collectionInterval = trackers[0].getCollectionInterval(); } else { return; } int min = Integer.MAX_VALUE; int[][] totals = new int[trackers.length][]; for (int i=0; i < totals.length; i++) { totals[i] = ((AccumulatingTracker) trackers[i]).getTotalsByInterval(); if (totals[i].length < min) { min = totals[i].length; } if (trackers[i].getDuration() < duration) { duration = trackers[i].getDuration(); } } int[] aggregateCounts = new int[min]; for (int i=0; i < aggregateCounts.length; i++) { aggregateCounts[i] = 0; for (int j=0; j < totals.length; j++) { aggregateCounts[i] += totals[j][i]; } totalCount = aggregateCounts[i]; } totalList = new ArrayList<Integer>(aggregateCounts.length); for (int i=0; i < aggregateCounts.length; i++) { totalList.add(aggregateCounts[i]); } } /** * Retrieves brief one-line summary string with cumulative information about * this stat tracker. * * @return A brief one-line summary string containing cumulative information * about this stat tracker. */ public String getSummaryString() { return displayName + " -- Total Count: " + totalCount + "; Avg Count/Second: " + decimalFormat.format(getAverageCountPerSecond()) + "; Avg Count/Interval: " + decimalFormat.format(getAverageCountPerInterval()); } /** * Retrieves a detailed (potentially multi-line) string with verbose * information about the data collected by this stat tracker. * * @return A detailed string with verbose information about the data * collected by this stat tracker. */ public String getDetailString() { StringBuilder returnBuffer = new StringBuilder(); returnBuffer.append(displayName + Constants.EOL); returnBuffer.append("Total Count: " + totalCount + Constants.EOL); returnBuffer.append("Average Count/Second: " + decimalFormat.format(getAverageCountPerSecond()) + Constants.EOL); returnBuffer.append("Average Count/Interval: " + decimalFormat.format(getAverageCountPerInterval()) + Constants.EOL); for (int i=0; i < totalList.size(); i++) { returnBuffer.append("Interval "); returnBuffer.append(i+1); returnBuffer.append(": "); returnBuffer.append(totalList.get(i)); returnBuffer.append(Constants.EOL); } return returnBuffer.toString(); } /** * Retrieves a version of the summary information for this stat tracker * formatted for display in an HTML document. * * @return An HTML version of the summary data for this stat tracker. */ public String getSummaryHTML() { StringBuilder html = new StringBuilder(); html.append("<TABLE BORDER=\"1\">" + Constants.EOL); html.append(" <TR>" + Constants.EOL); html.append(" <TD><B>Total Count</B></TD>" + Constants.EOL); html.append(" <TD><B>Avg Count/Second</B></TD>" + Constants.EOL); html.append(" <TD><B>Avg Count/Interval</B></TD>" + Constants.EOL); html.append(" </TR>" + Constants.EOL); html.append(" <TR>" + Constants.EOL); html.append(" <TD>" + totalCount + "</TD>" + Constants.EOL); html.append(" <TD>" + decimalFormat.format(getAverageCountPerSecond()) + "</TD>" + Constants.EOL); html.append(" <TD>" + decimalFormat.format(getAverageCountPerInterval()) + "</TD>" + Constants.EOL); html.append(" </TR>" + Constants.EOL); html.append("</TABLE>" + Constants.EOL); return html.toString(); } /** * Retrieves a version of the verbose information for this stat tracker, * formatted for display in an HTML document. * * @return An HTML version of the verbose data for this stat tracker. */ public String getDetailHTML() { StringBuilder html = new StringBuilder(); html.append("<TABLE BORDER=\"1\">" + Constants.EOL); html.append(" <TR>" + Constants.EOL); html.append(" <TD><B>Total Count</B></TD>" + Constants.EOL); html.append(" <TD><B>Avg Count/Second</B></TD>" + Constants.EOL); html.append(" <TD><B>Avg Count/Interval</B></TD>" + Constants.EOL); html.append(" </TR>" + Constants.EOL); html.append(" <TR>" + Constants.EOL); html.append(" <TD>" + totalCount + "</TD>" + Constants.EOL); html.append(" <TD>" + decimalFormat.format(getAverageCountPerSecond()) + "</TD>" + Constants.EOL); html.append(" <TD>" + decimalFormat.format(getAverageCountPerInterval()) + "</TD>" + Constants.EOL); html.append(" </TR>" + Constants.EOL); html.append("</TABLE>" + Constants.EOL); html.append("<BR><BR>" + Constants.EOL); html.append("<TABLE BORDER=\"1\">" + Constants.EOL); html.append(" <TR>" + Constants.EOL); html.append(" <TD><B>Interval</B></TD>" + Constants.EOL); html.append(" <TD><B>Accumulated Total</B></TD>" + Constants.EOL); html.append(" <TD><B>Avg Count/Second</B></TD>" + Constants.EOL); html.append(" </TR>" + Constants.EOL); int lastValue = 0; for (int i=0; i < totalList.size(); i++) { int currentValue = totalList.get(i); html.append(" <TR>" + Constants.EOL); html.append(" <TD>" + (i+1) + "</TD>" + Constants.EOL); html.append(" <TD>" + currentValue + "</TD>" + Constants.EOL); html.append(" <TD>" + decimalFormat.format(1.0 * (currentValue-lastValue) / collectionInterval) + "</TD>" + Constants.EOL); html.append(" </TR>" + Constants.EOL); lastValue = currentValue; } html.append("</TABLE>" + Constants.EOL); return html.toString(); } /** * Retrieves a string array with the labels corresponding to the values * returned from the <CODE>getSummaryData</CODE> method. * * @return A string array with the labels corresponding to the values * returned from the <CODE>getSummaryData</CODE> method. */ public String[] getSummaryLabels() { return new String[] { displayName + " Total Count", displayName + " Avg Count/Second", displayName + " Avg Count/Interval" }; } /** * Retrieves the summary string data for this stat tracker as separate values. * * @return The summary string data for this stat tracker as separate values. */ public String[] getSummaryData() { return new String[] { String.valueOf(totalCount), decimalFormat.format(getAverageCountPerSecond()), decimalFormat.format(getAverageCountPerInterval()) }; } /** * Retrieves the raw data associated with this stat tracker in a form that * can be easily converted for export to CSV, tab-delimited text, or some * other format for use in an external application. There should be one value * per "cell". * * @param includeLabels Indicates whether the information being exported * should contain labels. * * @return The raw data associated with this stat tracker in a form that can * be exported to some external form. */ public String[][] getDataForExport(boolean includeLabels) { if (includeLabels) { String[][] returnArray = new String[totalList.size()+1][]; returnArray[0] = new String[] { "Interval", "Accumulated Total" }; for (int i=0; i < totalList.size(); i++) { returnArray[i+1] = new String[] { String.valueOf(i+1), String.valueOf(totalList.get(i)) }; } return returnArray; } else { String[][] returnArray = new String[totalList.size()][]; for (int i=0; i < totalList.size(); i++) { returnArray[i] = new String[] { String.valueOf(totalList.get(i)) }; } return returnArray; } } /** * Encodes the data collected by this tracker into a byte array that may be * transferred over the network or written out to persistent storage. * * @return The data collected by this tracker encoded as a byte array. */ public byte[] encode() { ASN1Element[] elements = new ASN1Element[totalList.size()]; for (int i=0; i < elements.length; i++) { elements[i] = new ASN1Integer(totalList.get(i)); } return new ASN1Sequence(elements).encode(); } /** * Decodes the provided data and uses it as the data for this stat tracker. * * @param encodedData The encoded version of the data to use for this * stat tracker. * * @throws SLAMDException If the provided data cannot be decoded and used * with this stat tracker. */ public void decode(byte[] encodedData) throws SLAMDException { try { ASN1Element[] elements = ASN1Element.decode(encodedData).decodeAsSequence().getElements(); totalList = new ArrayList<Integer>(elements.length); totalCount = 0; for (int i=0; i < elements.length; i++) { int accumulatedTotal = elements[i].decodeAsInteger().getIntValue(); totalList.add(accumulatedTotal); totalCount = accumulatedTotal; } } catch (Exception e) { throw new SLAMDException("Unable to decode data: " + e, e); } } /** * Retrieves the set of parameters that may be specified to customize the * graph that is generated based on the statistical information in the stat * trackers. * * @param job The job containing the statistical information to be graphed. * * @return The set of parameters that may be used to customize the graph that * is generated. */ public ParameterList getGraphParameterStubs(Job job) { ArrayList<String> dataSetList = new ArrayList<String>(); dataSetList.add("Overall Summary for Job " + job.getJobID()); dataSetList.add("Summary Statistics Per Client"); String[] clientIDs = job.getStatTrackerClientIDs(); for (int i=0; i < clientIDs.length; i++) { dataSetList.add("Detail Statistics for Client " + clientIDs[i]); } String[] dataSets = new String[dataSetList.size()]; dataSetList.toArray(dataSets); BooleanParameter baseAtZeroParameter = new BooleanParameter(Constants.SERVLET_PARAM_BASE_AT_ZERO, "Base at Zero", "Indicates whether the lower bound for the " + "graph should be based at zero rather than " + "dynamically calculated from the information " + "contained in the data provided.", true); BooleanParameter excludeFirstIntervalParameter = new BooleanParameter(Constants.SERVLET_PARAM_EXCLUDE_FIRST_INTERVAL, "Exclude First Interval", "Indicates whether information captured during " + "the first collection interval should be " + "ignored while generating the graph.", false); BooleanParameter excludeLastIntervalParameter = new BooleanParameter(Constants.SERVLET_PARAM_EXCLUDE_LAST_INTERVAL, "Exclude Last Interval", "Indicates whether information captured during " + "the last collection interval should be " + "ignored while generating the graph.", false); BooleanParameter includeHGridParameter = new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_HORIZ_GRID, "Include Horizontal Grid Lines", "Indicates whether the graph generated should " + "include horizontal grid lines.", true); BooleanParameter includeLegendParameter = new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_LABELS, "Include Legend", "Indicates whether the graph generated should " + "include a legend that shows the categories " + "included in the graph.", false); BooleanParameter includeVGridParameter = new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_VERT_GRID, "Include Vertical Grid Lines", "Indicates whether the graph generated should " + "include vertical grid lines.", true); MultiChoiceParameter detailLevelParameter = new MultiChoiceParameter(Constants.SERVLET_PARAM_DETAIL_LEVEL, "Data Set to Display", "Indicates the data that should be " + "displayed in the graph (overall for the " + "job, a summary for the client, or detail " + "for a particular client thread.", dataSets, dataSets[0]); Parameter[] parameters = new Parameter[] { detailLevelParameter, includeLegendParameter, excludeFirstIntervalParameter, excludeLastIntervalParameter, includeHGridParameter, includeVGridParameter, baseAtZeroParameter }; return new ParameterList(parameters); } /** * Retrieves the set of parameters that may be specified to customize the * graph that is generated based on the resource monitor information in the * stat trackers. * * @param job The job containing the resource monitor information to be * graphed. * * @return The set of parameters that may be used to customize the graph that * is generated. */ public ParameterList getMonitorGraphParameterStubs(Job job) { BooleanParameter baseAtZeroParameter = new BooleanParameter(Constants.SERVLET_PARAM_BASE_AT_ZERO, "Base at Zero", "Indicates whether the lower bound for the " + "graph should be based at zero rather than " + "dynamically calculated from the information " + "contained in the data provided.", true); BooleanParameter excludeFirstIntervalParameter = new BooleanParameter(Constants.SERVLET_PARAM_EXCLUDE_FIRST_INTERVAL, "Exclude First Interval", "Indicates whether information captured during " + "the first collection interval should be " + "ignored while generating the graph.", false); BooleanParameter excludeLastIntervalParameter = new BooleanParameter(Constants.SERVLET_PARAM_EXCLUDE_LAST_INTERVAL, "Exclude Last Interval", "Indicates whether information captured during " + "the last collection interval should be " + "ignored while generating the graph.", false); BooleanParameter includeHGridParameter = new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_HORIZ_GRID, "Include Horizontal Grid Lines", "Indicates whether the graph generated should " + "include horizontal grid lines.", true); BooleanParameter includeLegendParameter = new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_LABELS, "Include Legend", "Indicates whether the graph generated should " + "include a legend that shows the categories " + "included in the graph.", false); BooleanParameter includeVGridParameter = new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_VERT_GRID, "Include Vertical Grid Lines", "Indicates whether the graph generated should " + "include vertical grid lines.", true); Parameter[] parameters = new Parameter[] { includeLegendParameter, excludeFirstIntervalParameter, excludeLastIntervalParameter, includeHGridParameter, includeVGridParameter, baseAtZeroParameter }; return new ParameterList(parameters); } /** * Retrieves the set of parameters that may be specified to customize the * graph that is generated based on the statistical information in the stat * trackers. * * @param jobs The job containing the statistical information to be compared * and graphed. * * @return The set of parameters that may be used to customize the graph that * is generated. */ public ParameterList getGraphParameterStubs(Job[] jobs) { BooleanParameter baseAtZeroParameter = new BooleanParameter(Constants.SERVLET_PARAM_BASE_AT_ZERO, "Base at Zero", "Indicates whether the lower bound for the " + "graph should be based at zero rather than " + "dynamically calculated from the information " + "contained in the data provided.", true); BooleanParameter excludeFirstIntervalParameter = new BooleanParameter(Constants.SERVLET_PARAM_EXCLUDE_FIRST_INTERVAL, "Exclude First Interval", "Indicates whether information captured during " + "the first collection interval should be " + "ignored while generating the graph.", false); BooleanParameter excludeLastIntervalParameter = new BooleanParameter(Constants.SERVLET_PARAM_EXCLUDE_LAST_INTERVAL, "Exclude Last Interval", "Indicates whether information captured during " + "the last collection interval should be " + "ignored while generating the graph.", false); BooleanParameter includeLegendParameter = new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_LABELS, "Include Legend", "Indicates whether the graph generated should " + "include a legend that shows the categories " + "included in the graph.", false); BooleanParameter includeHGridParameter = new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_HORIZ_GRID, "Include Horizontal Grid Lines", "Indicates whether the graph generated should " + "include horizontal grid lines.", true); BooleanParameter includeVGridParameter = new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_VERT_GRID, "Include Vertical Grid Lines", "Indicates whether the graph generated should " + "include vertical grid lines.", true); Parameter[] parameters = new Parameter[] { includeLegendParameter, excludeFirstIntervalParameter, excludeLastIntervalParameter, includeHGridParameter, includeVGridParameter, baseAtZeroParameter }; return new ParameterList(parameters); } /** * Retrieves the data that represents the points in a line graph for this * stat tracker. This is only applicable if <CODE>isSearchable</CODE> * returns <CODE>true</CODE>. * * @return The data that represents the points in a line graph for this stat * tracker, or <CODE>null</CODE> if that data is not available. */ public double[] getGraphData() { int[] intervalTotals = getTotalsByInterval(); double[] doubleTotals = new double[intervalTotals.length]; for (int i=0; i < doubleTotals.length; i++) { doubleTotals[i] = intervalTotals[i]; } return doubleTotals; } /** * Retrieves the label that should be included along the vertical axis in a * line graph for this stat tracker. This is only applicable if * <CODE>isSearchable</CODE> returns <CODE>true</CODE>. * * @return The label that should be included along the vertical axis in a * line graph for this stat tracker, or <CODE>null</CODE> if that * data is not applicable. */ public String getAxisLabel() { return "Accumulated Total"; } /** * Creates a graph that visually depicts the information in the provided set * of stat trackers. The provided stat trackers must be of the same type as * this stat tracker. * * @param job The job containing the statistical information to be * graphed. * @param width The width in pixels of the graph to create. * @param height The height in pixels of the graph to create. * @param parameters The set of parameters that may be used to customize the * graph that is generated. * * @return The graph created from the statistical information in the provided * job. */ public BufferedImage createGraph(Job job, int width, int height, ParameterList parameters) { boolean includeLegend = false; boolean excludeFirst = false; boolean excludeLast = false; boolean includeHGrid = false; boolean includeVGrid = false; boolean baseAtZero = false; String detailLevelStr = "Overall Summary for Job " + job.getJobID(); String graphTitle = displayName + " for Job " + job.getJobID(); String legendTitle = "Job ID"; BooleanParameter includeLegendParameter = parameters.getBooleanParameter(Constants.SERVLET_PARAM_INCLUDE_LABELS); if (includeLegendParameter != null) { includeLegend = includeLegendParameter.getBooleanValue(); } BooleanParameter excludeFirstParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_EXCLUDE_FIRST_INTERVAL); if (excludeFirstParameter != null) { excludeFirst = excludeFirstParameter.getBooleanValue(); } BooleanParameter excludeLastParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_EXCLUDE_LAST_INTERVAL); if (excludeLastParameter != null) { excludeLast = excludeLastParameter.getBooleanValue(); } BooleanParameter includeHGridParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_INCLUDE_HORIZ_GRID); if (includeHGridParameter != null) { includeHGrid = includeHGridParameter.getBooleanValue(); } BooleanParameter includeVGridParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_INCLUDE_VERT_GRID); if (includeVGridParameter != null) { includeVGrid = includeVGridParameter.getBooleanValue(); } BooleanParameter baseAtZeroParameter = parameters.getBooleanParameter(Constants.SERVLET_PARAM_BASE_AT_ZERO); if (baseAtZeroParameter != null) { baseAtZero = baseAtZeroParameter.getBooleanValue(); } MultiChoiceParameter detailLevelParameter = parameters.getMultiChoiceParameter( Constants.SERVLET_PARAM_DETAIL_LEVEL); if (detailLevelParameter != null) { detailLevelStr = detailLevelParameter.getStringValue(); } StatTracker[] trackersToGraph = new StatTracker[0]; int statCategory = Constants.STAT_CATEGORY_JOB_STATS; if (detailLevelStr.equals("Summary Statistics Per Client")) { statCategory = Constants.STAT_CATEGORY_CLIENT_STATS; graphTitle = displayName + " by Client"; legendTitle = "Client ID"; String[] clientIDs = job.getStatTrackerClientIDs(); ArrayList<StatTracker> trackerList = new ArrayList<StatTracker>(); for (int i=0; i < clientIDs.length; i++) { StatTracker[] trackers = job.getStatTrackers(displayName, clientIDs[i]); if ((trackers != null) && (trackers.length > 0)) { AccumulatingTracker tracker = new AccumulatingTracker(clientIDs[i], "", displayName, trackers[0].getCollectionInterval()); tracker.aggregate(trackers); trackerList.add(tracker); } } trackersToGraph = new StatTracker[trackerList.size()]; trackerList.toArray(trackersToGraph); } else if (detailLevelStr.startsWith("Detail Statistics for Client ")) { statCategory = Constants.STAT_CATEGORY_THREAD_STATS; String clientID = detailLevelStr.substring(29); graphTitle = displayName + " by Thread for Client " + clientID; legendTitle = "Thread ID"; trackersToGraph = job.getStatTrackers(displayName, clientID); } else { StatTracker[] trackers = job.getStatTrackers(displayName); AccumulatingTracker tracker = new AccumulatingTracker("", "", displayName, trackers[0].getCollectionInterval()); tracker.aggregate(trackers); trackersToGraph = new StatTracker[] { tracker }; } StatGrapher grapher = new StatGrapher(width, height, graphTitle); grapher.setBaseAtZero(baseAtZero); grapher.setIncludeLegend(includeLegend, legendTitle); grapher.setVerticalAxisTitle("Accumulated Total"); grapher.setIncludeHorizontalGrid(includeHGrid); grapher.setIncludeVerticalGrid(includeVGrid); grapher.setIgnoreZeroValues(false); for (int i=0; i < trackersToGraph.length; i++) { AccumulatingTracker tracker = (AccumulatingTracker) trackersToGraph[i]; int[] intervalTotals = tracker.getTotalsByInterval(); int numElements = intervalTotals.length; int j = 0; int subtractor = 0; int lastIndex = intervalTotals.length; if (excludeFirst) { numElements--; j = 1; subtractor = 1; } if (excludeLast) { numElements--; lastIndex--; } double[] graphTotals = new double[numElements]; for ( ; j < lastIndex; j++) { graphTotals[j-subtractor] = intervalTotals[j]; } String label; switch (statCategory) { case Constants.STAT_CATEGORY_CLIENT_STATS: label = trackersToGraph[i].getClientID(); break; case Constants.STAT_CATEGORY_THREAD_STATS: label = trackersToGraph[i].getThreadID(); break; default: label = "Job " + job.getJobID(); break; } grapher.addDataSet(graphTotals, job.getCollectionInterval(), label); } return grapher.generateLineGraph(); } /** * Creates a graph that visually depicts the information collected by resource * monitors associated with the provided job. * * @param job The job containing the statistical information to be * graphed. * @param width The width in pixels of the graph to create. * @param height The height in pixels of the graph to create. * @param parameters The set of parameters that may be used to customize * the graph that is generated. * * @return The graph created from the statistical information in the provided * job. */ public BufferedImage createMonitorGraph(Job job, int width, int height, ParameterList parameters) { boolean excludeFirst = false; boolean excludeLast = false; boolean includeLegend = false; boolean includeHGrid = false; boolean includeVGrid = false; boolean baseAtZero = false; String graphTitle = displayName; BooleanParameter excludeFirstParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_EXCLUDE_FIRST_INTERVAL); if (excludeFirstParameter != null) { excludeFirst = excludeFirstParameter.getBooleanValue(); } BooleanParameter excludeLastParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_EXCLUDE_LAST_INTERVAL); if (excludeLastParameter != null) { excludeLast = excludeLastParameter.getBooleanValue(); } BooleanParameter includeLegendParameter = parameters.getBooleanParameter(Constants.SERVLET_PARAM_INCLUDE_LABELS); if (includeLegendParameter != null) { includeLegend = includeLegendParameter.getBooleanValue(); } BooleanParameter includeHGridParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_INCLUDE_HORIZ_GRID); if (includeHGridParameter != null) { includeHGrid = includeHGridParameter.getBooleanValue(); } BooleanParameter includeVGridParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_INCLUDE_VERT_GRID); if (includeVGridParameter != null) { includeVGrid = includeVGridParameter.getBooleanValue(); } BooleanParameter baseAtZeroParameter = parameters.getBooleanParameter(Constants.SERVLET_PARAM_BASE_AT_ZERO); if (baseAtZeroParameter != null) { baseAtZero = baseAtZeroParameter.getBooleanValue(); } StatTracker[] trackers = job.getResourceStatTrackers(displayName); AccumulatingTracker tracker = new AccumulatingTracker("", "", displayName, trackers[0].getCollectionInterval()); tracker.aggregate(trackers); StatTracker[] trackersToGraph = new StatTracker[] { tracker }; StatGrapher grapher = new StatGrapher(width, height, graphTitle); grapher.setBaseAtZero(baseAtZero); grapher.setIncludeLegend(includeLegend, "Job ID"); grapher.setVerticalAxisTitle("Accumulated Total"); grapher.setIncludeHorizontalGrid(includeHGrid); grapher.setIncludeVerticalGrid(includeVGrid); grapher.setIgnoreZeroValues(false); for (int i=0; i < trackersToGraph.length; i++) { tracker = (AccumulatingTracker) trackersToGraph[i]; int[] intervalTotals = tracker.getTotalsByInterval(); int numElements = intervalTotals.length; int j = 0; int subtractor = 0; int lastIndex = intervalTotals.length; if (excludeFirst) { numElements--; j = 1; subtractor = 1; } if (excludeLast) { numElements--; lastIndex--; } double[] graphTotals = new double[numElements]; for ( ; j < lastIndex; j++) { graphTotals[j-subtractor] = intervalTotals[j]; } grapher.addDataSet(graphTotals, job.getCollectionInterval(), "Job " + job.getJobID()); } return grapher.generateLineGraph(); } /** * Creates a graph that visually depicts the information in the provided set * of stat trackers. The provided stat trackers must be the of the same type * as this stat tracker. * * @param jobs The job containing the statistical information to be * compared and graphed. * @param width The width in pixels of the graph to create. * @param height The height in pixels of the graph to create. * @param parameters The set of parameters that may be used to customize the * graph that is generated. * * @return The graph created from the statistical information in the provided * job. */ public BufferedImage createGraph(Job[] jobs, int width, int height, ParameterList parameters) { boolean drawAsBarGraph = false; boolean includeLegend = false; boolean excludeFirst = false; boolean excludeLast = false; boolean includeHGrid = false; boolean includeVGrid = false; boolean baseAtZero = false; String graphTitle = "Comparison of " + displayName; BooleanParameter includeLegendParameter = parameters.getBooleanParameter(Constants.SERVLET_PARAM_INCLUDE_LABELS); if (includeLegendParameter != null) { includeLegend = includeLegendParameter.getBooleanValue(); } BooleanParameter drawAsBarParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_DRAW_AS_BAR_GRAPH); if (drawAsBarParameter != null) { drawAsBarGraph = drawAsBarParameter.getBooleanValue(); } BooleanParameter excludeFirstParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_EXCLUDE_FIRST_INTERVAL); if (excludeFirstParameter != null) { excludeFirst = excludeFirstParameter.getBooleanValue(); } BooleanParameter excludeLastParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_EXCLUDE_LAST_INTERVAL); if (excludeLastParameter != null) { excludeLast = excludeLastParameter.getBooleanValue(); } BooleanParameter includeHGridParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_INCLUDE_HORIZ_GRID); if (includeHGridParameter != null) { includeHGrid = includeHGridParameter.getBooleanValue(); } BooleanParameter includeVGridParameter = parameters.getBooleanParameter( Constants.SERVLET_PARAM_INCLUDE_VERT_GRID); if (includeVGridParameter != null) { includeVGrid = includeVGridParameter.getBooleanValue(); } BooleanParameter baseAtZeroParameter = parameters.getBooleanParameter(Constants.SERVLET_PARAM_BASE_AT_ZERO); if (baseAtZeroParameter != null) { baseAtZero = baseAtZeroParameter.getBooleanValue(); } StatTracker[] trackersToGraph = new StatTracker[jobs.length]; for (int i=0; i < jobs.length; i++) { StatTracker[] jobTrackers = jobs[i].getStatTrackers(displayName); AccumulatingTracker jobTracker = new AccumulatingTracker(null, null, displayName, jobTrackers[0].getCollectionInterval()); jobTracker.aggregate(jobTrackers); trackersToGraph[i] = jobTracker; } StatGrapher grapher = new StatGrapher(width, height, graphTitle); grapher.setBaseAtZero(baseAtZero); grapher.setIncludeLegend(includeLegend, "Job"); grapher.setVerticalAxisTitle("Accumulated Total"); grapher.setIncludeHorizontalGrid(includeHGrid); grapher.setIncludeVerticalGrid(includeVGrid); grapher.setIgnoreZeroValues(false); for (int i=0; i < trackersToGraph.length; i++) { AccumulatingTracker tracker = (AccumulatingTracker) trackersToGraph[i]; int[] intervalTotals = tracker.getTotalsByInterval(); int numElements = intervalTotals.length; int j = 0; int subtractor = 0; int lastIndex = intervalTotals.length; if (excludeFirst) { numElements--; j = 1; subtractor = 1; } if (excludeLast) { numElements--; lastIndex--; } double[] graphTotals = new double[numElements]; for ( ; j < lastIndex; j++) { graphTotals[j-subtractor] = intervalTotals[j]; } String label = jobs[i].getJobDescription(); if ((label == null) || (label.length() == 0)) { label = jobs[i].getJobID(); } grapher.addDataSet(graphTotals, jobs[i].getCollectionInterval(), label); } if (drawAsBarGraph) { return grapher.generateBarGraph(); } else { return grapher.generateLineGraph(); } } /** * Creates a graph that visually depicts the information in this stat tracker * using all the default settings. * * @param width The width in pixels of the graph to create. * @param height The height in pixels of the graph to create. * * @return The graph created from this stat tracker. */ public BufferedImage createGraph(int width, int height) { StatGrapher grapher = new StatGrapher(width, height, displayName); grapher.setBaseAtZero(true); grapher.setIncludeHorizontalGrid(true); grapher.setIncludeVerticalGrid(true); grapher.setVerticalAxisTitle("Accumulated Total"); grapher.setIncludeLegend(false, ""); grapher.setIgnoreZeroValues(false); int[] intervalTotals = getTotalsByInterval(); double[] graphTotals = new double[intervalTotals.length]; for (int i=0; i< intervalTotals.length; i++) { graphTotals[i] = intervalTotals[i]; } grapher.addDataSet(graphTotals, collectionInterval, displayName); return grapher.generateLineGraph(); } }