/*
* 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 track events
* associated with integer values.
*
*
* @author Neil A. Wilson
*/
public class IntegerValueTracker
implements StatTracker
{
/**
* The text that will be used to indicate that the comparison between multiple
* jobs will be performed in parallel.
*/
public static final String COMPARE_IN_PARALLEL_STRING = "Compare in Parallel";
/**
* The text that will be used to indicate that the comparison between multiple
* jobs will be performed over time.
*/
public static final String COMPARE_OVER_TIME_STRING = "Compare over Time";
// The list that contains the data collected by this tracker, broken up into
// intervals.
private ArrayList<Integer> countList;
// The list that contains the sums of the values collected by this tracker,
// broken up into intervals.
private ArrayList<Integer> totalList;
// Indicates whether to enable real-time statistics collection.
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 number of occurrences so far for the current interval.
private int intervalCount;
// The current interval number for reporting real-time stats.
private int intervalNum;
// The total so far for the current interval.
private int intervalTotal;
// The maximum value provided while gathering statistics.
private int maxValue;
// The minimum value provided while gathering statistics.
private int minValue;
// 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 statistics.
private RealTimeStatReporter statReporter;
// The client ID for 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 for the client thread that used this stat tracker.
private String threadID;
/**
* Creates a new integer value tracker intended for use as a placeholder for
* decoding purposes. This version of the constructor should not be used
* by job classes.
*/
public IntegerValueTracker()
{
this.clientID = "";
this.threadID = "";
this.displayName = "";
this.collectionInterval = Constants.DEFAULT_COLLECTION_INTERVAL;
decimalFormat = new DecimalFormat("0.000");
intervalCount = 0;
intervalTotal = 0;
maxValue = Integer.MIN_VALUE;
minValue = Integer.MAX_VALUE;
duration = 0;
intervalStopTime = 0;
startTime = System.currentTimeMillis();
stopTime = 0;
countList = new ArrayList<Integer>();
totalList = new ArrayList<Integer>();
enableRealTimeStats = false;
statReporter = null;
intervalNum = 0;
hasBeenStarted = false;
isRunning = false;
}
/**
* Creates a new integer value 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 client 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 IntegerValueTracker(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");
intervalCount = 0;
intervalTotal = 0;
maxValue = Integer.MIN_VALUE;
minValue = Integer.MAX_VALUE;
duration = 0;
intervalStopTime = 0;
startTime = System.currentTimeMillis();
stopTime = 0;
countList = new ArrayList<Integer>();
totalList = new ArrayList<Integer>();
enableRealTimeStats = false;
statReporter = null;
intervalNum = 0;
hasBeenStarted = false;
isRunning = false;
}
/**
* 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 IntegerValueTracker(clientID, threadID, displayName,
collectionInterval);
}
/**
* Adds the specified value to the set of information collected so far for
* this stat tracker.
*
* @param intValue The value to add to the set of information collected so
* far for this stat tracker.
*/
public void addValue(int intValue)
{
long now = System.currentTimeMillis();
// We're in the same interval as the last time a value was added. Just add
// this new one.
if (now < intervalStopTime)
{
intervalCount++;
intervalTotal += intValue;
}
// 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
{
countList.add(intervalCount);
totalList.add(intervalTotal);
if (enableRealTimeStats)
{
statReporter.reportStatToAverage(this, intervalNum++,
(1.0 * intervalTotal / intervalCount));
}
intervalStopTime += (1000 * collectionInterval);
while (intervalStopTime < now)
{
countList.add(0);
totalList.add(0);
if (enableRealTimeStats)
{
statReporter.reportStatToAverage(this, intervalNum++, 0.0);
}
intervalStopTime += (1000 * collectionInterval);
}
intervalCount = 1;
intervalTotal = intValue;
}
// Check to see if this value is larger than the maximum or smaller than
// the minimum
if (intValue > maxValue)
{
maxValue = intValue;
}
if (intValue < minValue)
{
minValue = intValue;
}
}
/**
* 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 " +
"integer value stat tracker " + displayName);
}
else
{
hasBeenStarted = true;
isRunning = true;
}
// Just in case, reset all the counter info.
intervalCount = 0;
intervalTotal = 0;
maxValue = Integer.MIN_VALUE;
minValue = Integer.MAX_VALUE;
countList = new ArrayList<Integer>();
totalList = new ArrayList<Integer>();
// 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 integer value " +
"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)
{
countList.add(intervalCount);
totalList.add(intervalTotal);
if (enableRealTimeStats)
{
statReporter.reportStatToAverage(this, intervalNum++,
(1.0 * intervalTotal / intervalCount));
}
intervalStopTime += (1000 * collectionInterval);
}
while (intervalStopTime < now)
{
countList.add(0);
totalList.add(0);
if (enableRealTimeStats)
{
statReporter.reportStatToAverage(this, intervalNum++, 0.0);
}
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()
{
int totalCount = 0;
for (int i=0; i < countList.size(); i++)
{
totalCount += countList.get(i);
}
return totalCount;
}
/**
* Retrieves the total number of times that the tracked event occurred over
* the total duration. This method will return the count as a long, which is
* less likely to overflow than an integer.
*
* @return The total number of times that the tracked event occurred over the
* total duration.
*/
public long getTotalCountAsLong()
{
long totalCount = 0;
for (int i=0; i < countList.size(); i++)
{
totalCount += countList.get(i);
}
return totalCount;
}
/**
* Retrieves the sum of all the individual values added to this stat tracker
* over the total duration.
*
* @return The sum of all the individual values added to this stat tracker
* over the total duration.
*/
public int getTotalValue()
{
int totalValue = 0;
for (int i=0; i < totalList.size(); i++)
{
totalValue += totalList.get(i);
}
return totalValue;
}
/**
* Retrieves the sum of all the individual values added to this stat tracker
* over the total duration. The total value will be returned as a long, which
* is less likely to overflow than an integer.
*
* @return The sum of all the individual values added to this stat tracker
* over the total duration.
*/
public long getTotalValueAsLong()
{
long totalValue = 0;
for (int i=0; i < totalList.size(); i++)
{
totalValue += totalList.get(i);
}
return totalValue;
}
/**
* Retrieves the sum of all the values added to this stat tracker divided by
* the number of values added to this stat tracker.
*
* @return The average of all the values added to this stat tracker.
*/
public double getAverageValue()
{
long totalCount = getTotalCountAsLong();
if (totalCount > 0)
{
return (1.0 * getTotalValueAsLong() / totalCount);
}
else
{
return 0.0;
}
}
/**
* Retrieves an array indicating the number of times the tracked event
* occurred over each interval.
*
* @return An array indicating the number of times the tracked event occurred
* over each interval.
*/
public int[] getIntervalCounts()
{
int[] intValues = new int[countList.size()];
for (int i=0; i < intValues.length; i++)
{
intValues[i] = countList.get(i);
}
return intValues;
}
/**
* Retrieves an array containing the sums of all the values added to this
* tracker over each interval.
*
* @return An array containing the sums of all the values added to this
* tracker over each interval.
*/
public int[] getIntervalTotals()
{
int[] totalValues = new int[totalList.size()];
for (int i=0; i < totalValues.length; i++)
{
totalValues[i] = totalList.get(i);
}
return totalValues;
}
/**
* Specifies the data associated with this tracker in terms of the total value
* per interval and the number of occurrences per interval. Note that the
* number of elements in each array must be identical.
*
* @param intervalTotals An array containing the sums of all the values
* added to this tracker over each interval.
* @param intervalCounts An array indicating the number of times the tracked
* event occurred over each interval.
*/
public void setIntervalData(int[] intervalTotals, int[] intervalCounts)
{
maxValue = Integer.MIN_VALUE;
minValue = Integer.MAX_VALUE;
totalList = new ArrayList<Integer>();
countList = new ArrayList<Integer>();
for (int i=0; i < intervalTotals.length; i++)
{
totalList.add(intervalTotals[i]);
countList.add(intervalCounts[i]);
if (intervalTotals[i] > maxValue)
{
maxValue = intervalTotals[i];
}
if (intervalTotals[i] < minValue)
{
minValue = intervalTotals[i];
}
}
duration = collectionInterval * intervalTotals.length;
}
/**
* Retrieves the maximum value supplied to this tracker over the entire
* duration.
*
* @return The maximum value supplied to this tracker over the entire
* duration.
*/
public int getMaxValue()
{
return maxValue;
}
/**
* Retrieves the minimum value supplied to this tracker over the entire
* duration.
*
* @return The minimum value supplied to this tracker over the entire
* duration.
*/
public int getMinValue()
{
return minValue;
}
/**
* 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 (! countList.isEmpty())
{
return (1.0 * getTotalCountAsLong() / countList.size());
}
else
{
return 0.0;
}
}
/**
* Retrieves the average total of all the values added in a single interval.
*
* @return The average total of all values added in a single interval.
*/
public double getAverageValuePerInterval()
{
if (! totalList.isEmpty())
{
return (1.0 * getTotalValueAsLong() / totalList.size());
}
else
{
return 0.0;
}
}
/**
* Retrieves the average number of times that this tracker was updated in a
* single second.
*
* @return The average number of times that this tracker was updated in a
* single second.
*/
public double getAverageCountPerSecond()
{
if (duration > 0)
{
return (1.0 * getTotalCountAsLong() / duration);
}
else
{
return 0;
}
}
/**
* Retrieves the sum of all the values provided to this tracker divided by the
* total number of seconds that it was collecting data.
*
* @return The sum of all the values provided to this tracker divided by the
* total number of seconds that it was collecting data.
*/
public double getAverageValuePerSecond()
{
if (duration > 0)
{
return (1.0 * getTotalValueAsLong() / duration);
}
else
{
return 0;
}
}
/**
* Retrieves the standard deviation for this tracker, based on the average
* value per occurrence.
*
* @return The standard deviation for this tracker, based on average value
* per occurrence.
*/
public double getStandardDeviation()
{
double avgValue = getAverageValue();
double sumOfDifferencesSquared = 0.0;
int n = 0;
for (int i=0; i < countList.size(); i++)
{
int intervalTotal = totalList.get(i);
int intervalCount = countList.get(i);
if (intervalCount > 0)
{
double intervalAvgPerOccurrence = 1.0 * intervalTotal / intervalCount;
double difference = (intervalAvgPerOccurrence - avgValue);
sumOfDifferencesSquared += (difference * difference);
n++;
}
}
return Math.sqrt(sumOfDifferencesSquared / (n - 1));
}
/**
* Retrieves the correlation coefficient for the data, which is a measure of
* how strongly a change in one variable impacts a change in another. If the
* regression line is perfectly horizontal (i.e., a change in one variable has
* no impact whatsoever on the value of the other), then the correlation
* coefficient will be zero. If the data is perfectly linear but not
* horizontal (i.e., a change in one variable has a linear, proportional
* change in the other), then it will be 1.0 or -1.0. If the data is not
* linear, then the value will be somewhere between -1.0 and 0.0, or between
* 0.0 and 1.0.
*
* @return The regression coefficient for the data.
*/
public double getCorrelationCoefficient()
{
int n = countList.size();
double avgX = getAverageValue();
double avgY = (countList.size() * collectionInterval) / 2;
double sumOfXDiffsSquared = 0.0;
double sumOfYDiffsSquared = 0.0;
double sumOfXYDiffs = 0.0;
for (int i=0; i < countList.size(); i++)
{
int intervalTotal = totalList.get(i);
int intervalCount = countList.get(i);
if (intervalCount > 0)
{
double xValue = intervalTotal / intervalCount;
int yValue = (collectionInterval * i);
double xDiff = xValue - avgX;
double yDiff = yValue - avgY;
sumOfXDiffsSquared += (xDiff*xDiff);
sumOfYDiffsSquared += (yDiff*yDiff);
sumOfXYDiffs += (xDiff*yDiff);
}
}
if (sumOfXDiffsSquared == 0)
{
return 0.0;
}
double stdDevX = Math.sqrt(sumOfXDiffsSquared / (n-1));
double stdDevY = Math.sqrt(sumOfYDiffsSquared / (n-1));
return (sumOfXYDiffs / ((n-1)*stdDevX*stdDevY));
}
/**
* Retrieves the linear regression coefficients for this tracker, based on the
* average value per occurrence. The array returned will have two values,
* A and B, which can be used in the equation y = A + Bx to generate the line
* that most closely approximates the set of results.
*
* @return The linear regression coefficients for this tracker, based on the
* average value per occurrence.
*/
public double[] getRegressionCoefficients()
{
double sx = 0.0;
double sy = 0.0;
double sxx = 0.0;
double syy = 0.0;
double sxy = 0.0;
int n = 0;
for (int i=0; i < countList.size(); i++)
{
int intervalTotal = totalList.get(i);
int intervalCount = countList.get(i);
if (intervalCount > 0)
{
double intervalAvgValue = 1.0 * intervalTotal / intervalCount;
sx += (collectionInterval*i);
sy += intervalAvgValue;
sxx += (collectionInterval*collectionInterval*i*i);
syy += (intervalAvgValue*intervalAvgValue);
sxy += (collectionInterval*i*intervalAvgValue);
n++;
}
}
double b = (sxy - (sx*sy)/n) / (sxx - (sx*sx)/n);
double a = (sy - b*sx) / n;
return new double[] { a, b };
}
/**
* Retrieves a T score value that can be used to determine how confident we
* are that the result set is approximately linear. It does this by dividing
* the data set into two halves and performing a confidence test to determine
* whether those two means can be considered equal.
*
* @return The calculated T score value.
*/
public double getHorizontalityTScore()
{
// First, get the counts and totals as integer arrays to make things easier.
int[] totalArray = getIntervalTotals();
int[] countArray = getIntervalCounts();
// Break the data up into two equal halves.
int n = countArray.length;
int n1 = n / 2;
int n2 = n - n1;
// Now calculate the totals for each half.
int total1 = 0;
int total2 = 0;
int count1 = 0;
int count2 = 0;
for (int i=0; i < n; i++)
{
if (i < n1)
{
total1 += totalArray[i];
count1 += countArray[i];
}
else
{
total2 += totalArray[i];
count2 += countArray[i];
}
}
// If the count for both halves is zero, then the T score will automatically
// be zero.
if ((count1 == 0) && (count2 == 0))
{
return 0.0;
}
// Find the average for each half.
double mean1 = 1.0 * total1 / n1 / count1;
double mean2 = 1.0 * total2 / n2 / count2;
// Next, calculate the standard deviation for each half.
double sumOfDifferencesSquared1 = 0.0;
double sumOfDifferencesSquared2 = 0.0;
for (int i=0; i < n; i++)
{
if (i < n1)
{
double intervalAvg = 1.0 * totalArray[i] / countArray[i];
double difference = (mean1 - intervalAvg);
sumOfDifferencesSquared1 += (difference * difference);
}
else
{
double intervalAvg = 1.0 * totalArray[i] / countArray[i];
double difference = (mean2 - intervalAvg);
sumOfDifferencesSquared2 += (difference * difference);
}
}
double stdDev1 = Math.sqrt(sumOfDifferencesSquared1 / (n1 - 1));
double stdDev2 = Math.sqrt(sumOfDifferencesSquared2 / (n2 - 1));
// Finally, calculate the standard error and the T score.
double stdError = Math.sqrt(stdDev1*stdDev1/n1 + stdDev2*stdDev2/n2);
return Math.abs(mean1 - mean2) / stdError;
}
/**
* 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 (getAverageValue() >= 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 (getAverageValue() <= 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 getAverageValue();
}
/**
* 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 countList.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)
{
duration = Integer.MAX_VALUE;
maxValue = Integer.MIN_VALUE;
minValue = Integer.MAX_VALUE;
if (trackers.length > 0)
{
collectionInterval = trackers[0].getCollectionInterval();
}
int min = Integer.MAX_VALUE;
int[][] counts = new int[trackers.length][];
int[][] totals = new int[trackers.length][];
for (int i=0; i < counts.length; i++)
{
counts[i] = ((IntegerValueTracker) trackers[i]).getIntervalCounts();
totals[i] = ((IntegerValueTracker) trackers[i]).getIntervalTotals();
if (counts[i].length < min)
{
min = counts[i].length;
}
if (trackers[i].getDuration() < duration)
{
duration = trackers[i].getDuration();
}
if (((IntegerValueTracker) trackers[i]).getMaxValue() > maxValue)
{
maxValue = ((IntegerValueTracker) trackers[i]).getMaxValue();
}
if (((IntegerValueTracker) trackers[i]).getMinValue() < minValue)
{
minValue = ((IntegerValueTracker) trackers[i]).getMinValue();
}
}
int[] aggregateCounts = new int[min];
int[] aggregateTotals = new int[min];
for (int i=0; i < aggregateCounts.length; i++)
{
aggregateCounts[i] = 0;
aggregateTotals[i] = 0;
for (int j=0; j < counts.length; j++)
{
aggregateCounts[i] += counts[j][i];
aggregateTotals[i] += totals[j][i];
}
}
countList = new ArrayList<Integer>(aggregateCounts.length);
totalList = new ArrayList<Integer>(aggregateCounts.length);
for (int i=0; i < aggregateCounts.length; i++)
{
countList.add(aggregateCounts[i]);
totalList.add(aggregateTotals[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: " + getTotalValueAsLong() +
"; Avg Value: " + decimalFormat.format(getAverageValue()) +
"; Avg/Second: " +
decimalFormat.format(getAverageValuePerSecond()) +
"; Avg/Interval: " +
decimalFormat.format(getAverageValuePerInterval()) +
"; Std Dev: " + decimalFormat.format(getStandardDeviation()) +
"; Corr Coeff: " +
decimalFormat.format(getCorrelationCoefficient());
}
/**
* 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();
double[] regressionCoefficients = getRegressionCoefficients();
String regressionEquation = "y = " +
decimalFormat.format(regressionCoefficients[0]);
if (regressionCoefficients[1] < 0)
{
regressionEquation += " - " +
decimalFormat.format(Math.abs(regressionCoefficients[1])) + "x";
}
else
{
regressionEquation += " + " +
decimalFormat.format(regressionCoefficients[1]) + "x";
}
returnBuffer.append(displayName + Constants.EOL);
returnBuffer.append("Total: " + getTotalValueAsLong() + Constants.EOL);
returnBuffer.append("Count: " + getTotalCountAsLong() + Constants.EOL);
returnBuffer.append("Average Value: " +
decimalFormat.format(getAverageValue()) +
Constants.EOL);
if (getMaxValue() == Integer.MIN_VALUE)
{
returnBuffer.append("Maximum Value: N/A" + Constants.EOL);
}
else
{
returnBuffer.append("Maximum Value: " + getMaxValue() + Constants.EOL);
}
if (getMinValue() == Integer.MAX_VALUE)
{
returnBuffer.append("Minimum Value: N/A" + Constants.EOL);
}
else
{
returnBuffer.append("Minimum Value: " + getMinValue() + Constants.EOL);
}
returnBuffer.append("Average Total/Second: " +
decimalFormat.format(getAverageValuePerSecond()) +
Constants.EOL);
returnBuffer.append("Average Count/Second: " +
decimalFormat.format(getAverageCountPerSecond()) +
Constants.EOL);
returnBuffer.append("Average Total/Interval: " +
decimalFormat.format(getAverageValuePerInterval()) +
Constants.EOL);
returnBuffer.append("Average Count/Interval: " +
decimalFormat.format(getAverageCountPerInterval()) +
Constants.EOL);
returnBuffer.append("Std Dev: " +
decimalFormat.format(getStandardDeviation()) +
Constants.EOL);
returnBuffer.append("Correlation Coefficient: " +
decimalFormat.format(getCorrelationCoefficient()) +
Constants.EOL);
returnBuffer.append("Regression Equation: " + regressionEquation +
Constants.EOL);
returnBuffer.append("Horizontality T Score: " +
decimalFormat.format(getHorizontalityTScore()) +
Constants.EOL);
for (int i=0; i < countList.size(); i++)
{
int c = countList.get(i);
int t = totalList.get(i);
returnBuffer.append("Interval ");
returnBuffer.append(i+1);
returnBuffer.append(": ");
returnBuffer.append("Total=" + t);
returnBuffer.append("; Count=" + c);
if (c > 0)
{
returnBuffer.append("; Average=" + decimalFormat.format(1.0 * t / c));
}
else
{
returnBuffer.append("; Average=0.000");
}
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</B></TD>" + Constants.EOL);
html.append(" <TD><B>Avg Value</B></TD>" + Constants.EOL);
html.append(" <TD><B>Avg/Second</B></TD>" + Constants.EOL);
html.append(" <TD><B>Avg/Interval</B></TD>" + Constants.EOL);
html.append(" <TD><B>Std Dev</B></TD>" + Constants.EOL);
html.append(" <TD><B>Corr Coeff</B></TD>" + Constants.EOL);
html.append(" </TR>" + Constants.EOL);
html.append(" <TR>" + Constants.EOL);
html.append(" <TD>" + getTotalValueAsLong() + "</TD>" + Constants.EOL);
html.append(" <TD>" + decimalFormat.format(getAverageValue()) + "</TD>" +
Constants.EOL);
html.append(" <TD>" + decimalFormat.format(getAverageValuePerSecond()) +
"</TD>" + Constants.EOL);
html.append(" <TD>" +
decimalFormat.format(getAverageValuePerInterval()) + "</TD>" +
Constants.EOL);
html.append(" <TD>" + decimalFormat.format(getStandardDeviation()) +
"</TD>" + Constants.EOL);
html.append(" <TD>" + decimalFormat.format(getCorrelationCoefficient()) +
"</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();
double[] regressionCoefficients = getRegressionCoefficients();
String regressionEquation = "y = " +
decimalFormat.format(regressionCoefficients[0]);
if (regressionCoefficients[1] < 0)
{
regressionEquation += " - " +
decimalFormat.format(Math.abs(regressionCoefficients[1])) + "x";
}
else
{
regressionEquation += " + " +
decimalFormat.format(regressionCoefficients[1]) + "x";
}
html.append("<TABLE BORDER=\"1\">" + Constants.EOL);
html.append(" <TR>" + Constants.EOL);
html.append(" <TD><B>Total</B></TD>" + Constants.EOL);
html.append(" <TD><B>Avg Value</B></TD>" + Constants.EOL);
html.append(" <TD><B>Max Value</B></TD>" + Constants.EOL);
html.append(" <TD><B>Min Value</B></TD>" + Constants.EOL);
html.append(" <TD><B>Avg/Second</B></TD>" + Constants.EOL);
html.append(" <TD><B>Avg/Interval</B></TD>" + Constants.EOL);
html.append(" <TD><B>Std Dev</B></TD>" + Constants.EOL);
html.append(" <TD><B>Correlation Coefficient</B></TD>" + Constants.EOL);
html.append(" <TD><B>Regression Equation</B></TD>" + Constants.EOL);
html.append(" <TD><B>Horizontality T Score</B></TD>" + Constants.EOL);
html.append(" </TR>" + Constants.EOL);
html.append(" <TR>" + Constants.EOL);
html.append(" <TD>" + getTotalValueAsLong() + "</TD>" + Constants.EOL);
html.append(" <TD>" + decimalFormat.format(getAverageValue()) + "</TD>" +
Constants.EOL);
if (getMaxValue() == Integer.MIN_VALUE)
{
html.append(" <TD>N/A</TD>" + Constants.EOL);
}
else
{
html.append(" <TD>" + getMaxValue() + "</TD>" + Constants.EOL);
}
if (getMinValue() == Integer.MAX_VALUE)
{
html.append(" <TD>N/A</TD>" + Constants.EOL);
}
else
{
html.append(" <TD>" + getMinValue() + "</TD>" + Constants.EOL);
}
html.append(" <TD>" + decimalFormat.format(getAverageValuePerSecond()) +
"</TD>" + Constants.EOL);
html.append(" <TD>" +
decimalFormat.format(getAverageValuePerInterval()) + "</TD>" +
Constants.EOL);
html.append(" <TD>" + decimalFormat.format(getStandardDeviation()) +
"</TD>" + Constants.EOL);
html.append(" <TD>" + decimalFormat.format(getCorrelationCoefficient()) +
"</TD>" + Constants.EOL);
html.append(" <TD>" + regressionEquation +"</TD>" + Constants.EOL);
html.append(" <TD>" + decimalFormat.format(getHorizontalityTScore()) +
"</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>Total</B></TD>" + Constants.EOL);
html.append(" <TD><B>Count</B></TD>" + Constants.EOL);
html.append(" <TD><B>Average</B></TD>" + Constants.EOL);
html.append(" <TD><B>Avg/Second</B></TD>" + Constants.EOL);
html.append(" </TR>" + Constants.EOL);
for (int i=0; i < countList.size(); i++)
{
int c = countList.get(i);
int t = totalList.get(i);
html.append(" <TR>" + Constants.EOL);
html.append(" <TD>" + (i+1) + "</TD>" + Constants.EOL);
html.append(" <TD>" + t + "</TD>" + Constants.EOL);
html.append(" <TD>" + c + "</TD>" + Constants.EOL);
if (c > 0)
{
html.append(" <TD>" + decimalFormat.format(1.0 * t / c) + "</TD>" +
Constants.EOL);
}
else
{
html.append(" <TD>0.000</TD>" + Constants.EOL);
}
html.append(" <TD>" +
decimalFormat.format(1.0 * t / collectionInterval) + "</TD>" +
Constants.EOL);
html.append(" </TR>" + Constants.EOL);
}
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 Value",
displayName + " Avg Value",
displayName + " Avg/Second",
displayName + " Avg/Interval",
displayName + " Std Dev",
displayName + " Corr Coeff"
};
}
/**
* 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(getTotalValueAsLong()),
String.valueOf(decimalFormat.format(getAverageValue())),
String.valueOf(decimalFormat.format(getAverageValuePerSecond())),
String.valueOf(decimalFormat.format(getAverageValuePerInterval())),
decimalFormat.format(getStandardDeviation()),
decimalFormat.format(getCorrelationCoefficient())
};
}
/**
* 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[countList.size()+1][];
returnArray[0] = new String[] { "Interval", "Total", "Count" };
for (int i=0; i < countList.size(); i++)
{
returnArray[i+1] = new String[]
{
String.valueOf(i+1),
String.valueOf(totalList.get(i)),
String.valueOf(countList.get(i))
};
}
return returnArray;
}
else
{
String[][] returnArray = new String[countList.size()][];
for (int i=0; i < countList.size(); i++)
{
returnArray[i] = new String[]
{
String.valueOf(totalList.get(i)),
String.valueOf(countList.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()
{
// OK. This is cheating because it's not really being encoded in the
// spirit of ASN.1. But the encoded representation is much smaller than
// if I had used sequence of sequence, and this format isn't going to
// be publicly documented anyway.
ASN1Element[] elements = new ASN1Element[(2*countList.size())+2];
elements[0] = new ASN1Integer(maxValue);
elements[1] = new ASN1Integer(minValue);
for (int i=0,j=2; i < countList.size(); i++, j += 2)
{
elements[j] = new ASN1Integer(totalList.get(i));
elements[j+1] = new ASN1Integer(countList.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();
countList = new ArrayList<Integer>();
totalList = new ArrayList<Integer>();
maxValue = elements[0].decodeAsInteger().getIntValue();
minValue = elements[1].decodeAsInteger().getIntValue();
for (int i=2; i < elements.length; i += 2)
{
int intervalTotal = elements[i].decodeAsInteger().getIntValue();
int intervalCount = elements[i+1].decodeAsInteger().getIntValue();
totalList.add(intervalTotal);
countList.add(intervalCount);
}
}
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 includeAverageParameter =
new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_AVERAGE,
"Include Average Line",
"Indicates whether the graph generated should " +
"include a line that shows the average value " +
"for the displayed data set.", false);
BooleanParameter includeRegressionParameter =
new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_REGRESSION,
"Include Regression Line",
"Indicates whether the graph generated should " +
"include a line that shows the calculated " +
"linear regression for the displayed data set " +
"(i.e., a trend line).", 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);
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,
includeAverageParameter,
includeRegressionParameter,
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 includeAverageParameter =
new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_AVERAGE,
"Include Average Line",
"Indicates whether the graph generated should " +
"include a line that shows the average value " +
"for the displayed data set.", false);
BooleanParameter includeRegressionParameter =
new BooleanParameter(Constants.SERVLET_PARAM_INCLUDE_REGRESSION,
"Include Regression Line",
"Indicates whether the graph generated should " +
"include a line that shows the calculated " +
"linear regression for the displayed data set " +
"(i.e., a trend line).", 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,
includeAverageParameter,
includeRegressionParameter,
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)
{
String[] choices = new String[]
{
COMPARE_IN_PARALLEL_STRING,
COMPARE_OVER_TIME_STRING
};
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 drawAsBarParameter =
new BooleanParameter(Constants.SERVLET_PARAM_DRAW_AS_BAR_GRAPH,
"Draw as Bar Graph",
"Indicates whether this graph should be drawn " +
"as a bar graph instead of a line graph.",
(jobs.length > 2));
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);
MultiChoiceParameter comparisonTypeParameter =
new MultiChoiceParameter(Constants.SERVLET_PARAM_COMPARE_TYPE,
"Comparison Type",
"Indicates whether the job information " +
"should be compared in parallel (with " +
"statistics overlayed), or over time (with " +
"each job comprising a single data point).",
choices, choices[0]);
Parameter[] parameters = new Parameter[]
{
// comparisonTypeParameter,
drawAsBarParameter,
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 = getIntervalTotals();
double[] avgTotalsPerSecond = new double[intervalTotals.length];
for (int i=0; i < avgTotalsPerSecond.length; i++)
{
avgTotalsPerSecond[i] = 1.0 * intervalTotals[i] / collectionInterval;
}
return avgTotalsPerSecond;
}
/**
* 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 lable 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 "Average/Second";
}
/**
* 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 includeAverage = false;
boolean includeRegression = 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 includeAverageParameter =
parameters.getBooleanParameter(
Constants.SERVLET_PARAM_INCLUDE_AVERAGE);
if (includeAverageParameter != null)
{
includeAverage = includeAverageParameter.getBooleanValue();
}
BooleanParameter includeRegressionParameter =
parameters.getBooleanParameter(
Constants.SERVLET_PARAM_INCLUDE_REGRESSION);
if (includeRegressionParameter != null)
{
includeRegression = includeRegressionParameter.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;
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))
{
IntegerValueTracker tracker =
new IntegerValueTracker(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);
IntegerValueTracker tracker =
new IntegerValueTracker("", "", 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("Average Value");
grapher.setIncludeHorizontalGrid(includeHGrid);
grapher.setIncludeVerticalGrid(includeVGrid);
grapher.setIgnoreZeroValues(false);
for (int i=0; i < trackersToGraph.length; i++)
{
IntegerValueTracker tracker = (IntegerValueTracker) trackersToGraph[i];
int[] intervalTotals = tracker.getIntervalTotals();
int[] intervalCounts = tracker.getIntervalCounts();
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[] avgTotalsPerInterval = new double[numElements];
for ( ; j < lastIndex; j++)
{
if (intervalCounts[j] == 0)
{
avgTotalsPerInterval[j-subtractor] = Double.NaN;
}
else
{
avgTotalsPerInterval[j-subtractor] = 1.0 * intervalTotals[j] /
intervalCounts[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(avgTotalsPerInterval, job.getCollectionInterval(),
label);
}
grapher.setIncludeAverage(includeAverage);
grapher.setIncludeRegression(includeRegression);
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 includeAverage = false;
boolean includeLegend = false;
boolean includeRegression = false;
boolean excludeFirst = false;
boolean excludeLast = false;
boolean includeHGrid = false;
boolean includeVGrid = false;
boolean baseAtZero = false;
String graphTitle = displayName;
BooleanParameter includeAverageParameter =
parameters.getBooleanParameter(
Constants.SERVLET_PARAM_INCLUDE_AVERAGE);
if (includeAverageParameter != null)
{
includeAverage = includeAverageParameter.getBooleanValue();
}
BooleanParameter includeLegendParameter =
parameters.getBooleanParameter(Constants.SERVLET_PARAM_INCLUDE_LABELS);
if (includeLegendParameter != null)
{
includeLegend = includeLegendParameter.getBooleanValue();
}
BooleanParameter includeRegressionParameter =
parameters.getBooleanParameter(
Constants.SERVLET_PARAM_INCLUDE_REGRESSION);
if (includeRegressionParameter != null)
{
includeRegression = includeRegressionParameter.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[] trackers = job.getResourceStatTrackers(displayName);
IntegerValueTracker tracker =
new IntegerValueTracker("", "", 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("Average Value");
grapher.setIncludeHorizontalGrid(includeHGrid);
grapher.setIncludeVerticalGrid(includeVGrid);
grapher.setIgnoreZeroValues(false);
for (int i=0; i < trackersToGraph.length; i++)
{
tracker = (IntegerValueTracker) trackersToGraph[i];
int[] intervalTotals = tracker.getIntervalTotals();
int[] intervalCounts = tracker.getIntervalCounts();
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[] avgTotalsPerInterval = new double[numElements];
for ( ; j < lastIndex; j++)
{
if (intervalCounts[j] == 0)
{
avgTotalsPerInterval[j-subtractor] = Double.NaN;
}
else
{
avgTotalsPerInterval[j-subtractor] = 1.0 * intervalTotals[j] /
intervalCounts[j];
}
}
grapher.addDataSet(avgTotalsPerInterval, job.getCollectionInterval(),
"Job " + job.getJobID());
}
grapher.setIncludeAverage(includeAverage);
grapher.setIncludeRegression(includeRegression);
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 compareOverTime = false;
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();
}
MultiChoiceParameter compareOverTimeParameter =
parameters.getMultiChoiceParameter(
Constants.SERVLET_PARAM_COMPARE_TYPE);
if (compareOverTimeParameter != null)
{
String compareStr = compareOverTimeParameter.getStringValue();
compareOverTime = compareStr.equalsIgnoreCase(COMPARE_OVER_TIME_STRING);
}
if (compareOverTime)
{
// NYI
return null;
}
else
{
StatTracker[] trackersToGraph = new StatTracker[jobs.length];
for (int i=0; i < jobs.length; i++)
{
StatTracker[] jobTrackers = jobs[i].getStatTrackers(displayName);
IntegerValueTracker jobTracker =
new IntegerValueTracker(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("Average/Second");
grapher.setIncludeHorizontalGrid(includeHGrid);
grapher.setIncludeVerticalGrid(includeVGrid);
grapher.setIgnoreZeroValues(false);
for (int i=0; i < trackersToGraph.length; i++)
{
IntegerValueTracker tracker = (IntegerValueTracker) trackersToGraph[i];
int[] intervalTotals = tracker.getIntervalTotals();
int[] intervalCounts = tracker.getIntervalCounts();
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[] avgTotalsPerInterval = new double[numElements];
for ( ; j < lastIndex; j++)
{
if (intervalCounts[j] == 0)
{
avgTotalsPerInterval[j-subtractor] = Double.NaN;
}
else
{
avgTotalsPerInterval[j-subtractor] = 1.0 * intervalTotals[j] /
intervalCounts[j];
}
}
String label = jobs[i].getJobDescription();
if ((label == null) || (label.length() == 0))
{
label = jobs[i].getJobID();
}
grapher.addDataSet(avgTotalsPerInterval,
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("Average Value");
grapher.setIncludeLegend(false, "");
grapher.setIgnoreZeroValues(false);
int[] intervalTotals = getIntervalTotals();
double[] avgTotalsPerSecond = new double[intervalTotals.length];
for (int i=0; i < intervalTotals.length; i++)
{
avgTotalsPerSecond[i] = 1.0 * intervalTotals[i] / collectionInterval;
}
grapher.addDataSet(avgTotalsPerSecond, collectionInterval, displayName);
return grapher.generateLineGraph();
}
}