/* * 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.server; import java.util.HashMap; import com.slamd.asn1.ASN1Element; import com.slamd.asn1.ASN1Sequence; import com.slamd.common.Constants; import com.slamd.db.SLAMDDB; import com.slamd.job.JobClass; import com.slamd.message.RegisterStatisticMessage; import com.slamd.message.ReportStatisticMessage; /** * This class implements a mechanism for handling statistical data reported to * the SLAMD server in real time. It aggregates the data reported by all the * clients and can make it available for display to end users on request. * * * @author Neil A. Wilson */ public class RealTimeStatHandler { // The maximum number of collection intervals that should be maintained. protected int maxIntervals; // The hash map that associates statistical data with the corresponding job. private HashMap<String,RealTimeJobStats> statHash; // The mutex used to provide threadsafe access to the stat hash. private final Object statHashMutex; // The configuration database that stores the configuration for this stat // handler. private SLAMDDB configDB; // The SLAMD server with which this stat handler is associated. private SLAMDServer slamdServer; /** * Creates a new instance of this real-time stat handler. * * @param slamdServer The SLAMD server with which this stat handler is * associated. */ public RealTimeStatHandler(SLAMDServer slamdServer) { this.slamdServer = slamdServer; statHash = new HashMap<String,RealTimeJobStats>(); statHashMutex = new Object(); maxIntervals = Constants.DEFAULT_MAX_STAT_INTERVALS; configDB = slamdServer.getConfigDB(); String intervalsStr = configDB.getConfigParameter(Constants.PARAM_MAX_STAT_INTERVALS); if ((intervalsStr != null) && (intervalsStr.length() > 0)) { try { maxIntervals = Integer.parseInt(intervalsStr); } catch (Exception e) { slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(e)); slamdServer.logMessage(Constants.LOG_LEVEL_CONFIG, "Invalid value for config parameter " + Constants.PARAM_MAX_STAT_INTERVALS + ": " + intervalsStr + " -- " + e); } } } /** * Retrieves the reference to the SLAMD server with which this stat handler * is associated. * * @return The reference to the SLAMD server with which this stat handler is * associated. */ public SLAMDServer getSLAMDServer() { return slamdServer; } /** * Handles the work of registering a client with the SLAMD server for the * purpose of real-time statistics reporting. * * @param message The message containing the information about the client * being registered. */ public void handleRegisterStatMessage(RegisterStatisticMessage message) { String jobID = message.getJobID(); String statName = message.getDisplayName(); synchronized (statHashMutex) { RealTimeJobStats jobStats = statHash.get(jobID); if (jobStats == null) { try { jobStats = new RealTimeJobStats(this, jobID, maxIntervals); statHash.put(jobID, jobStats); } catch (SLAMDServerException sse) { // What to do here? slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(sse)); slamdServer.logMessage(Constants.LOG_LEVEL_JOB_PROCESSING, "Stat handler asked to register statistics " + "for unknown job " + jobID); return; } } jobStats.registerStatistic(statName); } } /** * Handles the work of processing the statistical data contained in the * provided message and making it available to the server. * * @param message The message containing the statistical data to be * reported. */ public void handleReportStatMessage(ReportStatisticMessage message) { String jobID = message.getJobID(); ASN1Sequence[] dataSequences = message.getDataSequences(); synchronized (statHashMutex) { RealTimeJobStats jobStats = statHash.get(jobID); if (jobStats == null) { // What to do here? slamdServer.logMessage(Constants.LOG_LEVEL_JOB_PROCESSING, "Stat handler asked to report statistics " + "for unregistered job " + jobID); return; } for (int i=0; i < dataSequences.length; i++) { try { ASN1Element[] dataElements = dataSequences[i].getElements(); // These are currently unused. // String clientID = // dataElements[0].decodeAsOctetString().getStringValue(); // String threadID = // dataElements[1].decodeAsOctetString().getStringValue(); String statName = dataElements[2].decodeAsOctetString().getStringValue(); // Oops. There is a bug in the way that stat done messages are // encoded, in that they do not include an interval number. This // screws up parsing, so it is necessary to detect that (because the // interval number and stat type have different ASN.1 types) and work // around the problem. int intervalNum = -1; int statType; if (dataElements[3].getType() == ASN1Element.ASN1_ENUMERATED_TYPE) { statType = dataElements[3].decodeAsEnumerated().getIntValue(); } else { intervalNum = dataElements[3].decodeAsInteger().getIntValue(); statType = dataElements[4].decodeAsEnumerated().getIntValue(); } switch (statType) { case Constants.STAT_REPORT_TYPE_ADD: double statValue = Double.parseDouble(dataElements[5].decodeAsOctetString(). getStringValue()); jobStats.updateStatToAdd(statName, intervalNum, statValue); break; case Constants.STAT_REPORT_TYPE_AVERAGE: statValue = Double.parseDouble(dataElements[5].decodeAsOctetString(). getStringValue()); jobStats.updateStatToAverage(statName, intervalNum, statValue); break; case Constants.STAT_REPORT_TYPE_DONE: jobStats.deregisterStatistic(); break; } } catch (Exception e) { // What to do here? slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(e)); slamdServer.logMessage(Constants.LOG_LEVEL_JOB_PROCESSING, "Stat handler encountered error while " + "processing data: " + e); } } jobStats.setLastUpdateTime(); } } /** * Removes the real-time stat data for the specified job from this stat * handler. Note that this method does not do any locking and assumes that * the stat hash lock is already held by the thread calling this method. * * @param jobID The job ID of the job for which to remove the data from this * stat handler. */ public void removeJobStatsUnlocked(String jobID) { statHash.remove(jobID); } }