/*
* 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.scripting.general;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import com.slamd.common.Constants;
import com.slamd.job.JobClass;
import com.slamd.scripting.engine.Argument;
import com.slamd.scripting.engine.Method;
import com.slamd.scripting.engine.ScriptException;
import com.slamd.scripting.engine.ScriptParser;
import com.slamd.scripting.engine.StopRunningException;
import com.slamd.scripting.engine.Variable;
/**
* This class defines a set of methods that may be called during script
* execution to perform various tasks. These methods are:
*
* <UL>
* <LI>debugMessage(string message) -- Writes the specified message to the
* client's debug writer. This method does not return a value.</LI>
* <LI>exit() -- Stops execution of the script. This method does not return a
* value.</LI>
* <LI>exitWithError() -- Stops execution of the script and indicates that one
* or more errors occurred during execution that may impact the accuracy
* of the results collected. This method does not return a value.</LI>
* <LI>getClientNumber() -- Retrieves the client number that has been assigned
* to the client running the job. The first client will be client number
* 0, the second will be client number 1, etc.</LI>
* <LI>getThreadNumber() -- Retrieves the thread number that has been assigned
* to the thread running the job on the current client. The first thread
* will be thread number 0, the second will be thread number 1, etc. Each
* client will have identical thread number ranges.</LI>
* <LI>getScriptArgument(string name) -- Retrieves the specified argument that
* has been passed to the script at the time that it was scheduled. The
* argument value is returned as a string, and a null string is returned
* if the parameter was not specified.</LI>
* <LI>getScriptArgument(string name, string defaultValue) -- Retrieves the
* specified argument that has been passed to the script at the time that
* it was scheduled. The argument value is returned as a string, and the
* default value is returned if the parameter was not specified.</LI>
* <LI>getScriptBooleanArgument(string name, boolean defaultValue) --
* Retrieves the boolean value of the specified argument that has been
* passed to the script at the time that it was scheduled. The argument
* value is returned as a boolean, and the default value is returned if
* the argument was not specified or could not be converted to a
* boolean.</LI>
* <LI>getScriptIntArgument(string name, int defaultValue) -- Retrieves the
* integer value of the specified argument that has been passed to the
* script at the time that it was scheduled. The argument value is
* returned as an integer, and the default value is returned if the
* parameter was not specified or could not be converted to an
* integer.</LI>
* <LI>logMessage(string message) -- Writes the specified message to the SLAMD
* log. This method does not return a value.</LI>
* <LI>randomInt(int lowerBound, int upperBound) -- Retrieves a random integer
* value between the specified lower and upper bounds (inclusive).</LI>
* <LI>randomString(int length) -- Retrieves a random string value with the
* specified number of alphabetic characters.</LI>
* <LI>randomString(int length, string characterSet) -- Retrieves a random
* string value with the specified number of characters from the provided
* character set.</LI>
* <LI>shouldNotStop() -- Indicates whether the script has not yet been
* requested to stop running.</LI>
* <LI>shouldStop() -- Indicates whether the script should stop running.</LI>
* <LI>sleep(int milliseconds) -- Causes the execution of the script to pause
* for the specified number of milliseconds. This method does not return
* a value.</LI>
* <LI>timestamp() -- Retrieves a string value that contains a timestamp in
* YYYYMMDDhhmmss format.</LI>
* <LI>timestamp(string format) -- Retrieves a string value contains a
* timestamp using the specified format. The format used should be in the
* form required by the <CODE>java.text.SimpleDateFormat</CODE> class.
* </UL>
*
*
* @author Neil A. Wilson
*/
public class ScriptVariable
extends Variable
{
/**
* The set of characters that will be used to generate random string values.
*/
public static final char[] ALPHABET =
"abcdefghijklmnopqrstuvwxyz".toCharArray();
/**
* The name of the method that can be used to write debug messages to the
* client's debug writer.
*/
public static final String DEBUG_MESSAGE_METHOD_NAME = "debugmessage";
/**
* The method number for the "debugMessage" method.
*/
public static final int DEBUG_MESSAGE_METHOD_NUMBER = 0;
/**
* The name of the method that will be used to stop execution of the script.
*/
public static final String EXIT_METHOD_NAME = "exit";
/**
* The method number for the "exit" method.
*/
public static final int EXIT_METHOD_NUMBER = 1;
/**
* The name of the method that will be used to stop execution of the script
* and indicate that an error occurred.
*/
public static final String EXIT_WITH_ERROR_METHOD_NAME = "exitwitherror";
/**
* The method number for the "exitWithError" method.
*/
public static final int EXIT_WITH_ERROR_METHOD_NUMBER = 2;
/**
* The name of the method that will be used to get argument information
* provided at the time the job was scheduled.
*/
public static final String GET_SCRIPT_ARGUMENT_METHOD_NAME =
"getscriptargument";
/**
* The method number for the first "getScriptArgument" method.
*/
public static final int GET_SCRIPT_ARGUMENT_1_METHOD_NUMBER = 3;
/**
* The method number for the second "getScriptArgument" method.
*/
public static final int GET_SCRIPT_ARGUMENT_2_METHOD_NUMBER = 4;
/**
* The name of the method that will be used to get the boolean value of an
* argument provided at the time the job was scheduled.
*/
public static final String GET_SCRIPT_BOOLEAN_ARGUMENT_METHOD_NAME =
"getscriptbooleanargument";
/**
* The method number for the "getScriptBooleanArgument" method.
*/
public static final int GET_SCRIPT_BOOLEAN_ARGUMENT_METHOD_NUMBER = 5;
/**
* The name of the method that will be used to get the integer value of an
* argument provided at the time the job was scheduled.
*/
public static final String GET_SCRIPT_INT_ARGUMENT_METHOD_NAME =
"getscriptintargument";
/**
* The method number for the "getScriptIntArgument" method.
*/
public static final int GET_SCRIPT_INT_ARGUMENT_METHOD_NUMBER = 6;
/**
* The name of the method that will be used to write messages to the SLAMD
* log.
*/
public static final String LOG_MESSAGE_METHOD_NAME = "logmessage";
/**
* The method number for the "logMessage" method.
*/
public static final int LOG_MESSAGE_METHOD_NUMBER = 7;
/**
* The name of the method that will be used to generate a random integer
* value.
*/
public static final String RANDOM_INT_METHOD_NAME = "randomint";
/**
* The method number for the "randomInt" method.
*/
public static final int RANDOM_INT_METHOD_NUMBER = 8;
/**
* The name of the method that will be used to generate a random string
* value.
*/
public static final String RANDOM_STRING_METHOD_NAME = "randomstring";
/**
* The method number for the first "randomString" method.
*/
public static final int RANDOM_STRING_1_METHOD_NUMBER = 9;
/**
* The method number for the second "randomString" method.
*/
public static final int RANDOM_STRING_2_METHOD_NUMBER = 10;
/**
* The name of the method that will be used to determine whether the script
* has not yet been requested to stop running.
*/
public static final String SHOULD_NOT_STOP_METHOD_NAME = "shouldnotstop";
/**
* The method number for the "shouldNotStop" method.
*/
public static final int SHOULD_NOT_STOP_METHOD_NUMBER = 11;
/**
* The name of the method that will be used to determine whether the script
* should stop running.
*/
public static final String SHOULD_STOP_METHOD_NAME = "shouldstop";
/**
* The method number for the "shouldStop" method.
*/
public static final int SHOULD_STOP_METHOD_NUMBER = 12;
/**
* The name of the method that will be used to pause execution of the script.
*/
public static final String SLEEP_METHOD_NAME = "sleep";
/**
* The method number for the "sleep" method.
*/
public static final int SLEEP_METHOD_NUMBER = 13;
/**
* The name of the method that will be used to generate a timestamp.
*/
public static final String TIMESTAMP_METHOD_NAME = "timestamp";
/**
* The method number for the first "timestamp" method.
*/
public static final int TIMESTAMP_1_METHOD_NUMBER = 14;
/**
* The method number for the second "timestamp" method.
*/
public static final int TIMESTAMP_2_METHOD_NUMBER = 15;
/**
* The name of the method that will be used to obtain the client number.
*/
public static final String GET_CLIENT_NUMBER_METHOD_NAME = "getclientnumber";
/**
* The method number for the "getClientNumber" method.
*/
public static final int GET_CLIENT_NUMBER_METHOD_NUMBER = 16;
/**
* The name of the method that will be used to obtain the thread number.
*/
public static final String GET_THREAD_NUMBER_METHOD_NAME = "getthreadnumber";
/**
* The method number for the "getThreadNumber" method.
*/
public static final int GET_THREAD_NUMBER_METHOD_NUMBER = 17;
/**
* The set of methods associated with script variables.
*/
public static final Method[] SCRIPT_VARIABLE_METHODS = new Method[]
{
new Method(DEBUG_MESSAGE_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE }, null),
new Method(EXIT_METHOD_NAME, new String[0], null),
new Method(EXIT_WITH_ERROR_METHOD_NAME, new String[0], null),
new Method(GET_SCRIPT_ARGUMENT_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE },
StringVariable.STRING_VARIABLE_TYPE),
new Method(GET_SCRIPT_ARGUMENT_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE,
StringVariable.STRING_VARIABLE_TYPE },
StringVariable.STRING_VARIABLE_TYPE),
new Method(GET_SCRIPT_BOOLEAN_ARGUMENT_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE,
BooleanVariable.BOOLEAN_VARIABLE_TYPE },
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(GET_SCRIPT_INT_ARGUMENT_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE,
IntegerVariable.INTEGER_VARIABLE_TYPE },
IntegerVariable.INTEGER_VARIABLE_TYPE),
new Method(LOG_MESSAGE_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE }, null),
new Method(RANDOM_INT_METHOD_NAME,
new String[] { IntegerVariable.INTEGER_VARIABLE_TYPE,
IntegerVariable.INTEGER_VARIABLE_TYPE },
IntegerVariable.INTEGER_VARIABLE_TYPE),
new Method(RANDOM_STRING_METHOD_NAME,
new String[] { IntegerVariable.INTEGER_VARIABLE_TYPE },
StringVariable.STRING_VARIABLE_TYPE),
new Method(RANDOM_STRING_METHOD_NAME,
new String[] { IntegerVariable.INTEGER_VARIABLE_TYPE,
StringVariable.STRING_VARIABLE_TYPE },
StringVariable.STRING_VARIABLE_TYPE),
new Method(SHOULD_NOT_STOP_METHOD_NAME, new String[0],
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(SHOULD_STOP_METHOD_NAME, new String[0],
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(SLEEP_METHOD_NAME,
new String[] { IntegerVariable.INTEGER_VARIABLE_TYPE }, null),
new Method(TIMESTAMP_METHOD_NAME, new String[0],
StringVariable.STRING_VARIABLE_TYPE),
new Method(TIMESTAMP_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE },
StringVariable.STRING_VARIABLE_TYPE),
new Method(GET_CLIENT_NUMBER_METHOD_NAME, new String[0],
IntegerVariable.INTEGER_VARIABLE_TYPE),
new Method(GET_THREAD_NUMBER_METHOD_NAME, new String[0],
IntegerVariable.INTEGER_VARIABLE_TYPE)
};
private static Random parentRandom = new Random();
// The job thread that is being used to run the SLAMD script.
private JobClass jobThread;
// The random number generator used by this variable.
private Random random;
// The script parser with which this script variable is associated.
private ScriptParser parser;
// The date formatter that will be used to obtain timestamp values.
private SimpleDateFormat timestampFormat;
/**
* Creates a new variable with no name, to be used only when creating a
* variable with <CODE>Class.newInstance()</CODE>, and only when
* <CODE>setName()</CODE> is called after that to set the name.
*
* @throws ScriptException If a problem occurs while initializing the new
* variable.
*/
public ScriptVariable()
throws ScriptException
{
// Seed the random number generator.
random = new Random(parentRandom.nextLong());
// Initialize the date formatter.
timestampFormat = new SimpleDateFormat(Constants.ATTRIBUTE_DATE_FORMAT);
}
/**
* Associates this instance of the script variable with the job thread that is
* running it.
*
* @param jobThread THe job thread that is running this script.
*/
public void setJobThread(JobClass jobThread)
{
this.jobThread = jobThread;
}
/**
* Associates this instance of the script variable with the script parser in
* which it is defined.
*
* @param parser The script parser with which this script variable is being
* used.
*/
public void setParser(ScriptParser parser)
{
this.parser = parser;
}
/**
* Retrieves the name of the variable type for this variable.
*
* @return The name of the variable type for this variable.
*/
@Override()
public String getVariableTypeName()
{
return "script";
}
/**
* Retrieves a list of all methods defined for this variable.
*
* @return A list of all methods defined for this variable.
*/
@Override()
public Method[] getMethods()
{
return SCRIPT_VARIABLE_METHODS;
}
/**
* Indicates whether this variable type has a method with the specified name.
*
* @param methodName The name of the method.
*
* @return <CODE>true</CODE> if this variable has a method with the specified
* name, or <CODE>false</CODE> if it does not.
*/
@Override()
public boolean hasMethod(String methodName)
{
for (int i=0; i < SCRIPT_VARIABLE_METHODS.length; i++)
{
if (SCRIPT_VARIABLE_METHODS[i].getName().equals(methodName))
{
return true;
}
}
return false;
}
/**
* Retrieves the method number for the method that has the specified name and
* argument types, or -1 if there is no such method.
*
* @param methodName The name of the method.
* @param argumentTypes The list of argument types for the method.
*
* @return The method number for the method that has the specified name and
* argument types.
*/
@Override()
public int getMethodNumber(String methodName, String[] argumentTypes)
{
for (int i=0; i < SCRIPT_VARIABLE_METHODS.length; i++)
{
if (SCRIPT_VARIABLE_METHODS[i].hasSignature(methodName, argumentTypes))
{
return i;
}
}
return -1;
}
/**
* Retrieves the return type for the method with the specified name and
* argument types.
*
* @param methodName The name of the method.
* @param argumentTypes The set of argument types for the method.
*
* @return The return type for the method, or <CODE>null</CODE> if there is
* no such method defined.
*/
@Override()
public String getReturnTypeForMethod(String methodName,
String[] argumentTypes)
{
for (int i=0; i < SCRIPT_VARIABLE_METHODS.length; i++)
{
if (SCRIPT_VARIABLE_METHODS[i].hasSignature(methodName, argumentTypes))
{
return SCRIPT_VARIABLE_METHODS[i].getReturnType();
}
}
return null;
}
/**
* Executes the specified method, using the provided variables as arguments
* to the method, and makes the return value available to the caller.
*
* @param lineNumber The line number of the script in which the method
* call occurs.
* @param methodNumber The method number of the method to execute.
* @param arguments The set of arguments to use for the method.
*
* @return The value returned from the method, or <CODE>null</CODE> if it
* does not return a value.
*
* @throws ScriptException If the specified method does not exist, or if a
* problem occurs while attempting to execute it.
*/
@Override()
public Variable executeMethod(int lineNumber, int methodNumber,
Argument[] arguments)
throws ScriptException
{
switch (methodNumber)
{
case DEBUG_MESSAGE_METHOD_NUMBER:
// Get the provided string value.
StringVariable sv = (StringVariable) arguments[0].getArgumentValue();
// Log the message and don't return a value.
jobThread.writeVerbose(sv.getStringValue());
return null;
case EXIT_METHOD_NUMBER:
// Stop the job and don't return a value.
jobThread.stopJob(Constants.JOB_STATE_COMPLETED_SUCCESSFULLY);
throw new StopRunningException(lineNumber);
case EXIT_WITH_ERROR_METHOD_NUMBER:
// Stop the job and don't return a value.
jobThread.stopJob(Constants.JOB_STATE_COMPLETED_WITH_ERRORS);
jobThread.indicateStoppedDueToError();
throw new StopRunningException(lineNumber);
case GET_SCRIPT_ARGUMENT_1_METHOD_NUMBER:
// Determine which parameter is being requested.
sv = (StringVariable) arguments[0].getArgumentValue();
String argName = sv.getStringValue().toLowerCase();
// Retrieve the requested parameter value, or a null string.
return new StringVariable(parser.getScriptArgument(argName));
case GET_SCRIPT_ARGUMENT_2_METHOD_NUMBER:
// Determine which parameter is being requested and the default value to
// use.
sv = (StringVariable) arguments[0].getArgumentValue();
StringVariable sv2 = (StringVariable) arguments[1].getArgumentValue();
argName = sv.getStringValue().toLowerCase();
// Retrieve the requested parameter value, or the default string.
String value = parser.getScriptArgument(argName);
if (value == null)
{
return new StringVariable(sv2.getStringValue());
}
else
{
return new StringVariable(value);
}
case GET_SCRIPT_BOOLEAN_ARGUMENT_METHOD_NUMBER:
// Determine which parameter is being requested and the default value to
// use.
sv = (StringVariable) arguments[0].getArgumentValue();
BooleanVariable bv = (BooleanVariable) arguments[1].getArgumentValue();
argName = sv.getStringValue().toLowerCase();
// Retrieve the requested parameter value, or the default string.
value = parser.getScriptArgument(argName);
if (value == null)
{
return bv;
}
else
{
try
{
return new BooleanVariable(Boolean.valueOf(value));
}
catch (Exception e)
{
return bv;
}
}
case GET_SCRIPT_INT_ARGUMENT_METHOD_NUMBER:
// Determine which parameter is being requested and the default value to
// use.
sv = (StringVariable) arguments[0].getArgumentValue();
IntegerVariable iv = (IntegerVariable) arguments[1].getArgumentValue();
argName = sv.getStringValue().toLowerCase();
// Retrieve the requested parameter value, or the default string.
value = parser.getScriptArgument(argName);
if (value == null)
{
return iv;
}
else
{
try
{
return new IntegerVariable(Integer.parseInt(value));
}
catch (Exception e)
{
return iv;
}
}
case LOG_MESSAGE_METHOD_NUMBER:
// Get the provided string value.
sv = (StringVariable) arguments[0].getArgumentValue();
// Log the message and don't return a value.
jobThread.logMessage(sv.getStringValue());
return null;
case RANDOM_INT_METHOD_NUMBER:
// Get the provided integer values.
iv = (IntegerVariable) arguments[0].getArgumentValue();
IntegerVariable iv2 = (IntegerVariable) arguments[1].getArgumentValue();
int lowerBound = iv.getIntValue();
int upperBound = iv2.getIntValue();
// Generate and return the random integer.
int span = upperBound - lowerBound + 1;
return new IntegerVariable(Math.abs(random.nextInt()) % span +
lowerBound);
case RANDOM_STRING_1_METHOD_NUMBER:
// Get the provided integer value.
iv = (IntegerVariable) arguments[0].getArgumentValue();
// Generate and return the random string.
char[] randomChars = new char[iv.getIntValue()];
for (int i=0; i < randomChars.length; i++)
{
randomChars[i] = ALPHABET[Math.abs(random.nextInt()) %
ALPHABET.length];
}
return new StringVariable(new String(randomChars));
case RANDOM_STRING_2_METHOD_NUMBER:
// Get the provided integer value.
iv = (IntegerVariable) arguments[0].getArgumentValue();
sv = (StringVariable) arguments[1].getArgumentValue();
// Generate and return the random string.
char[] charSet = sv.getStringValue().toCharArray();
randomChars = new char[iv.getIntValue()];
for (int i=0; i < randomChars.length; i++)
{
randomChars[i] = charSet[Math.abs(random.nextInt()) % charSet.length];
}
return new StringVariable(new String(randomChars));
case SHOULD_NOT_STOP_METHOD_NUMBER:
// Make the determination and return the result as a Boolean value.
return new BooleanVariable(! jobThread.shouldStop());
case SHOULD_STOP_METHOD_NUMBER:
// Make the determination and return the result as a Boolean value.
return new BooleanVariable(jobThread.shouldStop());
case SLEEP_METHOD_NUMBER:
// Get the provided integer value.
iv = (IntegerVariable) arguments[0].getArgumentValue();
// Sleep for the specified length of time and don't return a value.
try
{
Thread.sleep(iv.getIntValue());
} catch (InterruptedException ie) {}
return null;
case TIMESTAMP_1_METHOD_NUMBER:
// Generate and return the timestamp.
return new StringVariable(timestampFormat.format(new Date()));
case TIMESTAMP_2_METHOD_NUMBER:
// Generate and return the timestamp.
sv = (StringVariable) arguments[0].getArgumentValue();
SimpleDateFormat dateFormat = new SimpleDateFormat(sv.getStringValue());
return new StringVariable(dateFormat.format(new Date()));
case GET_CLIENT_NUMBER_METHOD_NUMBER:
// Return the client number.
return new IntegerVariable(jobThread.getClientNumber());
case GET_THREAD_NUMBER_METHOD_NUMBER:
// Return the client number.
return new IntegerVariable(jobThread.getThreadNumber());
default:
throw new ScriptException(lineNumber,
"There is no method " + methodNumber +
" defined for " + getArgumentType() +
" variables.");
}
}
/**
* Assigns the value of the provided argument to this variable. The value of
* the provided argument must be of the same type as this variable.
*
* @param argument The argument whose value should be assigned to this
* variable.
*
* @throws ScriptException If a problem occurs while performing the
* assignment.
*/
@Override()
public void assign(Argument argument)
throws ScriptException
{
// Script variables cannot be assigned.
throw new ScriptException("Attempt to assign a script variable rejected.");
}
/**
* Retrieves a string representation of the value of this argument.
*
* @return A string representation of the value of this argument.
*/
public String getValueAsString()
{
return "script";
}
}