/*
* 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.jdbc;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import com.slamd.scripting.engine.Argument;
import com.slamd.scripting.engine.Method;
import com.slamd.scripting.engine.ScriptException;
import com.slamd.scripting.engine.Variable;
import com.slamd.scripting.general.BooleanVariable;
import com.slamd.scripting.general.IntegerVariable;
import com.slamd.scripting.general.StringVariable;
/**
* This class defines a variable that provides a set of methods for interacting
* with a JDBC prepared statement. Those methods are:
*
* <UL>
* <LI>executeQuery() -- Executes the associated query and returns the result
* set of matching records.</LI>
* <LI>executeUpdate() -- Executes the associated update and returns an
* integer value that contains the row count for the update.</LI>
* <LI>getFailureReason() -- Retrieves a string containing the reason that the
* last operation failed.</LI>
* <LI>isNull() -- Retrieves a Boolean value that indicates whether this is a
* null prepared statement.</LI>
* <LI>setBoolean(int index, Boolean value) -- Updates the SQL for this
* prepared statement to place the provided Boolean value in the position
* of the specified placeholder. This method returns a Boolean value that
* indicates whether the update was successful.</LI>
* <LI>setInteger(int index, int value) -- Updates the SQL for this prepared
* statement to place the provided integer value in the position of the
* specified placeholder. This method returns a Boolean value that
* indicates whether the update was successful.</LI>
* <LI>setString(int index, string value) -- Updates the SQL for this prepared
* statement to place the provided string value in the position of the
* specified placeholder. This method returns a Boolean value that
* indicates whether the update was successful.</LI>
* </UL>
*
*
* @author Neil A. Wilson
*/
public class JDBCPreparedStatementVariable
extends Variable
{
/**
* The name that will be used for the data type of JDBC prepared statement
* variables.
*/
public static final String JDBC_PREPARED_STATEMENT_VARIABLE_TYPE =
"jdbcpreparedstatement";
/**
* The name of the method that can be used to execute a query using this
* prepared statement.
*/
public static final String EXECUTE_QUERY_METHOD_NAME = "executequery";
/**
* The method number for the "executeQuery" method.
*/
public static final int EXECUTE_QUERY_METHOD_NUMBER = 0;
/**
* The name of the method that can be used to execute an update using this
* prepared statement.
*/
public static final String EXECUTE_UPDATE_METHOD_NAME = "executeupdate";
/**
* The method number for the "executeUpdate" method.
*/
public static final int EXECUTE_UPDATE_METHOD_NUMBER = 1;
/**
* The name of the method that can be used to determine the reason for the
* last failure.
*/
public static final String GET_FAILURE_REASON_METHOD_NAME =
"getfailurereason";
/**
* The method number for the "getFailureReason" method.
*/
public static final int GET_FAILURE_REASON_METHOD_NUMBER = 2;
/**
* The name of the method that can be used to determine whether this is a null
* prepared statement.
*/
public static final String IS_NULL_METHOD_NAME = "isnull";
/**
* The method number for the "isNull" method.
*/
public static final int IS_NULL_METHOD_NUMBER = 3;
/**
* The name of the method that can be used to specify the boolean value to use
* in place of a given placeholder.
*/
public static final String SET_BOOLEAN_METHOD_NAME = "setboolean";
/**
* The method number for the "setBoolean" method.
*/
public static final int SET_BOOLEAN_METHOD_NUMBER = 4;
/**
* The name of the method that can be used to specify the integer value to use
* in place of a given placeholder.
*/
public static final String SET_INTEGER_METHOD_NAME = "setinteger";
/**
* The method number for the "setInteger" method.
*/
public static final int SET_INTEGER_METHOD_NUMBER = 5;
/**
* The name of the method that can be used to specify the string value to use
* in place of a given placeholder.
*/
public static final String SET_STRING_METHOD_NAME = "setstring";
/**
* The method number for the "setString" method.
*/
public static final int SET_STRING_METHOD_NUMBER = 6;
/**
* The set of methods associated with JDBC prepared statement variables.
*/
public static final Method[] JDBC_PREPARED_STATEMENT_VARIABLE_METHODS =
new Method[]
{
new Method(EXECUTE_QUERY_METHOD_NAME, new String[0],
JDBCResultSetVariable.JDBC_RESULT_SET_VARIABLE_TYPE),
new Method(EXECUTE_UPDATE_METHOD_NAME, new String[0],
IntegerVariable.INTEGER_VARIABLE_TYPE),
new Method(GET_FAILURE_REASON_METHOD_NAME, new String[0],
StringVariable.STRING_VARIABLE_TYPE),
new Method(IS_NULL_METHOD_NAME, new String[0],
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(SET_BOOLEAN_METHOD_NAME,
new String[] { IntegerVariable.INTEGER_VARIABLE_TYPE,
BooleanVariable.BOOLEAN_VARIABLE_TYPE },
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(SET_INTEGER_METHOD_NAME,
new String[] { IntegerVariable.INTEGER_VARIABLE_TYPE,
IntegerVariable.INTEGER_VARIABLE_TYPE },
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(SET_STRING_METHOD_NAME,
new String[] { IntegerVariable.INTEGER_VARIABLE_TYPE,
StringVariable.STRING_VARIABLE_TYPE },
BooleanVariable.BOOLEAN_VARIABLE_TYPE)
};
// The actual JDBC prepared statement that we will use to perform all
// processing.
private PreparedStatement preparedStatement;
// The variable that holds the reason for the last failure.
private String failureReason;
// The SQL statement associated with this prepared statement variable.
private String sql;
/**
* 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 JDBCPreparedStatementVariable()
throws ScriptException
{
preparedStatement = null;
sql = null;
failureReason = null;
}
/**
* Creates a new JDBC prepared statement variable using the provided
* information.
*
* @param sql The SQL statement to use for this prepared
* statement.
* @param preparedStatement The prepared statement to use for this
* prepared statement.
*/
public JDBCPreparedStatementVariable(String sql,
PreparedStatement preparedStatement)
{
this.sql = sql;
this.preparedStatement = preparedStatement;
failureReason = null;
}
/**
* 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 JDBC_PREPARED_STATEMENT_VARIABLE_TYPE;
}
/**
* 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 JDBC_PREPARED_STATEMENT_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 < JDBC_PREPARED_STATEMENT_VARIABLE_METHODS.length; i++)
{
if (JDBC_PREPARED_STATEMENT_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 < JDBC_PREPARED_STATEMENT_VARIABLE_METHODS.length; i++)
{
if (JDBC_PREPARED_STATEMENT_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 < JDBC_PREPARED_STATEMENT_VARIABLE_METHODS.length; i++)
{
if (JDBC_PREPARED_STATEMENT_VARIABLE_METHODS[i].hasSignature(methodName,
argumentTypes))
{
return JDBC_PREPARED_STATEMENT_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 EXECUTE_QUERY_METHOD_NUMBER:
if (preparedStatement == null)
{
failureReason = "The prepared statement has not been initialized.";
return new JDBCResultSetVariable(sql, null);
}
try
{
ResultSet resultSet = preparedStatement.executeQuery();
failureReason = null;
return new JDBCResultSetVariable(sql, resultSet);
}
catch (Exception e)
{
failureReason = "Caught Exception: " + e;
return new JDBCResultSetVariable(sql, null);
}
case EXECUTE_UPDATE_METHOD_NUMBER:
if (preparedStatement == null)
{
failureReason = "The prepared statement has not been initialized.";
return new IntegerVariable(-1);
}
try
{
int rowCount = preparedStatement.executeUpdate();
failureReason = null;
return new IntegerVariable(rowCount);
}
catch (Exception e)
{
failureReason = "Caught Exception: " + e;
return new IntegerVariable(-1);
}
case GET_FAILURE_REASON_METHOD_NUMBER:
return new StringVariable(failureReason);
case IS_NULL_METHOD_NUMBER:
failureReason = null;
return new BooleanVariable(preparedStatement == null);
case SET_BOOLEAN_METHOD_NUMBER:
IntegerVariable iv = (IntegerVariable) arguments[0].getArgumentValue();
BooleanVariable bv = (BooleanVariable) arguments[1].getArgumentValue();
if (preparedStatement == null)
{
failureReason = "The prepared statement has not been initialized.";
return new BooleanVariable(false);
}
try
{
preparedStatement.setBoolean(iv.getIntValue(), bv.getBooleanValue());
failureReason = null;
return new BooleanVariable(true);
}
catch (Exception e)
{
failureReason = "Caught Exception: " + e;
return new BooleanVariable(false);
}
case SET_INTEGER_METHOD_NUMBER:
IntegerVariable iv1 = (IntegerVariable) arguments[0].getArgumentValue();
IntegerVariable iv2 = (IntegerVariable) arguments[1].getArgumentValue();
if (preparedStatement == null)
{
failureReason = "The prepared statement has not been initialized.";
return new BooleanVariable(false);
}
try
{
preparedStatement.setInt(iv1.getIntValue(), iv2.getIntValue());
failureReason = null;
return new BooleanVariable(true);
}
catch (Exception e)
{
failureReason = "Caught Exception: " + e;
return new BooleanVariable(false);
}
case SET_STRING_METHOD_NUMBER:
iv = (IntegerVariable) arguments[0].getArgumentValue();
StringVariable sv = (StringVariable) arguments[1].getArgumentValue();
if (preparedStatement == null)
{
failureReason = "The prepared statement has not been initialized.";
return new BooleanVariable(false);
}
try
{
preparedStatement.setString(iv.getIntValue(), sv.getStringValue());
failureReason = null;
return new BooleanVariable(true);
}
catch (Exception e)
{
failureReason = "Caught Exception: " + e;
return new BooleanVariable(false);
}
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
{
if (! argument.getArgumentType().equals(
JDBC_PREPARED_STATEMENT_VARIABLE_TYPE))
{
throw new ScriptException("Attempt to assign an argument of type " +
argument.getArgumentType() +
" to a variable of type " +
JDBC_PREPARED_STATEMENT_VARIABLE_TYPE +
" rejected.");
}
JDBCPreparedStatementVariable jpsv =
(JDBCPreparedStatementVariable) argument.getArgumentValue();
preparedStatement = jpsv.preparedStatement;
sql = jpsv.sql;
failureReason = jpsv.failureReason;
}
/**
* Retrieves a string representation of the value of this argument.
*
* @return A string representation of the value of this argument.
*/
public String getValueAsString()
{
if (sql == null)
{
return "JDBC Prepared Statement";
}
else
{
return "JDBC Prepared Statement " + sql;
}
}
}