/*
* 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.job;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.StringTokenizer;
import com.slamd.asn1.ASN1Boolean;
import com.slamd.asn1.ASN1Element;
import com.slamd.asn1.ASN1Integer;
import com.slamd.asn1.ASN1OctetString;
import com.slamd.asn1.ASN1Sequence;
import com.slamd.common.Constants;
import com.slamd.common.SLAMDException;
import com.slamd.db.DecodeException;
import com.slamd.message.JobCompletedMessage;
import com.slamd.message.JobControlResponseMessage;
import com.slamd.message.JobResponseMessage;
import com.slamd.parameter.LabelParameter;
import com.slamd.parameter.Parameter;
import com.slamd.parameter.ParameterList;
import com.slamd.parameter.PlaceholderParameter;
import com.slamd.parameter.StringParameter;
import com.slamd.resourcemonitor.LegacyResourceMonitor;
import com.slamd.resourcemonitor.ResourceMonitor;
import com.slamd.server.ClientConnection;
import com.slamd.server.RealTimeJobStats;
import com.slamd.server.ResourceMonitorClientConnection;
import com.slamd.server.SLAMDServer;
import com.slamd.stat.ResourceMonitorStatTracker;
import com.slamd.stat.StatEncoder;
import com.slamd.stat.StatTracker;
/**
* This class defines a job that can be run by the SLAMD server. Most of the
* information in this class is used for administrative purposes. All of the
* actual processing is defined in the job class associated with each job
* implementation -- this class merely handles starting the appropriate number
* of job threads on the appropriate set of clients.
*
*
* @author Neil A. Wilson
*/
public class Job
implements Comparable, JobItem
{
/**
* The name of the encoded element that holds the job ID.
*/
public static final String ELEMENT_JOB_ID = "job_id";
/**
* The name of the encoded element that holds the job class name.
*/
public static final String ELEMENT_JOB_CLASS = "job_class";
/**
* The name of the encoded element that holds the optimizing job ID.
*/
public static final String ELEMENT_OPTIMIZING_JOB_ID = "optimizing_job_id";
/**
* The name of the encoded element that holds the job group name.
*/
public static final String ELEMENT_JOB_GROUP = "job_group";
/**
* The name of the encoded element that holds the folder name.
*/
public static final String ELEMENT_FOLDER = "folder";
/**
* The name of the encoded element that holds the job state.
*/
public static final String ELEMENT_JOB_STATE = "job_state";
/**
* The name of the encoded element that indicates whether this job should be
* displayed in restricted read-only mode.
*/
public static final String ELEMENT_DISPLAY_IN_READ_ONLY =
"display_in_read_only";
/**
* The name of the encoded element that holds the job description.
*/
public static final String ELEMENT_DESCRIPTION = "description";
/**
* The name of the encoded element that holds the scheduled start time.
*/
public static final String ELEMENT_START_TIME = "start_time";
/**
* The name of the encoded element that holds the scheduled stop time.
*/
public static final String ELEMENT_STOP_TIME = "stop_time";
/**
* The name of the encoded element that holds the scheduled duration.
*/
public static final String ELEMENT_DURATION = "duration";
/**
* The name of the encoded element that holds the number of clients.
*/
public static final String ELEMENT_NUM_CLIENTS = "num_clients";
/**
* The name of the encoded element that holds the requested clients.
*/
public static final String ELEMENT_REQUESTED_CLIENTS = "requested_clients";
/**
* The name of the encoded element that holds the requested resource monitor
* clients.
*/
public static final String ELEMENT_MONITOR_CLIENTS = "monitor_clients";
/**
* The name of the encoded element that indicates whether the clients should
* be monitored if they are running resource monitor clients.
*/
public static final String ELEMENT_MONITOR_CLIENTS_IF_AVAILABLE =
"monitor_clients_if_available";
/**
* The name of the encoded element that indicates whether to wait for clients
* to become available.
*/
public static final String ELEMENT_WAIT_FOR_CLIENTS = "wait_for_clients";
/**
* The name of the encoded element that holds the number of threads per
* client.
*/
public static final String ELEMENT_THREADS_PER_CLIENT = "threads_per_client";
/**
* The name of the encoded element that holds the thread startup delay.
*/
public static final String ELEMENT_THREAD_STARTUP_DELAY =
"thread_startup_delay";
/**
* The name of the encoded element that holds the set of dependencies.
*/
public static final String ELEMENT_DEPENDENCIES = "dependencies";
/**
* The name of the encoded element that holds the e-mail addresses of the
* users to notify on completion.
*/
public static final String ELEMENT_NOTIFY_ADDRESSES = "notify_addresses";
/**
* The name of the encoded element that holds the statistics collection
* interval.
*/
public static final String ELEMENT_COLLECTION_INTERVAL =
"collection_interval";
/**
* The name of the encoded element that holds the job comments.
*/
public static final String ELEMENT_COMMENTS = "comments";
/**
* The name of the encoded element that holds the job parameters.
*/
public static final String ELEMENT_PARAMETERS = "parameters";
/**
* The name of the encoded element that holds the actual start time.
*/
public static final String ELEMENT_ACTUAL_START_TIME = "actual_start_time";
/**
* The name of the encoded element that holds the actual stop time.
*/
public static final String ELEMENT_ACTUAL_STOP_TIME = "actual_stop_time";
/**
* The name of the encoded element that holds the actual duration.
*/
public static final String ELEMENT_ACTUAL_DURATION = "actual_duration";
/**
* The name of the encoded element that holds the job statistics.
*/
public static final String ELEMENT_STATS = "stats";
/**
* The name of the encoded element that holds legacy resource monitor
* statistical data (just stat trackers, not resource monitor stats).
*/
public static final String ELEMENT_LEGACY_MONITOR_STATS = "monitor_stats";
/**
* The name of the encoded element that holds resource monitor statistics.
*/
public static final String ELEMENT_RESOURCE_MONITOR_STATS = "resource_stats";
/**
* The name of the encoded element that holds the log messages.
*/
public static final String ELEMENT_LOG_MESSAGES = "log_messages";
// The set of clients that are actively processing this job.
private ArrayList<ClientConnection> activeClients;
// The set of resource monitor clients that are currently active.
private ArrayList<ResourceMonitorClientConnection> activeMonitorClients;
// The set of job IDs that must complete before this job may begin.
private ArrayList<String> dependencies;
// The set of messages logged during job execution.
private ArrayList<String> logMessages;
// The set of stat trackers maintained by the resource monitor clients.
private ArrayList<ResourceMonitorStatTracker> resourceStatTrackers;
// The set of stat trackers associated with this job.
private ArrayList<StatTracker> statTrackers;
// Indicates whether this job should be displayed in restricted read-only
// mode.
private boolean displayInReadOnlyMode;
// Indicates whether an attempt should be made to use any resource monitor
// clients running on the same system as the client system(s) used to process
// this job.
private boolean monitorClientsIfAvailable;
// Indicates whether the execution of this job will be delayed until the
// requested number of clients are available, or if execution should be
// cancelled if the requested number of clients are not available.
private boolean waitForClients;
// The time that the job actually started running.
private Date actualStartTime;
// The time that the job actually stopped running.
private Date actualStopTime;
// The time at which this job should start running.
private Date startTime;
// The time at which this job should stop running.
private Date stopTime;
// The actual length of time that the job was active.
private int actualDuration;
// The length of time in seconds to use as the statistics collection interval.
private int collectionInterval;
// The maximum length of time in seconds that the job should be allowed to
// run.
private int duration;
// Indicates the current state of the job. The value should be one of the
// JOB_STATE_* constants.
private int jobState;
// The number of clients that should be used to execute this job.
private int numClients;
// The job state that will be assigned to the job when it has completed
// processing. This will default to "completed successfully", but will change
// to reflect any other stop reason.
private int tentativeJobState;
// The number of concurrent job threads that should run on each client.
private int threadsPerClient;
// The delay in milliseconds that should be used when starting each thread on
// the client.
private int threadStartupDelay;
// The mutex used to provide threadsafe access to the list of active clients.
private final Object activeClientMutex = new Object();
// The set of parameters that can customize the behavior of the job.
private ParameterList parameters;
// The set of client connections associated with the clients that are running
// this job.
private ClientConnection[] clientConnections;
// The real-time stats associated with this job.
private RealTimeJobStats realTimeStats;
// The set of connections to the resource monitor clients for this job.
private ResourceMonitorClientConnection[] monitorConnections;
// The job thread that will be used to obtain information about the actual
// work to be performed.
private JobClass infoJobThread;
// The SLAMD server with which this job is associated
protected SLAMDServer slamdServer;
// The name of the folder in which this job is located.
private String folderName;
// Comments about the job.
private String jobComments;
// A user-specified description to use for the job.
private String jobDescription;
// The name of the job group with which this job is associated.
private String jobGroup;
// The unique ID assigned to this job
private String jobID;
// The name of the Java class file that serves as the job thread
private String jobThreadClassName;
// The job ID of the optimizing job with which this job is associated.
private String optimizingJobID;
// The addresses of the users that should be notified when this job is
// complete.
private String[] notifyAddresses;
// The set of resource monitor clients that have been requested for this job.
private String[] monitorClients;
// The set of clients that have been requested to run this job.
private String[] requestedClients;
/**
* Creates a new instance of the job based on the specified job thread class.
* This version of the constructor is to be used only for the purpose of
* obtaining information about the job -- not for actually running it.
*
* @param slamdServer The SLAMD server with which this job is
* associated.
* @param jobThreadClassName The fully-qualified name of the Java class that
* will actually be invoked to perform the work of
* this job.
*
* @throws SLAMDException If it is not possible to create an instance of the
* job class.
*/
public Job(SLAMDServer slamdServer, String jobThreadClassName)
throws SLAMDException
{
// Assign the values of the parameters to instance variables
this.slamdServer = slamdServer;
this.jobThreadClassName = jobThreadClassName;
// This is a fallback condition that will be used if this constructor can't
// complete without an exception. If that occurs, then the job won't be
// able to run.
jobState = Constants.JOB_STATE_STOPPED_DUE_TO_ERROR;
infoJobThread = slamdServer.getOrLoadJobClass(jobThreadClassName);
// Indicate that enough of the constructor has completed so that we won't
// have stopped due to error.
jobState = Constants.JOB_STATE_UNINITIALIZED;
}
/**
* Creates a new instance of the job based on the specified job thread class
* that may be actually used to run the job.
*
* @param slamdServer The SLAMD server with which this job is
* associated.
* @param jobThreadClassName The fully-qualified name of the Java class
* that will actually be invoked to perform the
* work of this job.
* @param numClients The number of clients that should be used to
* execute the job.
* @param threadsPerClient The number of concurrent job thread
* instances that will run on each client.
* @param threadStartupDelay The delay in milliseconds that should be
* used when starting each thread on the client
* system.
* @param startTime The time at which the job should start
* running.
* @param stopTime The time at which the job should stop
* running.
* @param duration The maximum length of time in seconds that
* the job should be allowed to run.
* @param collectionInterval The length of time in seconds to use as the
* statistics collection interval.
* @param parameters The parameter list that provides the details
* of how the job should run.
* @param displayInReadOnlyMode Indicates whether this job should be
* displayed in restricted read-only mode.
*
* @throws SLAMDException If a problem occurred while creating an instance
* of the job thread class.
*/
public Job(SLAMDServer slamdServer, String jobThreadClassName,
int numClients, int threadsPerClient, int threadStartupDelay,
Date startTime, Date stopTime, int duration,
int collectionInterval, ParameterList parameters,
boolean displayInReadOnlyMode)
throws SLAMDException
{
// Invoke the first version of the constructor
this(slamdServer, jobThreadClassName);
// Set all of the other instance variables
this.numClients = numClients;
this.threadsPerClient = threadsPerClient;
this.threadStartupDelay = threadStartupDelay;
this.startTime = startTime;
this.stopTime = stopTime;
this.duration = duration;
this.collectionInterval = collectionInterval;
this.parameters = parameters;
this.displayInReadOnlyMode = displayInReadOnlyMode;
waitForClients = false;
optimizingJobID = null;
jobComments = null;
jobDescription = null;
actualStartTime = null;
actualStopTime = null;
actualDuration = -1;
notifyAddresses = new String[0];
dependencies = new ArrayList<String>();
monitorClientsIfAvailable = false;
monitorClients = null;
requestedClients = null;
statTrackers = new ArrayList<StatTracker>();
resourceStatTrackers = new ArrayList<ResourceMonitorStatTracker>();
tentativeJobState = Constants.JOB_STATE_COMPLETED_SUCCESSFULLY;
logMessages = new ArrayList<String>();
// Create the variables for working with the list of clients that are
// actively processing this job.
activeClients = new ArrayList<ClientConnection>();
activeMonitorClients = new ArrayList<ResourceMonitorClientConnection>();
jobState = Constants.JOB_STATE_NOT_YET_STARTED;
}
/**
* Retrieves an instance of the job class with which this job is associated.
*
* @return An instance of the job class with which this job is associated,
* or <CODE>null</CODE> if it could not be retrieved for some reason.
*/
public JobClass getJobClass()
{
if (infoJobThread != null)
{
return infoJobThread;
}
try
{
return slamdServer.getOrLoadJobClass(jobThreadClassName);
}
catch (SLAMDException se)
{
return null;
}
}
/**
* Retrieves the job ID associated with this job.
*
* @return The job ID associated with this job.
*/
public String getJobID()
{
return jobID;
}
/**
* Sets the job ID for this job.
*
* @param jobID The job ID to use for this job.
*/
public void setJobID(String jobID)
{
this.jobID = jobID;
}
/**
* Retrieves the ID of the optimizing job with which this job is associated.
*
* @return The ID of the optimizing job with which this job is associated, or
* <CODE>null</CODE> if it is not associated with an optimizing job.
*/
public String getOptimizingJobID()
{
return optimizingJobID;
}
/**
* Specifies the ID of the optimizing job with which this job is associated.
*
* @param optimizingJobID The ID of the optimizing job with which this job
* is associated.
*/
public void setOptimizingJobID(String optimizingJobID)
{
this.optimizingJobID = optimizingJobID;
}
/**
* Retrieves the name of the job group with which this job is associated.
*
* @return The name of the job group with which this job is associated, or
* <CODE>null</CODE> if it was not scheduled as part of any job
* group.
*/
public String getJobGroup()
{
return jobGroup;
}
/**
* Specifies the name of the job group with which this job is associated.
*
* @param jobGroup The name of the job group with which this job is
* associated.
*/
public void setJobGroup(String jobGroup)
{
this.jobGroup = jobGroup;
}
/**
* Retrieves the name of the folder in which this job is located.
*
* @return The name of the folder in which this job is located.
*/
public String getFolderName()
{
return folderName;
}
/**
* Specifies the name of the folder in which this job is located.
*
* @param folderName The name of the folder in which this job is located.
*/
public void setFolderName(String folderName)
{
this.folderName = folderName;
}
/**
* Retrieves the user-specified comments for this job.
*
* @return The user-specified comments for this job, or <CODE>null</CODE> if
* no comments have been made.
*/
public String getJobComments()
{
return jobComments;
}
/**
* Specifies a set of comments for this job.
*
* @param jobComments The set of comments to use for this job.
*/
public void setJobComments(String jobComments)
{
this.jobComments = jobComments;
}
/**
* Retrieves a user-specified description for this job.
*
* @return A description for this job, or <CODE>null</CODE> if no description
* has been provided.
*/
public String getJobDescription()
{
return jobDescription;
}
/**
* Specifies the description that should be used for this job.
*
* @param jobDescription The job description that should be used for this
* job.
*/
public void setJobDescription(String jobDescription)
{
this.jobDescription = jobDescription;
}
/**
* Retrieves the name of the Java class that this job will execute to perform
* the work.
*
* @return The name of the Java class that this job will execute to perform
* the work.
*/
public String getJobClassName()
{
return jobThreadClassName;
}
/**
* Retrieves the time that this job should start.
*
* @return The time that this job should start.
*/
public Date getStartTime()
{
return startTime;
}
/**
* Specifies the start time to use for the job.
*
* @param startTime The start time to use for the job.
*/
public void setStartTime(Date startTime)
{
this.startTime = startTime;
}
/**
* Retrieves the time that this job should stop.
*
* @return The time that this job should start.
*/
public Date getStopTime()
{
return stopTime;
}
/**
* Specifies the stop time to use for this job.
*
* @param stopTime The stop time to use for this job.
*/
public void setStopTime(Date stopTime)
{
this.stopTime = stopTime;
}
/**
* Retrieves the maximum amount of time that this job should run.
*
* @return The maximum amount of time that this job should run.
*/
public int getDuration()
{
return duration;
}
/**
* Specifies the duration to use for the job.
*
* @param duration The duration to use for the job.
*/
public void setDuration(int duration)
{
this.duration = duration;
}
/**
* Retrieves the name of this job.
*
* @return The name of this job.
*/
public String getJobName()
{
return infoJobThread.getJobName();
}
/**
* Retrieves a description of the job class.
*
* @return A description of the job class.
*/
public String getJobClassDescription()
{
return infoJobThread.getShortDescription();
}
/**
* Retrieves the number of clients that should be used to run this job.
*
* @return The number of clients that should be used to run this job.
*/
public int getNumberOfClients()
{
return numClients;
}
/**
* Specifies the number of clients that should be used to run this job.
*
* @param numberOfClients The number of clients that should be used to run
* this job.
*/
public void setNumberOfClients(int numberOfClients)
{
this.numClients = numberOfClients;
}
/**
* Retrieves the number of threads that each client should use to run this
* job.
*
* @return The number of threads that each client should use to run this job.
*/
public int getThreadsPerClient()
{
return threadsPerClient;
}
/**
* Specifies the number of threads that each client should use to run this
* job.
*
* @param threadsPerClient The number of threads that each client should use
* to run this job.
*/
public void setThreadsPerClient(int threadsPerClient)
{
this.threadsPerClient = threadsPerClient;
}
/**
* Retrieves the delay in milliseconds that should be used when starting each
* thread on the client system.
*
* @return The delay in milliseconds that should be used when starting each
* thread on the client system.
*/
public int getThreadStartupDelay()
{
return threadStartupDelay;
}
/**
* Specifies the delay in milliseconds that should be used when starting each
* thread on the client system.
*
* @param threadStartupDelay The delay in milliseconds that should be used
* when starting each thread on the client system.
*/
public void setThreadStartupDelay(int threadStartupDelay)
{
this.threadStartupDelay = threadStartupDelay;
}
/**
* Retrieves the length of time in seconds that should be used as the
* statistics collection interval.
*
* @return The length of time in seconds that should be used as the
* statistics collection interval.
*/
public int getCollectionInterval()
{
return collectionInterval;
}
/**
* Specifies the length of time in seconds that should be used as the
* statistics collection interval.
*
* @param collectionInterval The length of time in seconds that should be
* used as the statistics collection interval.
*/
public void setCollectionInterval(int collectionInterval)
{
this.collectionInterval = collectionInterval;
}
/**
* Retrieves a list of all the parameters that can be used to configure the
* behavior of this job.
*
* @return A list of all the parameters that can be used to configure the
* behavior of this job.
*/
public ParameterList getParameterStubs()
{
return infoJobThread.getParameterStubs().clone();
}
/**
* Retrieves a list of all the parameters that can be used to configure the
* behavior of this job.
*
* @return A list of all the parameters that can be used to configure the
* behavior of this job.
*/
public ParameterList getClientSideParameterStubs()
{
return infoJobThread.getClientSideParameterStubs().clone();
}
/**
* Retrieves a list of the parameters that have been configured for this job.
* This will not be the parameter stubs, but rather the actual parameters set
* that the user has specified.
*
* @return A list of the parameters that have been configured for this job.
*/
public ParameterList getParameterList()
{
return parameters;
}
/**
* Specifies the list of parameters to use for this job.
*
* @param parameters The list of parameters to use for this job.
*/
public void setParameterList(ParameterList parameters)
{
this.parameters = parameters;
}
/**
* Retrieves the state of the current job. The return value should be that of
* one of the JOB_STATE_* constants.
*
* @return The state of the current job.
*/
public int getJobState()
{
return jobState;
}
/**
* Specifies the state of the current job. The provided state value should be
* one of the JOB_STATE_* constants.
*
* @param jobState The state to use for the current job.
*/
public void setJobState(int jobState)
{
this.jobState = jobState;
}
/**
* Retrieves a string description of the current job state.
*
* @return A string description of the current job state.
*/
public String getJobStateString()
{
return Constants.jobStateToString(jobState);
}
/**
* Indicates whether this job will wait for the appropriate number of
* available clients to start running, or whether it will be cancelled if
* there are not enough clients available when the start time arrives.
*
* @return <CODE>true</CODE> if this job will wait for the requested number
* of clients before starting, or <CODE>false</CODE> if not.
*/
public boolean waitForClients()
{
return waitForClients;
}
/**
* Specifies whether this job will wait for the appropriate number of
* available clients to start running, or whether it will be cancelled if
* there are not enough clients available when the start time arrives.
*
* @param waitForClients Indicates whether the job will wait for clients to
* be available.
*/
public void setWaitForClients(boolean waitForClients)
{
this.waitForClients = waitForClients;
}
/**
* Indicates whether this job should be displayed in restricted read-only
* mode.
*
* @return <CODE>true</CODE> if this job should be displayed in restricted
* read-only mode, or <CODE>false</CODE> if not.
*/
public boolean displayInReadOnlyMode()
{
return displayInReadOnlyMode;
}
/**
* Specifies whether this job should be displayed in restricted read-only
* mode.
*
* @param displayInReadOnlyMode Indicates whether this job should be
* displayed in restricted read-only mode.
*/
public void setDisplayInReadOnlyMode(boolean displayInReadOnlyMode)
{
this.displayInReadOnlyMode = displayInReadOnlyMode;
}
/**
* Retrieves the job IDs of the jobs that must complete before this job will
* be eligible for processing.
*
* @return The job IDs of the jobs that must complete before this job will be
* eligible for processing, or <CODE>null</CODE> if there are no such
* dependencies.
*/
public String[] getDependencies()
{
if (dependencies.isEmpty())
{
return null;
}
String[] dependencyArray = new String[dependencies.size()];
dependencies.toArray(dependencyArray);
return dependencyArray;
}
/**
* Specifies the job IDs of the jobs that must complete before this job will
* be eligible for processing.
*
* @param dependencies The job IDs of the jobs that must complete before
* this job will be eligible for processing.
*/
public void setDependencies(String[] dependencies)
{
this.dependencies.clear();
for (int i=0; ((dependencies != null) && (i < dependencies.length)); i++)
{
if ((dependencies[i] != null) && (dependencies[i].length() > 0))
{
this.dependencies.add(dependencies[i]);
}
}
}
/**
* Removes the specified job as a dependency of this job.
*
* @param jobID The job ID for the job to remove as a dependency of this
* job.
*/
public void removeDependency(String jobID)
{
for (int i=0; i < dependencies.size(); i++)
{
String dependency = dependencies.get(i);
if (dependency.equalsIgnoreCase(jobID))
{
dependencies.remove(i);
break;
}
}
}
/**
* Retrieves the set of clients that have been requested to run this job.
*
* @return The set of clients that have been requested to run this job.
*/
public String[] getRequestedClients()
{
return requestedClients;
}
/**
* Specifies the set of clients that have been requested to run this job.
*
* @param requestedClients The set of clients that have been requested to
* run this job.
*/
public void setRequestedClients(String[] requestedClients)
{
this.requestedClients = requestedClients;
}
/**
* Retrieves the set of resource monitor clients that have been requested for
* this job.
*
* @return The set of resource monitor clients that have been requested for
* this job.
*/
public String[] getResourceMonitorClients()
{
return monitorClients;
}
/**
* Specifies the set of resource monitor clients that should be used when
* running this job.
*
* @param monitorClients The set of resource monitor clients that should be
* used when running this job.
*/
public void setResourceMonitorClients(String[] monitorClients)
{
this.monitorClients = monitorClients;
}
/**
* Indicates whether an attempt will be made to use resource monitor clients
* on the same systems as the clients used to run this job.
*
* @return <CODE>true</CODE> if resource monitor clients on the same systems
* as the clients will be used, or <CODE>false</CODE> if not.
*/
public boolean monitorClientsIfAvailable()
{
return monitorClientsIfAvailable;
}
/**
* Specifies whether an attempt should be made to use resource monitor clients
* on the same systems as the clients used to run this job.
*
* @param monitorClientsIfAvailable Specifies whether an attempt should be
* made to use resource monitor clients
* on the same systems as the clients used
* to run this job.
*/
public void setMonitorClientsIfAvailable(boolean monitorClientsIfAvailable)
{
this.monitorClientsIfAvailable = monitorClientsIfAvailable;
}
/**
* Retrieves the e-mail addresses of the users that should be notified when
* this job has completed running.
*
* @return The e-mail addresses of the users that should be notified when
* this job has completed running.
*/
public String[] getNotifyAddresses()
{
return notifyAddresses;
}
/**
* Specifies the e-mail addresses of the users that should be notified when
* this job has completed running.
*
* @param notifyAddresses The e-mail addresses of the users that should be
* notified when this job has completed running.
*/
public void setNotifyAddresses(String[] notifyAddresses)
{
if (notifyAddresses == null)
{
this.notifyAddresses = new String[0];
}
else
{
this.notifyAddresses = notifyAddresses;
}
}
/**
* Indicates whether all possible processing has been done for this job.
*
* @return <CODE>true</CODE> if there is no more processing to be done, or
* <CODE>false</CODE> if the job is still running or pending
* execution.
*/
public boolean doneRunning()
{
return ((jobState != Constants.JOB_STATE_NOT_YET_STARTED) &&
(jobState != Constants.JOB_STATE_DISABLED) &&
(jobState != Constants.JOB_STATE_RUNNING));
}
/**
* Indicates whether enough information is available for this job to generate
* graphs of the results.
*
* @return <CODE>true</CODE> if graphs are available, or <CODE>false</CODE>
* if not.
*/
public boolean graphsAvailable()
{
for (int i=0; i < statTrackers.size(); i++)
{
StatTracker tracker = statTrackers.get(i);
if (tracker.getNumIntervals() > 1)
{
return true;
}
}
return false;
}
/**
* Indicates whether enough information is available for this job to generate
* graphs of the resource monitor statistics.
*
* @return <CODE>true</CODE> if graphs are available, or <CODE>false</CODE>
* if not.
*/
public boolean resourceGraphsAvailable()
{
for (int i=0; i < resourceStatTrackers.size(); i++)
{
ResourceMonitorStatTracker monitorTracker = resourceStatTrackers.get(i);
if (monitorTracker.getStatTracker().getNumIntervals() > 1)
{
return true;
}
}
return false;
}
/**
* Indicates whether this job has real-time stat data associated with it.
*
* @return <CODE>true</CODE> if this job has real-time stat data associated
* with it, or <CODE>false</CODE> if not.
*/
public boolean hasRealTimeStats()
{
return (realTimeStats != null);
}
/**
* Specifies the real-time stat data associated with this job.
*
* @param realTimeStats The real-time stat data associated with this job.
*/
public void setRealTimeStats(RealTimeJobStats realTimeStats)
{
this.realTimeStats = realTimeStats;
}
/**
* Retrieves the real-time stat data associated with this job.
*
* @return The real-time stat data associated with this job, or
* <CODE>null</CODE> if no real-time stat data is being collected.
*/
public RealTimeJobStats getRealTimeStats()
{
return realTimeStats;
}
/**
* Indicates whether this job has statistical information associated with it.
*
* @return <CODE>true</CODE> if this job has statistical information
* associated with it, or <CODE>false</CODE> if not.
*/
public boolean hasStats()
{
return (! statTrackers.isEmpty());
}
/**
* Indicates whether this job has resource monitor information associated with
* it.
*
* @return <CODE>true</CODE> if this job has resource monitor information
* associated with it, or <CODE>false</CODE> if not.
*/
public boolean hasResourceStats()
{
return (! resourceStatTrackers.isEmpty());
}
/**
* Retrieves the display names of all the stat trackers associated with this
* job.
*
* @return The names of all stat trackers associated with this job.
*/
public String[] getStatTrackerNames()
{
// Although we could get the list of names from the job thread, it is better
// to see the names that we actually have, in case the job thread is not
// giving us the full or correct list (e.g., in the case of scripted jobs).
ArrayList<String> trackerNames = new ArrayList<String>();
for (int i=0; i < statTrackers.size(); i++)
{
String name = statTrackers.get(i).getDisplayName();
boolean matchFound = false;
for (int j=0; j < trackerNames.size(); j++)
{
if (name.equalsIgnoreCase(trackerNames.get(j)))
{
matchFound = true;
break;
}
}
if (! matchFound)
{
trackerNames.add(name);
}
}
String[] names = new String[trackerNames.size()];
trackerNames.toArray(names);
return names;
}
/**
* Retrieves the display names of all the resource monitor stat trackers
* associated with this job.
*
* @return The names of all the resource monitor stat trackers associated
* with this job.
*/
public String[] getResourceStatTrackerNames()
{
ArrayList<String> trackerNames = new ArrayList<String>();
for (int i=0; i < resourceStatTrackers.size(); i++)
{
ResourceMonitorStatTracker monitorTracker = resourceStatTrackers.get(i);
String name = monitorTracker.getStatTracker().getDisplayName();
boolean matchFound = false;
for (int j=0; j < trackerNames.size(); j++)
{
if (name.equalsIgnoreCase(trackerNames.get(j)))
{
matchFound = true;
break;
}
}
if (! matchFound)
{
trackerNames.add(name);
}
}
String[] names = new String[trackerNames.size()];
trackerNames.toArray(names);
return names;
}
/**
* Retrieves the display names of all the resource monitor stat trackers
* associated with this job, in a map that links those names to the names of
* the associated resource monitor class.
*
* @return The display names of all the resource monitor stat trackers
* associated with this job, in a map that links those names to the
* names of the associated resource monitor class.
*/
public LinkedHashMap getResourceStatTrackerNamesAndClasses()
{
LinkedHashMap<String,String> trackerMap =
new LinkedHashMap<String,String>();
for (int i=0; i < resourceStatTrackers.size(); i++)
{
ResourceMonitorStatTracker monitorTracker = resourceStatTrackers.get(i);
String displayName = monitorTracker.getStatTracker().getDisplayName();
String className =
monitorTracker.getResourceMonitor().getClass().getName();
trackerMap.put(displayName, className);
}
return trackerMap;
}
/**
* Retrieves the set of all stat trackers associated with this job.
*
* @return The set of all stat trackers associated with this job.
*/
public StatTracker[] getStatTrackers()
{
StatTracker[] trackerArray = new StatTracker[statTrackers.size()];
statTrackers.toArray(trackerArray);
return trackerArray;
}
/**
* Retrieves the set of all resource monitor stat trackers associated with
* this job.
*
* @return The set of all resource monitor stat trackers associated with this
* job.
*/
public ResourceMonitorStatTracker[] getResourceMonitorStatTrackers()
{
ResourceMonitorStatTracker[] trackerArray =
new ResourceMonitorStatTracker[resourceStatTrackers.size()];
resourceStatTrackers.toArray(trackerArray);
return trackerArray;
}
/**
* Retrieves the set of all stat trackers associated with this job that have
* the specified display name.
*
* @param displayName The display name for the stat trackers to be
* retrieved.
*
* @return The set of all stat trackers associated with this job that have
* the specified display name.
*/
public StatTracker[] getStatTrackers(String displayName)
{
ArrayList<StatTracker> trackerList = new ArrayList<StatTracker>();
for (int i=0; i < statTrackers.size(); i++)
{
StatTracker tracker = statTrackers.get(i);
if (displayName.equals(tracker.getDisplayName()))
{
trackerList.add(tracker);
}
}
StatTracker[] trackerArray = new StatTracker[trackerList.size()];
trackerList.toArray(trackerArray);
return trackerArray;
}
/**
* Retrieves the set of all resource monitor stat trackers associated with
* this job that have the specified display name.
*
* @param displayName The display name for the resource stat trackers to be
* retrieved.
*
* @return The set of all resource stat trackers associated with this job
* that have the specified display name.
\ */
public StatTracker[] getResourceStatTrackers(String displayName)
{
ArrayList<StatTracker> trackerList = new ArrayList<StatTracker>();
for (int i=0; i < resourceStatTrackers.size(); i++)
{
ResourceMonitorStatTracker monitorTracker = resourceStatTrackers.get(i);
StatTracker tracker = monitorTracker.getStatTracker();
if (displayName.equals(tracker.getDisplayName()))
{
trackerList.add(tracker);
}
}
StatTracker[] trackerArray = new StatTracker[trackerList.size()];
trackerList.toArray(trackerArray);
return trackerArray;
}
/**
* Retrieves the set of all resource monitor stat trackers associated with
* this job that have the specified display name.
*
* @param displayName The display name for the resource stat trackers to be
* retrieved.
*
* @return The set of all resource stat trackers associated with this job
* that have the specified display name.
*/
public ResourceMonitorStatTracker[]
getResourceMonitorStatTrackers(String displayName)
{
ArrayList<ResourceMonitorStatTracker> trackerList =
new ArrayList<ResourceMonitorStatTracker>();
for (int i=0; i < resourceStatTrackers.size(); i++)
{
ResourceMonitorStatTracker tracker = resourceStatTrackers.get(i);
if (displayName.equals(tracker.getStatTracker().getDisplayName()))
{
trackerList.add(tracker);
}
}
ResourceMonitorStatTracker[] trackerArray =
new ResourceMonitorStatTracker[trackerList.size()];
trackerList.toArray(trackerArray);
return trackerArray;
}
/**
* Retrieves the set of resource monitor class instances for which statistics
* are available with this job.
*
* @return The set of resource monitor class instances for which statistics
* are available with this job.
*/
public ResourceMonitor[] getResourceMonitorClasses()
{
LinkedHashMap<String,ResourceMonitor> classMap =
new LinkedHashMap<String,ResourceMonitor>();
for (int i=0; i < resourceStatTrackers.size(); i++)
{
ResourceMonitorStatTracker tracker = resourceStatTrackers.get(i);
ResourceMonitor monitor = tracker.getResourceMonitor();
String monitorClass = monitor.getClass().getName();
if (! classMap.containsKey(monitorClass))
{
classMap.put(monitorClass, monitor);
}
}
ResourceMonitor[] monitors = new ResourceMonitor[classMap.size()];
classMap.values().toArray(monitors);
return monitors;
}
/**
* Retrieves the set of all resource monitor stat trackers associated with
* this job that were captured using the specified resource monitor class.
*
* @param className The fully-qualified name of the resource monitor class
* for which to retrieve the associated resource monitor
* stat trackers.
*
* @return The set of all resource monitor stat trackers associated with this
* job that have the specified monitor class.
*/
public ResourceMonitorStatTracker[]
getResourceMonitorStatTrackersForClass(String className)
{
ArrayList<ResourceMonitorStatTracker> trackerList =
new ArrayList<ResourceMonitorStatTracker>();
for (int i=0; i < resourceStatTrackers.size(); i++)
{
ResourceMonitorStatTracker tracker = resourceStatTrackers.get(i);
if (tracker.getResourceMonitor().getClass().getName().equals(className))
{
trackerList.add(tracker);
}
}
ResourceMonitorStatTracker[] trackerArray =
new ResourceMonitorStatTracker[trackerList.size()];
trackerList.toArray(trackerArray);
return trackerArray;
}
/**
* Retrieves the set of all stat trackers associated with this job that have
* the specified display name and client ID.
*
* @param displayName The display name for the stat trackers to be
* retrieved.
* @param clientID The client ID for the stat trackers to be retrieved.
*
* @return The set of all stat trackers associated with this job that have
* the specified display name and client ID.
*/
public StatTracker[] getStatTrackers(String displayName, String clientID)
{
ArrayList<StatTracker> trackerList = new ArrayList<StatTracker>();
for (int i=0; i < statTrackers.size(); i++)
{
StatTracker tracker = statTrackers.get(i);
if (displayName.equals(tracker.getDisplayName()) &&
clientID.equals(tracker.getClientID()))
{
trackerList.add(tracker);
}
}
StatTracker[] trackerArray = new StatTracker[trackerList.size()];
trackerList.toArray(trackerArray);
return trackerArray;
}
/**
* Retrieves the set of all resource monitor stat trackers associated with
* this job that have the specified display name and client ID.
*
* @param displayName The display name for the stat trackers to be
* retrieved.
* @param clientID The client ID for the stat trackers to be retrieved.
*
* @return The set of all resource monitor stat trackers associated with this
* job that have the specified display name and client ID.
*/
public StatTracker[] getResourceStatTrackers(String displayName,
String clientID)
{
ArrayList<StatTracker> trackerList = new ArrayList<StatTracker>();
for (int i=0; i < resourceStatTrackers.size(); i++)
{
ResourceMonitorStatTracker monitorTracker = resourceStatTrackers.get(i);
StatTracker tracker = monitorTracker.getStatTracker();
if (displayName.equals(tracker.getDisplayName()) &&
clientID.equals(tracker.getClientID()))
{
trackerList.add(tracker);
}
}
StatTracker[] trackerArray = new StatTracker[trackerList.size()];
trackerList.toArray(trackerArray);
return trackerArray;
}
/**
* Retrieves the set of all resource monitor stat trackers associated with
* this job that have the specified display name and client ID.
*
* @param displayName The display name for the stat trackers to be
* retrieved.
* @param clientID The client ID for the stat trackers to be retrieved.
*
* @return The set of all resource monitor stat trackers associated with this
* job that have the specified display name and client ID.
*/
public ResourceMonitorStatTracker[]
getResourceMonitorStatTrackers(String displayName,
String clientID)
{
ArrayList<ResourceMonitorStatTracker> trackerList =
new ArrayList<ResourceMonitorStatTracker>();
for (int i=0; i < resourceStatTrackers.size(); i++)
{
ResourceMonitorStatTracker monitorTracker = resourceStatTrackers.get(i);
StatTracker tracker = monitorTracker.getStatTracker();
if (displayName.equals(tracker.getDisplayName()) &&
clientID.equals(tracker.getClientID()))
{
trackerList.add(monitorTracker);
}
}
ResourceMonitorStatTracker[] trackerArray =
new ResourceMonitorStatTracker[trackerList.size()];
trackerList.toArray(trackerArray);
return trackerArray;
}
/**
* Retrieves the client IDs of all the clients that contributed stat tracker
* information.
*
* @return The client IDs of all the clients that contributed stat tracker
* information.
*/
public String[] getStatTrackerClientIDs()
{
ArrayList<String> idList = new ArrayList<String>();
for (int i=0; i < statTrackers.size(); i++)
{
StatTracker tracker = statTrackers.get(i);
boolean match = false;
for (int j=0; j < idList.size(); j++)
{
if (tracker.getClientID().equals(idList.get(j)))
{
match = true;
break;
}
}
if (! match)
{
idList.add(tracker.getClientID());
}
}
String[] clientIDs = new String[idList.size()];
idList.toArray(clientIDs);
return clientIDs;
}
/**
* Retrieves the client IDs of all the clients that contributed resource
* monitor information.
*
* @return The client IDs of all the clients that contributed resource
* monitor information.
*/
public String[] getResourceStatTrackerClientIDs()
{
ArrayList<String> idList = new ArrayList<String>();
for (int i=0; i < resourceStatTrackers.size(); i++)
{
ResourceMonitorStatTracker monitorTracker = resourceStatTrackers.get(i);
StatTracker tracker = monitorTracker.getStatTracker();
boolean match = false;
for (int j=0; j < idList.size(); j++)
{
if (tracker.getClientID().equals(idList.get(j)))
{
match = true;
break;
}
}
if (! match)
{
idList.add(tracker.getClientID());
}
}
String[] clientIDs = new String[idList.size()];
idList.toArray(clientIDs);
return clientIDs;
}
/**
* Specifies the set of stat trackers associated with this job.
*
* @param trackerArray The set of stat trackers to assign to this job.
*/
public void setStatTrackers(StatTracker[] trackerArray)
{
statTrackers = new ArrayList<StatTracker>(trackerArray.length);
for (int i=0; i < trackerArray.length; i++)
{
statTrackers.add(trackerArray[i]);
}
}
/**
* Specifies the set of resource monitor stat trackers associated with this
* job.
*
* @param trackerArray The set of resource monitor stat trackers to assign
* to this job.
*/
public void setResourceStatTrackers(StatTracker[] trackerArray)
{
resourceStatTrackers =
new ArrayList<ResourceMonitorStatTracker>(trackerArray.length);
for (int i=0; i < trackerArray.length; i++)
{
ResourceMonitorStatTracker monitorTracker =
new ResourceMonitorStatTracker(new LegacyResourceMonitor(),
trackerArray[i]);
resourceStatTrackers.add(monitorTracker);
}
}
/**
* Specifies the set of resource monitor stat trackers associated with this
* job.
*
* @param monitorTrackers The set of resource monitor stat trackers to
* assign to this job.
*/
public void setResourceStatTrackers(ResourceMonitorStatTracker[]
monitorTrackers)
{
resourceStatTrackers =
new ArrayList<ResourceMonitorStatTracker>(monitorTrackers.length);
for (int i=0; i < monitorTrackers.length; i++)
{
resourceStatTrackers.add(monitorTrackers[i]);
}
}
/**
* Retrieves the set of messages logged during the job's execution.
*
* @return The set of messages logged during the job's execution.
*/
public String[] getLogMessages()
{
ArrayList clone = (ArrayList) logMessages.clone();
String[] messageArray = new String[clone.size()];
for (int i=0; i < messageArray.length; i++)
{
messageArray[i] = (String) clone.get(i);
}
return messageArray;
}
/**
* Adds the specified message to the set of messages logged during this job's
* execution.
*
* @param message The message to add to the set of messages for this job.
*/
public void addLogMessage(String message)
{
logMessages.add(message);
}
/**
* Specifies the set of messages logged during the job's execution.
*
* @param logMessages The set of messages logged during the job's execution.
*/
public void setLogMessages(String[] logMessages)
{
ArrayList<String> messageList = new ArrayList<String>(logMessages.length);
for (int i=0; i < logMessages.length; i++)
{
messageList.add(logMessages[i]);
}
this.logMessages = messageList;
}
/**
* Specifies the set of messages logged during the job's execution.
*
* @param logMessages An array list containing the set of messages logged
* during the job's execution.
*/
public void setLogMessages(ArrayList<String> logMessages)
{
this.logMessages = logMessages;
}
/**
* Retrieves the time at which this job actually started running.
*
* @return The time at which this job actually started running.
*/
public Date getActualStartTime()
{
return actualStartTime;
}
/**
* Specifies the time that this job actually started running.
*
* @param actualStartTime The time that this job actually started running.
*/
public void setActualStartTime(Date actualStartTime)
{
this.actualStartTime = actualStartTime;
}
/**
* Specifies the time that the job actually started running.
*
* @param actualStartTime The time that the job actually started running.
*/
public void setActualStartTime(long actualStartTime)
{
this.actualStartTime = new Date(actualStartTime);
}
/**
* Retrieves the time that the job actually stopped running.
*
* @return The time that the job actually stopped running.
*/
public Date getActualStopTime()
{
return actualStopTime;
}
/**
* Specifies the time that the job actually stopped running.
*
* @param actualStopTime The time that the job actually stopped running.
*/
public void setActualStopTime(Date actualStopTime)
{
this.actualStopTime = actualStopTime;
}
/**
* Specifies the time that the job actually stopped running.
*
* @param actualStopTime The time that the job actually stopped running.
*/
public void setActualStopTime(long actualStopTime)
{
this.actualStopTime = new Date(actualStopTime);
}
/**
* Retrieves the length of time in seconds that the job was actually running.
*
* @return The length of time in seconds that the job was actually running.
*/
public int getActualDuration()
{
return actualDuration;
}
/**
* Specifies the length of time in seconds that the job was actually running.
*
* @param actualDuration The length of time in seconds that the job was
* actually running.
*/
public void setActualDuration(int actualDuration)
{
this.actualDuration = actualDuration;
}
/**
* Indicates that the job should start running on all the specified clients
* with the appropriate number of threads per client.
*
* @throws UnableToRunException If the job is not in a state in which it can
* be started.
*/
public void startProcessing()
throws UnableToRunException
{
// The job state must indicate that the job has not yet been started. Any
// other job state will exit with an error
if (! ((jobState == Constants.JOB_STATE_NOT_YET_STARTED) ||
(jobState == Constants.JOB_STATE_RUNNING)))
{
String message = "The job cannot be run in its current state (" +
Constants.jobStateToString(jobState) + ')';
throw new UnableToRunException(message);
}
// Technically it's not running yet, but update the state information to
// say that it is. In the event that some critical error occurs while
// starting the job, this will prevent the possibility that a change in the
// job state is overwritten by this "running" indicator.
jobState = Constants.JOB_STATE_RUNNING;
// Get the appropriate number of client connections. Make sure that it is
// not null. There probably should be a check to make sure we actually got
// the right number of connections.
clientConnections =
slamdServer.getClientListener().getClientConnections(this);
if (clientConnections == null)
{
throw new UnableToRunException("Not enough clients available to run " +
"the job");
}
// Get the appropriate set of resource monitor client connections.
if (monitorClientsIfAvailable ||
((monitorClients != null) && (monitorClients.length > 0)))
{
monitorConnections = slamdServer.getMonitorClientListener().
getMonitorClientConnections(this);
if (monitorConnections == null)
{
throw new UnableToRunException("At least one in the requested set of " +
"resource monitor clients is not " +
"available.");
}
}
// Perform the job-level initialization, if any has been specified.
try
{
infoJobThread.initializeJob(parameters);
}
catch (UnableToRunException utre)
{
// This job won't be executed after all, so iterate through each
// connection and make it available for processing again.
for (int i=0; i < clientConnections.length; i++)
{
slamdServer.getClientListener().setAvailableForProcessing(
clientConnections[i]);
}
throw utre;
}
// For each monitor client, send it a job request message and tell it to
// start processing
for (int i=0;
((monitorConnections != null) && (i < monitorConnections.length)); i++)
{
JobResponseMessage jobResponse =
monitorConnections[i].sendJobRequest(this, i);
if (jobResponse.getResponseCode() ==
Constants.MESSAGE_RESPONSE_SUCCESS)
{
// Add this monitor client to the list of active monitor clients.
synchronized (activeClientMutex)
{
activeMonitorClients.add(monitorConnections[i]);
}
JobControlResponseMessage startResponse =
monitorConnections[i].sendJobControlRequest(this,
Constants.JOB_CONTROL_TYPE_START);
if (startResponse.getResponseCode() !=
Constants.MESSAGE_RESPONSE_SUCCESS)
{
logMessages.add("Unable to start resource monitoring on client " +
monitorConnections[i].getConnectionID() + ": " +
startResponse.getResponseCode() + " (" +
startResponse.getResponseMessage() + ')');
synchronized (activeClientMutex)
{
removeMonitorClient(monitorConnections[i]);
}
}
}
}
// For each client retrieved, send it a job request message and tell it to
// start processing
for (int i=0; i < clientConnections.length; i++)
{
JobResponseMessage jobResponse =
clientConnections[i].sendJobRequest(this, i);
if (jobResponse.getResponseCode() ==
Constants.MESSAGE_RESPONSE_SUCCESS)
{
// Add this client to the list of active clients
synchronized (activeClientMutex)
{
activeClients.add(clientConnections[i]);
}
JobControlResponseMessage startResponse =
clientConnections[i].sendJobControlRequest(this,
Constants.JOB_CONTROL_TYPE_START);
String errMsg = startResponse.getResponseMessage();
switch (startResponse.getResponseCode())
{
case Constants.MESSAGE_RESPONSE_CLASS_NOT_FOUND:
jobState = Constants.JOB_STATE_NO_SUCH_JOB;
logMessages.add("Unable to start job on client " +
clientConnections[i].getClientID() +
": No such job.");
synchronized (activeClientMutex)
{
removeActiveClient(clientConnections[i]);
slamdServer.getClientListener().setAvailableForProcessing(
clientConnections[i]);
}
break;
case Constants.MESSAGE_RESPONSE_CLASS_NOT_VALID:
jobState = Constants.JOB_STATE_STOPPED_DUE_TO_ERROR;
logMessages.add("Unable to start job on client " +
clientConnections[i].getClientID() +
": Job class not valid.");
synchronized (activeClientMutex)
{
removeActiveClient(clientConnections[i]);
slamdServer.getClientListener().setAvailableForProcessing(
clientConnections[i]);
}
break;
case Constants.MESSAGE_RESPONSE_JOB_CREATION_FAILURE:
logMessages.add("Unable to start job on client " +
clientConnections[i].getClientID() +
": Job creation failure.");
if ((errMsg != null) && (errMsg.length() > 0))
{
logMessages.add(errMsg);
}
jobState = Constants.JOB_STATE_STOPPED_DUE_TO_ERROR;
synchronized (activeClientMutex)
{
removeActiveClient(clientConnections[i]);
slamdServer.getClientListener().setAvailableForProcessing(
clientConnections[i]);
}
break;
case Constants.MESSAGE_RESPONSE_LOCAL_ERROR:
logMessages.add("Unable to start job on client " +
clientConnections[i].getClientID() +
": Client local error.");
if ((errMsg != null) && (errMsg.length() > 0))
{
logMessages.add(errMsg);
}
jobState = Constants.JOB_STATE_STOPPED_DUE_TO_ERROR;
synchronized (activeClientMutex)
{
removeActiveClient(clientConnections[i]);
slamdServer.getClientListener().setAvailableForProcessing(
clientConnections[i]);
}
break;
case Constants.MESSAGE_RESPONSE_NO_SUCH_JOB:
logMessages.add("Unable to start job on client " +
clientConnections[i].getClientID() +
": Client does not know about job.");
jobState = Constants.JOB_STATE_STOPPED_DUE_TO_ERROR;
synchronized (activeClientMutex)
{
removeActiveClient(clientConnections[i]);
slamdServer.getClientListener().setAvailableForProcessing(
clientConnections[i]);
}
break;
}
}
}
// If there are no active clients, then the job could not be started.
if (activeClients.isEmpty())
{
// If there are active monitor connections, they should be stopped.
synchronized (activeClientMutex)
{
for (int i=0; i < activeMonitorClients.size(); i++)
{
ResourceMonitorClientConnection conn = activeMonitorClients.get(i);
conn.sendJobControlRequest(this, Constants.JOB_CONTROL_TYPE_STOP);
}
activeMonitorClients.clear();
}
jobState = Constants.JOB_STATE_STOPPED_DUE_TO_ERROR;
throw new UnableToRunException("None of the clients were able to start " +
"the job.");
}
}
/**
* Retrieves the set of client connections that have been selected to use to
* run this job. Note that this will only be available while the job is
* actually running and for a very brief period of time before it starts, so
* this method should definitely not be considered available for general use.
*
* @return The set of client connections that have been selected to use to
* run this job.
*/
public ClientConnection[] getClientConnections()
{
return clientConnections;
}
/**
* Retrieves the set of clients that are currently being used to run this job.
* The contents of this list will change as clients are started and stopped.
* It should only be used internally while the job is running.
*
* @return The set of clients that are currently being used to run this job.
*/
public ArrayList getActiveClients()
{
return activeClients;
}
/**
* Retrieves the set of resource monitor clients that are currently being used
* in conjunction with this job. The contents of this list will change as
* resource monitor clients start and stop collecting data for this job.
*
* @return The set of resource monitor clients that are currently being used
* to run this job.
*/
public ArrayList getActiveMonitorClients()
{
return activeMonitorClients;
}
/**
* Removes information about the specified client connection from the list of
* active connections associated with this job. This will be called once the
* job has completed, or if it is unable to start for some reason.
*
* @param clientConnection The client connection that should be removed from
* the list of active connections.
*/
public void removeActiveClient(ClientConnection clientConnection)
{
for (int i=0; i < activeClients.size(); i++)
{
ClientConnection conn = activeClients.get(i);
if (conn.getClientID().equals(clientConnection.getClientID()))
{
activeClients.remove(i);
break;
}
}
}
/**
* Removes information about the specified resource monitor client connection
* from the list of active connections associated with this job. This will be
* called once the job has completed, or if it is unable to start for some
* reason.
*
* @param clientConnection The resource monitor client connection that
* should be removed from the list of active
* connections.
*/
public void removeMonitorClient(ResourceMonitorClientConnection
clientConnection)
{
for (int i=0; i < activeMonitorClients.size(); i++)
{
ResourceMonitorClientConnection conn = activeMonitorClients.get(i);
if (conn.getConnectionID().equals(clientConnection.getConnectionID()))
{
activeMonitorClients.remove(i);
break;
}
}
}
/**
* Indicates that the specified client has completed its processing for this
* job.
*
* @param clientConnection The client connection that has completed
* processing for this job.
* @param jobCompletedMessage The job completed message for the client that
* contains all the information we need to track.
*/
public void clientDone(ClientConnection clientConnection,
JobCompletedMessage jobCompletedMessage)
{
boolean jobDone = false;
synchronized (activeClientMutex)
{
// First, remove the connection from the list of active connections
removeActiveClient(clientConnection);
// See if we need to update the tentative job state. It will be updated
// as long as it currently indicates that the job has completed
// successfully.
if (tentativeJobState == Constants.JOB_STATE_COMPLETED_SUCCESSFULLY)
{
tentativeJobState = jobCompletedMessage.getJobState();
}
// Next, update the status counter information for this job.
StatTracker[] clientTrackers = jobCompletedMessage.getStatTrackers();
for (int i=0; i < clientTrackers.length; i++)
{
statTrackers.add(clientTrackers[i]);
}
// Update the timing information for this job. Pick the earliest start
// time as the actual start time, the latest stop time as the actual
// stop time, and the longest duration as the actual duration. Note that
// differences in remote clocks may skew start and end time information,
// so duration may be the only reliable statistic unless time
// synchronization is used.
if ((actualStartTime == null) ||
(jobCompletedMessage.getActualStartTime().compareTo(
actualStartTime) < 0))
{
actualStartTime = jobCompletedMessage.getActualStartTime();
}
if ((actualStopTime == null) ||
(jobCompletedMessage.getActualStopTime().compareTo(
actualStopTime) > 0))
{
actualStopTime = jobCompletedMessage.getActualStopTime();
}
if ((actualDuration < 0) ||
(jobCompletedMessage.getActualDuration() > actualDuration))
{
actualDuration = jobCompletedMessage.getActualDuration();
}
// Log any appropriate information from the job completed message and
// store them the job's log message list.
String[] clientLogMessages = jobCompletedMessage.getLogMessages();
for (int i=0; i < clientLogMessages.length; i++)
{
slamdServer.logWithoutFormatting(Constants.LOG_LEVEL_JOB_PROCESSING,
clientLogMessages[i]);
logMessages.add(clientLogMessages[i]);
}
// If this was the last active connection, then check to see if any
// resource monitor clients are still active. If so, then indicate that
// they should stop. If not, then do all the appropriate finalization.
if (activeClients.isEmpty())
{
if (activeMonitorClients.isEmpty())
{
jobDone = true;
}
else
{
for (int i=0; i < activeMonitorClients.size(); i++)
{
ResourceMonitorClientConnection client =
activeMonitorClients.get(i);
client.sendJobControlRequest(this, Constants.JOB_CONTROL_TYPE_STOP);
}
}
}
}
if (jobDone)
{
// If this was the last active connection, then run the per-job
// finalization, update the job info to indicate that the job is done, and
// update the scheduler.
infoJobThread.finalizeJob();
this.jobState = tentativeJobState;
slamdServer.getScheduler().jobDone(this);
}
}
/**
* Indicates that the specified resource monitor client has completed its
* processing and that its resource statistics are available.
*
* @param clientConnection The resource monitor client connection that has
* completed its processing for this job.
* @param message The job completed message associated sent by the
* resource monitor client.
*/
public void resourceClientDone(
ResourceMonitorClientConnection clientConnection,
JobCompletedMessage message)
{
boolean jobDone = false;
synchronized (activeClientMutex)
{
// First, remove the connection from the list of active connections
removeMonitorClient(clientConnection);
// See if we need to update the tentative job state. It will be updated
// as long as it currently indicates that the job has completed
// successfully.
if (tentativeJobState == Constants.JOB_STATE_COMPLETED_SUCCESSFULLY)
{
tentativeJobState = message.getJobState();
}
// Next, update the status counter information for this job.
StatTracker[] monitorTrackers = message.getStatTrackers();
for (int i=0; i < monitorTrackers.length; i++)
{
// FIXME -- Correct this when the new protocol is in place.
ResourceMonitorStatTracker monitorTracker =
new ResourceMonitorStatTracker(new LegacyResourceMonitor(),
monitorTrackers[i]);
resourceStatTrackers.add(monitorTracker);
}
// Update the timing information for this job. Pick the earliest start
// time as the actual start time, the latest stop time as the actual
// stop time, and the longest duration as the actual duration. Note that
// differences in remote clocks may skew start and end time information,
// so duration may be the only reliable statistic unless time
// synchronization is used.
if ((actualStartTime == null) ||
(message.getActualStartTime().compareTo(actualStartTime) < 0))
{
actualStartTime = message.getActualStartTime();
}
if ((actualStopTime == null) ||
(message.getActualStopTime().compareTo(actualStopTime) > 0))
{
actualStopTime = message.getActualStopTime();
}
if ((actualDuration < 0) ||
(message.getActualDuration() > actualDuration))
{
actualDuration = message.getActualDuration();
}
// Log any appropriate information from the job completed message and
// store them the job's log message list.
String[] monitorLogMessages = message.getLogMessages();
for (int i=0; i < monitorLogMessages.length; i++)
{
slamdServer.logWithoutFormatting(Constants.LOG_LEVEL_JOB_PROCESSING,
monitorLogMessages[i]);
logMessages.add(monitorLogMessages[i]);
}
// If this was the last active connection, then set a flag so that we can
// know the job was done. Note that we can't actually run the
// finalization here because it is possible (although remote) that a
// deadlock could occur if we're holding the client mutex and trying to
// get the scheduler lock, while the scheduler holds its own lock and
// tries to get the client mutex.
if (activeClients.isEmpty() && activeMonitorClients.isEmpty())
{
jobDone = true;
}
}
if (jobDone)
{
// If this was the last active connection, then run the per-job
// finalization, update the job info to indicate that the job is done, and
// update the scheduler.
infoJobThread.finalizeJob();
this.jobState = tentativeJobState;
slamdServer.getScheduler().jobDone(this);
}
}
/**
* Indicates that all job threads should stop running on all clients.
*/
public void stopProcessing()
{
synchronized (activeClientMutex)
{
for (int i=0; i < activeClients.size(); i++)
{
ClientConnection client = activeClients.get(i);
client.sendJobControlRequest(this, Constants.JOB_CONTROL_TYPE_STOP);
}
for (int i=0; i < activeMonitorClients.size(); i++)
{
ResourceMonitorClientConnection client =
activeMonitorClients.get(i);
client.sendJobControlRequest(this, Constants.JOB_CONTROL_TYPE_STOP);
}
}
}
/**
* Indicates that all job threads should stop running on all clients, and will
* not return until that has occurred.
*/
public void stopAndWait()
{
synchronized (activeClientMutex)
{
for (int i=0; i < activeClients.size(); i++)
{
ClientConnection client = activeClients.get(i);
client.sendJobControlRequest(this,
Constants.JOB_CONTROL_TYPE_STOP_AND_WAIT);
}
for (int i=0; i < activeMonitorClients.size(); i++)
{
ResourceMonitorClientConnection client =
activeMonitorClients.get(i);
client.sendJobControlRequest(this,
Constants.JOB_CONTROL_TYPE_STOP_AND_WAIT);
}
}
}
/**
* Encodes this job to a byte array suitable for storage in the database.
*
* @return The job encoded as a byte array.
*/
public byte[] encode()
{
ArrayList<ASN1Element> elementList = new ArrayList<ASN1Element>();
SimpleDateFormat dateFormat =
new SimpleDateFormat(Constants.ATTRIBUTE_DATE_FORMAT);
elementList.add(new ASN1OctetString(ELEMENT_JOB_ID));
elementList.add(new ASN1OctetString(jobID));
elementList.add(new ASN1OctetString(ELEMENT_JOB_CLASS));
elementList.add(new ASN1OctetString(jobThreadClassName));
if ((optimizingJobID != null) && (optimizingJobID.length() > 0))
{
elementList.add(new ASN1OctetString(ELEMENT_OPTIMIZING_JOB_ID));
elementList.add(new ASN1OctetString(optimizingJobID));
}
if ((jobGroup != null) && (jobGroup.length() > 0))
{
elementList.add(new ASN1OctetString(ELEMENT_JOB_GROUP));
elementList.add(new ASN1OctetString(jobGroup));
}
if ((folderName != null) && (folderName.length() > 0))
{
elementList.add(new ASN1OctetString(ELEMENT_FOLDER));
elementList.add(new ASN1OctetString(folderName));
}
elementList.add(new ASN1OctetString(ELEMENT_JOB_STATE));
elementList.add(new ASN1Integer(jobState));
elementList.add(new ASN1OctetString(ELEMENT_DISPLAY_IN_READ_ONLY));
elementList.add(new ASN1Boolean(displayInReadOnlyMode));
if ((jobDescription != null) && (jobDescription.length() > 0))
{
elementList.add(new ASN1OctetString(ELEMENT_DESCRIPTION));
elementList.add(new ASN1OctetString(jobDescription));
}
elementList.add(new ASN1OctetString(ELEMENT_START_TIME));
elementList.add(new ASN1OctetString(dateFormat.format(startTime)));
if (stopTime != null)
{
elementList.add(new ASN1OctetString(ELEMENT_STOP_TIME));
elementList.add(new ASN1OctetString(dateFormat.format(stopTime)));
}
if (duration > 0)
{
elementList.add(new ASN1OctetString(ELEMENT_DURATION));
elementList.add(new ASN1Integer(duration));
}
elementList.add(new ASN1OctetString(ELEMENT_NUM_CLIENTS));
elementList.add(new ASN1Integer(numClients));
if ((requestedClients != null) && (requestedClients.length > 0))
{
ASN1Element[] clientElements = new ASN1Element[requestedClients.length];
for (int i=0; i < requestedClients.length; i++)
{
clientElements[i] = new ASN1OctetString(requestedClients[i]);
}
elementList.add(new ASN1OctetString(ELEMENT_REQUESTED_CLIENTS));
elementList.add(new ASN1Sequence(clientElements));
}
if ((monitorClients != null) && (monitorClients.length > 0))
{
ASN1Element[] clientElements = new ASN1Element[monitorClients.length];
for (int i=0; i < monitorClients.length; i++)
{
clientElements[i] = new ASN1OctetString(monitorClients[i]);
}
elementList.add(new ASN1OctetString(ELEMENT_MONITOR_CLIENTS));
elementList.add(new ASN1Sequence(clientElements));
}
elementList.add(new ASN1OctetString(ELEMENT_MONITOR_CLIENTS_IF_AVAILABLE));
elementList.add(new ASN1Boolean(monitorClientsIfAvailable));
elementList.add(new ASN1OctetString(ELEMENT_WAIT_FOR_CLIENTS));
elementList.add(new ASN1Boolean(waitForClients));
elementList.add(new ASN1OctetString(ELEMENT_THREADS_PER_CLIENT));
elementList.add(new ASN1Integer(threadsPerClient));
elementList.add(new ASN1OctetString(ELEMENT_THREAD_STARTUP_DELAY));
elementList.add(new ASN1Integer(threadStartupDelay));
if ((dependencies != null) && (! dependencies.isEmpty()))
{
ASN1Element[] dependencyElements = new ASN1Element[dependencies.size()];
for (int i=0; i < dependencyElements.length; i++)
{
dependencyElements[i] = new ASN1OctetString(dependencies.get(i));
}
elementList.add(new ASN1OctetString(ELEMENT_DEPENDENCIES));
elementList.add(new ASN1Sequence(dependencyElements));
}
if ((notifyAddresses != null) && (notifyAddresses.length > 0))
{
ASN1Element[] addrElements = new ASN1Element[notifyAddresses.length];
for (int i=0; i < notifyAddresses.length; i++)
{
addrElements[i] = new ASN1OctetString(notifyAddresses[i]);
}
elementList.add(new ASN1OctetString(ELEMENT_NOTIFY_ADDRESSES));
elementList.add(new ASN1Sequence(addrElements));
}
elementList.add(new ASN1OctetString(ELEMENT_COLLECTION_INTERVAL));
elementList.add(new ASN1Integer(collectionInterval));
if ((jobComments != null) && (jobComments.length() > 0))
{
elementList.add(new ASN1OctetString(ELEMENT_COMMENTS));
elementList.add(new ASN1OctetString(jobComments));
}
Parameter[] params = parameters.getParameters();
ArrayList<ASN1Element> paramList = new ArrayList<ASN1Element>();
for (int i=0; i < params.length; i++)
{
if ((params[i] instanceof PlaceholderParameter) ||
(params[i] instanceof LabelParameter))
{
continue;
}
ASN1Element[] paramElements = new ASN1Element[]
{
new ASN1OctetString(params[i].getName()),
new ASN1OctetString(params[i].getValueString())
};
paramList.add(new ASN1Sequence(paramElements));
}
ASN1Element[] paramsElements = new ASN1Element[paramList.size()];
paramList.toArray(paramsElements);
elementList.add(new ASN1OctetString(ELEMENT_PARAMETERS));
elementList.add(new ASN1Sequence(paramsElements));
if (actualStartTime != null)
{
elementList.add(new ASN1OctetString(ELEMENT_ACTUAL_START_TIME));
elementList.add(new ASN1OctetString(dateFormat.format(actualStartTime)));
}
if (actualStopTime != null)
{
elementList.add(new ASN1OctetString(ELEMENT_ACTUAL_STOP_TIME));
elementList.add(new ASN1OctetString(dateFormat.format(actualStopTime)));
}
if (actualDuration >= 0)
{
elementList.add(new ASN1OctetString(ELEMENT_ACTUAL_DURATION));
elementList.add(new ASN1Integer(actualDuration));
}
if ((statTrackers != null) && (! statTrackers.isEmpty()))
{
StatTracker[] trackers = new StatTracker[statTrackers.size()];
statTrackers.toArray(trackers);
elementList.add(new ASN1OctetString(ELEMENT_STATS));
elementList.add(StatEncoder.trackersToSequence(trackers));
}
if ((resourceStatTrackers != null) && (! resourceStatTrackers.isEmpty()))
{
elementList.add(new ASN1OctetString(ELEMENT_RESOURCE_MONITOR_STATS));
elementList.add(
ResourceMonitorStatTracker.trackersToSequence(resourceStatTrackers));
}
if ((logMessages != null) && (! logMessages.isEmpty()))
{
ASN1Element[] messageElements = new ASN1Element[logMessages.size()];
for (int i=0; i < messageElements.length; i++)
{
messageElements[i] = new ASN1OctetString(logMessages.get(i));
}
elementList.add(new ASN1OctetString(ELEMENT_LOG_MESSAGES));
elementList.add(new ASN1Sequence(messageElements));
}
ASN1Element[] elements = new ASN1Element[elementList.size()];
elementList.toArray(elements);
return new ASN1Sequence(elements).encode();
}
/**
* Decodes the provided byte array as a SLAMD job.
*
* @param slamdServer The SLAMD server instance with which this job is to be
* associated.
* @param encodedJob The byte array containing the encoded job data.
*
* @return The job decoded from the provided byte array.
*
* @throws DecodeException If a problem occurs while trying to decode the
* job data.
*/
public static Job decode(SLAMDServer slamdServer, byte[] encodedJob)
throws DecodeException
{
try
{
boolean displayInReadOnlyMode = false;
boolean monitorClientsIfAvailable = false;
boolean waitForClients = true;
Date actualStartTime = null;
Date actualStopTime = null;
Date startTime = null;
Date stopTime = null;
int actualDuration = -1;
int collectionInterval = -1;
int duration = -1;
int jobState = -1;
int numClients = -1;
int threadsPerClient = -1;
int threadStartupDelay = 0;
ParameterList parameters = new ParameterList();
StatTracker[] statTrackers = new StatTracker[0];
ResourceMonitorStatTracker[] monitorStatTrackers =
new ResourceMonitorStatTracker[0];
String comments = null;
String folderName = null;
String jobClassName = null;
String jobDescription = null;
String jobGroup = null;
String jobID = null;
String optimizingJobID = null;
String[] dependencies = new String[0];
String[] logMessages = new String[0];
String[] monitorClients = new String[0];
String[] notifyAddresses = new String[0];
String[] requestedClients = new String[0];
SimpleDateFormat dateFormat =
new SimpleDateFormat(Constants.ATTRIBUTE_DATE_FORMAT);
ASN1Element element = ASN1Element.decode(encodedJob);
ASN1Element[] elements = element.decodeAsSequence().getElements();
for (int i=0; i < elements.length; i += 2)
{
String elementName = elements[i].decodeAsOctetString().getStringValue();
if (elementName.equals(ELEMENT_JOB_ID))
{
jobID = elements[i+1].decodeAsOctetString().getStringValue();
}
else if (elementName.equals(ELEMENT_JOB_CLASS))
{
jobClassName = elements[i+1].decodeAsOctetString().getStringValue();
}
else if (elementName.equals(ELEMENT_OPTIMIZING_JOB_ID))
{
optimizingJobID =
elements[i+1].decodeAsOctetString().getStringValue();
}
else if (elementName.equals(ELEMENT_JOB_GROUP))
{
jobGroup = elements[i+1].decodeAsOctetString().getStringValue();
}
else if (elementName.equals(ELEMENT_FOLDER))
{
folderName = elements[i+1].decodeAsOctetString().getStringValue();
}
else if (elementName.equals(ELEMENT_JOB_STATE))
{
jobState = elements[i+1].decodeAsInteger().getIntValue();
}
else if (elementName.equals(ELEMENT_DISPLAY_IN_READ_ONLY))
{
displayInReadOnlyMode =
elements[i+1].decodeAsBoolean().getBooleanValue();
}
else if (elementName.equals(ELEMENT_DESCRIPTION))
{
jobDescription = elements[i+1].decodeAsOctetString().getStringValue();
}
else if (elementName.equals(ELEMENT_START_TIME))
{
String timeStr = elements[i+1].decodeAsOctetString().getStringValue();
startTime = dateFormat.parse(timeStr);
}
else if (elementName.equals(ELEMENT_STOP_TIME))
{
String timeStr = elements[i+1].decodeAsOctetString().getStringValue();
stopTime = dateFormat.parse(timeStr);
}
else if (elementName.equals(ELEMENT_DURATION))
{
duration = elements[i+1].decodeAsInteger().getIntValue();
}
else if (elementName.equals(ELEMENT_NUM_CLIENTS))
{
numClients = elements[i+1].decodeAsInteger().getIntValue();
}
else if (elementName.equals(ELEMENT_REQUESTED_CLIENTS))
{
ASN1Element[] clientElements =
elements[i+1].decodeAsSequence().getElements();
requestedClients = new String[clientElements.length];
for (int j=0; j < requestedClients.length; j++)
{
requestedClients[j] =
clientElements[j].decodeAsOctetString().getStringValue();
}
}
else if (elementName.equals(ELEMENT_MONITOR_CLIENTS))
{
ASN1Element[] clientElements =
elements[i+1].decodeAsSequence().getElements();
monitorClients = new String[clientElements.length];
for (int j=0; j < monitorClients.length; j++)
{
monitorClients[j] =
clientElements[j].decodeAsOctetString().getStringValue();
}
}
else if (elementName.equals(ELEMENT_MONITOR_CLIENTS_IF_AVAILABLE))
{
monitorClientsIfAvailable =
elements[i+1].decodeAsBoolean().getBooleanValue();
}
else if (elementName.equals(ELEMENT_WAIT_FOR_CLIENTS))
{
waitForClients = elements[i+1].decodeAsBoolean().getBooleanValue();
}
else if (elementName.equals(ELEMENT_THREADS_PER_CLIENT))
{
threadsPerClient = elements[i+1].decodeAsInteger().getIntValue();
}
else if (elementName.equals(ELEMENT_THREAD_STARTUP_DELAY))
{
threadStartupDelay = elements[i+1].decodeAsInteger().getIntValue();
}
else if (elementName.equals(ELEMENT_DEPENDENCIES))
{
ASN1Element[] dependencyElements =
elements[i+1].decodeAsSequence().getElements();
dependencies = new String[dependencyElements.length];
for (int j=0; j < dependencies.length; j++)
{
dependencies[j] =
dependencyElements[j].decodeAsOctetString().getStringValue();
}
}
else if (elementName.equals(ELEMENT_NOTIFY_ADDRESSES))
{
ASN1Element[] addrElements =
elements[i+1].decodeAsSequence().getElements();
notifyAddresses = new String[addrElements.length];
for (int j=0; j < notifyAddresses.length; j++)
{
notifyAddresses[j] =
addrElements[j].decodeAsOctetString().getStringValue();
}
}
else if (elementName.equals(ELEMENT_COLLECTION_INTERVAL))
{
collectionInterval = elements[i+1].decodeAsInteger().getIntValue();
}
else if (elementName.equals(ELEMENT_COMMENTS))
{
comments = elements[i+1].decodeAsOctetString().getStringValue();
}
else if (elementName.equals(ELEMENT_PARAMETERS))
{
if (jobClassName == null)
{
for (int j=i+2; j < elements.length; j += 2)
{
String name = elements[j].decodeAsOctetString().getStringValue();
if (name.equals(ELEMENT_JOB_CLASS))
{
jobClassName =
elements[j+1].decodeAsOctetString().getStringValue();
break;
}
}
}
try
{
Class<?> jobClass = Constants.classForName(jobClassName);
JobClass stubInstance = (JobClass) jobClass.newInstance();
parameters = stubInstance.getClientSideParameterStubs().clone();
ASN1Element[] paramsElements =
elements[i+1].decodeAsSequence().getElements();
for (int j=0; j < paramsElements.length; j++)
{
ASN1Element[] paramElements =
paramsElements[j].decodeAsSequence().getElements();
String name =
paramElements[0].decodeAsOctetString().getStringValue();
String value =
paramElements[1].decodeAsOctetString().getStringValue();
Parameter p = parameters.getParameter(name);
if (p != null)
{
p.setValueFromString(value);
}
}
}
catch (Exception e)
{
parameters = new ParameterList();
ASN1Element[] paramsElements =
elements[i+1].decodeAsSequence().getElements();
for (int j=0; j < paramsElements.length; j++)
{
ASN1Element[] paramElements =
paramsElements[j].decodeAsSequence().getElements();
String name =
paramElements[0].decodeAsOctetString().getStringValue();
String value =
paramElements[1].decodeAsOctetString().getStringValue();
parameters.addParameter(new StringParameter(name, value));
}
}
}
else if (elementName.equals(ELEMENT_ACTUAL_START_TIME))
{
String timeStr = elements[i+1].decodeAsOctetString().getStringValue();
actualStartTime = dateFormat.parse(timeStr);
}
else if (elementName.equals(ELEMENT_ACTUAL_STOP_TIME))
{
String timeStr = elements[i+1].decodeAsOctetString().getStringValue();
actualStopTime = dateFormat.parse(timeStr);
}
else if (elementName.equals(ELEMENT_ACTUAL_DURATION))
{
actualDuration = elements[i+1].decodeAsInteger().getIntValue();
}
else if (elementName.equals(ELEMENT_STATS))
{
statTrackers =
StatEncoder.sequenceToTrackers(elements[i+1].decodeAsSequence());
}
else if (elementName.equals(ELEMENT_LEGACY_MONITOR_STATS))
{
StatTracker[] trackers =
StatEncoder.sequenceToTrackers(elements[i+1].decodeAsSequence());
monitorStatTrackers = new ResourceMonitorStatTracker[trackers.length];
for (int j=0; j < trackers.length; j++)
{
monitorStatTrackers[j] =
new ResourceMonitorStatTracker(new LegacyResourceMonitor(),
trackers[j]);
}
}
else if (elementName.equals(ELEMENT_RESOURCE_MONITOR_STATS))
{
monitorStatTrackers = ResourceMonitorStatTracker.sequenceToTrackers(
elements[i+1].decodeAsSequence());
}
else if (elementName.equals(ELEMENT_LOG_MESSAGES))
{
// FIXME -- Encode the log messages as a single element rather than
// multiple elements.
ASN1Element[] msgElements =
elements[i+1].decodeAsSequence().getElements();
logMessages = new String[msgElements.length];
for (int j=0; j < logMessages.length; j++)
{
logMessages[j] =
msgElements[j].decodeAsOctetString().getStringValue();
}
}
}
Job job = new Job(slamdServer, jobClassName, numClients, threadsPerClient,
threadStartupDelay, startTime, stopTime, duration,
collectionInterval, parameters, displayInReadOnlyMode);
job.setJobID(jobID);
job.setOptimizingJobID(optimizingJobID);
job.setJobGroup(jobGroup);
job.setFolderName(folderName);
job.setJobState(jobState);
job.setJobDescription(jobDescription);
job.setRequestedClients(requestedClients);
job.setResourceMonitorClients(monitorClients);
job.setMonitorClientsIfAvailable(monitorClientsIfAvailable);
job.setWaitForClients(waitForClients);
job.setDependencies(dependencies);
job.setNotifyAddresses(notifyAddresses);
job.setJobComments(comments);
job.setActualStartTime(actualStartTime);
job.setActualStopTime(actualStopTime);
job.setActualDuration(actualDuration);
job.setStatTrackers(statTrackers);
job.setResourceStatTrackers(monitorStatTrackers);
job.setLogMessages(logMessages);
return job;
}
catch (Exception e)
{
throw new DecodeException("Unable to decode job: " + e, e);
}
}
/**
* Decodes the provided byte array as a SLAMD job, but only decodes a minimal
* set of data for display on summary pages.
*
* @param slamdServer The SLAMD server instance with which this job is to be
* associated.
* @param encodedJob The byte array containing the encoded job data.
*
* @return The summary job decoded from the provided byte array.
*
* @throws DecodeException If a problem occurs while trying to decode the
* job data.
*/
public static Job decodeSummaryJob(SLAMDServer slamdServer, byte[] encodedJob)
throws DecodeException
{
try
{
boolean displayInReadOnlyMode = false;
Date actualStartTime = null;
Date startTime = null;
int actualDuration = -1;
int jobState = -1;
String jobClassName = null;
String jobDescription = null;
String jobID = null;
SimpleDateFormat dateFormat =
new SimpleDateFormat(Constants.ATTRIBUTE_DATE_FORMAT);
ASN1Element element = ASN1Element.decode(encodedJob);
ASN1Element[] elements = element.decodeAsSequence().getElements();
for (int i=0; i < elements.length; i += 2)
{
String elementName = elements[i].decodeAsOctetString().getStringValue();
if (elementName.equals(ELEMENT_JOB_ID))
{
jobID = elements[i+1].decodeAsOctetString().getStringValue();
}
else if (elementName.equals(ELEMENT_JOB_CLASS))
{
jobClassName = elements[i+1].decodeAsOctetString().getStringValue();
}
else if (elementName.equals(ELEMENT_JOB_STATE))
{
jobState = elements[i+1].decodeAsInteger().getIntValue();
}
else if (elementName.equals(ELEMENT_DISPLAY_IN_READ_ONLY))
{
displayInReadOnlyMode =
elements[i+1].decodeAsBoolean().getBooleanValue();
}
else if (elementName.equals(ELEMENT_DESCRIPTION))
{
jobDescription = elements[i+1].decodeAsOctetString().getStringValue();
}
else if (elementName.equals(ELEMENT_START_TIME))
{
String timeStr = elements[i+1].decodeAsOctetString().getStringValue();
startTime = dateFormat.parse(timeStr);
}
else if (elementName.equals(ELEMENT_ACTUAL_START_TIME))
{
String timeStr = elements[i+1].decodeAsOctetString().getStringValue();
actualStartTime = dateFormat.parse(timeStr);
}
else if (elementName.equals(ELEMENT_ACTUAL_DURATION))
{
actualDuration = elements[i+1].decodeAsInteger().getIntValue();
}
}
Job job = new Job(slamdServer, jobClassName, 0, 0, 0, startTime, null, 0,
0, null, displayInReadOnlyMode);
job.setJobID(jobID);
job.setJobDescription(jobDescription);
job.setJobState(jobState);
job.setActualStartTime(actualStartTime);
job.setActualDuration(actualDuration);
return job;
}
catch (Exception e)
{
throw new DecodeException("Unable to decode job: " + e, e);
}
}
/**
* Compares this job with the provided object to determine the relative order
* of the two in a sorted list. The comparison will be based on the job IDs,
* comparing first the timestamp segments, then the counter segments. If
* they're all the same, then the iteration and rerun segments will be taken
* into account if appropriate.
*
* @param o The object to compare with this job. It must be a Job.
*
* @return A negative value if this job should be ordered before the provided
* object, a positive value if this job should be ordered after the
* provided object, or zero if there is no difference in ordering.
*
* @throws ClassCastException If the provided object is not a Job.
*/
public int compareTo(Object o)
throws ClassCastException
{
if (o == null)
{
return -1;
}
String jobID2 = "";
try
{
Job j = (Job) o;
jobID2 = j.getJobID();
StringTokenizer t1 = new StringTokenizer(jobID, "-");
StringTokenizer t2 = new StringTokenizer(jobID2, "-");
// Get the timestamp string. If they differ, then use the
// String.compareTo method.
String date1 = t1.nextToken();
String date2 = t2.nextToken();
if (! date1.equals(date2))
{
return date1.compareTo(date2);
}
// Get the random + counter portion. If they differ, then compare the
// numeric counter portions.
String counterStr1 = t1.nextToken();
String counterStr2 = t2.nextToken();
if (! counterStr1.equals(counterStr2))
{
Integer counter1 = new Integer(counterStr1.substring(6));
Integer counter2 = new Integer(counterStr2.substring(6));
return counter1.compareTo(counter2);
}
// Get the iteration portion, if they exist.
Integer iteration1;
if (t1.hasMoreTokens())
{
iteration1 = new Integer(t1.nextToken());
}
else
{
if (t2.hasMoreTokens())
{
// The provided job has an iteration but this job doesn't, so this job
// will come first.
return -1;
}
else
{
// They must be equal.
return 0;
}
}
if (t2.hasMoreTokens())
{
Integer iteration2 = new Integer(t2.nextToken());
// It is possible that one of the jobs is a rerun iteration, which
// should always be ordered after all other iterations of an optimizing
// job.
if (t1.hasMoreTokens())
{
if (t2.hasMoreTokens())
{
// They both still have stuff to come, so we'll order them by name.
return jobID.compareTo(jobID2);
}
else
{
// This is almost certainly a rerun iteration.
return 1;
}
}
else if (t2.hasMoreTokens())
{
// The provided job is a rerun iteration.
return -1;
}
else
{
// Neither is a rerun iteration so compare the iteration numbers.
return iteration1.compareTo(iteration2);
}
}
else
{
// The provided job does not have an iteration but this job does, so the
// provided job will come first.
return 1;
}
}
catch (Exception e)
{
slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG,
JobClass.stackTraceToString(e));
return jobID.compareTo(jobID2);
}
}
}