Index: C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties
===================================================================
--- C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties (revision 466937)
+++ C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties (working copy)
@@ -8,3 +8,9 @@
queryType.shortDescription=is true, se lanzar\u00E1 como una query y no como un update/inser. Si no, se lanza como update.
sql.displayName=Query SQL
varName.displayName=Nombre de Variable Ligada al Pool
+queryArguments.displayName=Argumentos
+queryArguments.shortDescription=los valores de los argumentos separados por comas
+queryArgumentsTypes.displayName=Tipos de los argumentos
+queryArgumentsTypes.shortDescription=los valores de los argumentos separados por comas
+
+
Index: C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java
===================================================================
--- C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java (revision 466937)
+++ C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java (working copy)
@@ -17,12 +17,19 @@
package org.apache.jmeter.protocol.jdbc.sampler;
+import java.lang.reflect.Field;
import java.sql.CallableStatement;
import java.sql.Connection;
+import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
import org.apache.avalon.excalibur.datasource.DataSourceComponent;
import org.apache.jmeter.samplers.AbstractSampler;
@@ -40,18 +47,44 @@
* @author Jeremy Arnold
*/
public class JDBCSampler extends AbstractSampler implements TestBean {
+ private static final int MAX_ENTRIES = 200;
+
private static final Logger log = LoggingManager.getLoggerForClass();
+ static Map mapJdbcNameToInt;
// Query types (used to communicate with GUI)
static final String SELECT = "Select Statement";
static final String UPDATE = "Update Statement";
static final String CALLABLE = "Callable Statement";
+ static final String PREPARED_SELECT = "Prepared Select Statement";
+ static final String PREPARED_UPDATE = "Prepared Update Statement";
private String query = "";
private String dataSource = "";
private String queryType = SELECT;
+ private String queryArguments = "";
+ private String queryArgumentsTypes = "";
+
+ /**
+ * Cache of PreparedStatements stored in a per-connection basis. Each entry of this
+ * cache is another Map mapping the statemente string to the actual PreparedStatement.
+ * The cache has a fixed size of MAX_ENTRIES and it will throw aways all PreparedStatements
+ * from the least recently used connections.
+ */
+ private static Map perConnCache = new LinkedHashMap(MAX_ENTRIES){
+ protected boolean removeEldestEntry(java.util.Map.Entry arg0) {
+ if (size() > MAX_ENTRIES) {
+ final Object value = arg0.getValue();
+ if (value instanceof Map) {
+ closeAllStatements(((Map)value).values());
+ }
+ return true;
+ }
+ return false;
+ }
+ };
/**
* Creates a JDBCSampler.
@@ -78,7 +111,7 @@
log.debug("DataSourceComponent: " + pool);
Connection conn = null;
Statement stmt = null;
- CallableStatement cs = null;
+
try {
@@ -88,11 +121,11 @@
// TODO: Consider creating a sub-result with the time to get the
// connection.
conn = pool.getConnection();
- stmt = conn.createStatement();
// Based on query return value, get results
String _queryType = getQueryType();
if (SELECT.equals(_queryType)) {
+ stmt = conn.createStatement();
ResultSet rs = null;
try {
rs = stmt.executeQuery(getQuery());
@@ -101,25 +134,32 @@
} finally {
close(rs);
}
- } else if (CALLABLE.equals(_queryType)) {
- cs = conn.prepareCall(getQuery());
- boolean hasResultSet = cs.execute();
- if (hasResultSet){
- ResultSet rs=cs.getResultSet();
- Data data = getDataFromResultSet(rs);
- res.setResponseData(data.toString().getBytes());
- } else {
- int updateCount = cs.getUpdateCount();
- String results = updateCount + " updates";
- res.setResponseData(results.getBytes());
- }
- //TODO process additional results (if any) using getMoreResults()
+ } else if (CALLABLE.equals(_queryType)) {
+ CallableStatement cstmt = getCallableStatement(conn);
+ setArguments(cstmt);
+ // A CallableStatement can return more than 1 ResultSets
+ // plus a number of update counts.
+ boolean hasResultSet = cstmt.execute();
+ String sb = resultSetsToString(cstmt,hasResultSet);
+ res.setResponseData(sb.toString().getBytes());
} else if (UPDATE.equals(_queryType)) {
- stmt.execute(getQuery());
+ stmt = conn.createStatement();
+ stmt.executeUpdate(getQuery());
int updateCount = stmt.getUpdateCount();
String results = updateCount + " updates";
res.setResponseData(results.getBytes());
- // TODO add support for PreparedStatments
+ } else if (PREPARED_SELECT.equals(_queryType)) {
+ PreparedStatement pstmt = getPreparedStatement(conn);
+ setArguments(pstmt);
+ pstmt.executeQuery();
+ String sb = resultSetsToString(pstmt,true);
+ res.setResponseData(sb.toString().getBytes());
+ } else if (PREPARED_UPDATE.equals(_queryType)) {
+ PreparedStatement pstmt = getPreparedStatement(conn);
+ setArguments(pstmt);
+ pstmt.executeUpdate();
+ String sb = resultSetsToString(pstmt,false);
+ res.setResponseData(sb.toString().getBytes());
} else { // User provided incorrect query type
String results="Unexpected query type: "+_queryType;
res.setResponseMessage(results);
@@ -131,7 +171,6 @@
res.setResponseMessage(ex.toString());
res.setSuccessful(false);
} finally {
- close(cs);
close(stmt);
close(conn);
}
@@ -140,6 +179,141 @@
return res;
}
+ private String resultSetsToString(PreparedStatement pstmt, boolean result) throws SQLException {
+ StringBuffer sb = new StringBuffer();
+ sb.append("\n");
+ int updateCount = 0;
+ if (!result) {
+ updateCount = pstmt.getUpdateCount();
+ }
+ do {
+ if (result) {
+ ResultSet rs = null;
+ try {
+ rs = pstmt.getResultSet();
+ Data data = getDataFromResultSet(rs);
+ sb.append(data.toString()).append("\n");
+ } finally {
+ close(rs);
+ }
+ } else {
+ sb.append(updateCount).append(" updates.\n");
+ }
+ result = pstmt.getMoreResults();
+ if (!result) {
+ updateCount = pstmt.getUpdateCount();
+ }
+ } while (result || (updateCount != -1));
+ return sb.toString();
+ }
+
+
+ private void setArguments(PreparedStatement pstmt) throws SQLException {
+ if ("".equals(getQueryArguments().trim())) {
+ return;
+ }
+ String[] arguments = getQueryArguments().split(",");
+ String[] argumentsTypes = getQueryArgumentsTypes().split(",");
+ if (arguments.length != argumentsTypes.length) {
+ throw new SQLException("number of arguments ("+arguments.length+") and number of types ("+argumentsTypes.length+") are not equal");
+ }
+ for (int i = 0; i < arguments.length; i++) {
+ String argument = arguments[i];
+ String argumentType = argumentsTypes[i];
+ //TODO should be a more elegant way to do it
+ int targetSqlType = getJdbcType(argumentType);
+ pstmt.setObject(i+1, argument, targetSqlType);
+ }
+ }
+
+ public static int getJdbcType(String jdbcType) {
+ // based on e291. Getting the Name of a JDBC Type from javaalmanac.com
+ // http://javaalmanac.com/egs/java.sql/JdbcInt2Str.html
+ if (mapJdbcNameToInt == null) {
+ mapJdbcNameToInt = new HashMap();
+
+ //Get all field in java.sql.Types
+ Field[] fields = java.sql.Types.class.getFields();
+ for (int i=0; i MAX_ENTRIES) {
+ Object value = arg0.getValue();
+ if (value instanceof PreparedStatement) {
+ PreparedStatement pstmt = (PreparedStatement) value;
+ try {
+ pstmt.close();
+ } catch (SQLException e) {
+ // ignore this exception
+ e.printStackTrace();
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ };
+ perConnCache.put(conn, preparedStatementMap);
+ }
+ PreparedStatement pstmt = (PreparedStatement) preparedStatementMap.get(getQuery());
+ if (null == pstmt) {
+ if (callable) {
+ pstmt = conn.prepareCall(getQuery());
+ } else {
+ pstmt = conn.prepareStatement(getQuery());
+ }
+ preparedStatementMap.put(getQuery(), pstmt);
+ }
+ pstmt.clearParameters();
+ return pstmt;
+ }
+
+ private static void closeAllStatements(Collection collection) {
+ Iterator iterator = collection.iterator();
+ while (iterator.hasNext()) {
+ PreparedStatement pstmt = (PreparedStatement) iterator.next();
+ try {
+ pstmt.close();
+ } catch (SQLException e) {
+ // ignore this exception
+ e.printStackTrace();
+ }
+ }
+
+ }
+
/**
* Gets a Data object from a ResultSet.
*
@@ -171,7 +345,7 @@
}
return data;
}
-
+
public static void close(Connection c) {
try {
if (c != null) c.close();
@@ -184,18 +358,11 @@
try {
if (s != null) s.close();
} catch (SQLException e) {
- log.warn("Error closing Statement", e);
+ log.warn("Error closing Statement " + s.toString(), e);
}
}
- public static void close(CallableStatement cs) {
- try {
- if (cs != null) cs.close();
- } catch (SQLException e) {
- log.warn("Error closing CallableStatement", e);
- }
- }
-
+
public static void close(ResultSet rs) {
try {
if (rs != null) rs.close();
@@ -253,4 +420,20 @@
public void setQueryType(String queryType) {
this.queryType = queryType;
}
+
+ public String getQueryArguments() {
+ return queryArguments;
+ }
+
+ public void setQueryArguments(String queryArguments) {
+ this.queryArguments = queryArguments;
+ }
+
+ public String getQueryArgumentsTypes() {
+ return queryArgumentsTypes;
+ }
+
+ public void setQueryArgumentsTypes(String queryArgumentsType) {
+ this.queryArgumentsTypes = queryArgumentsType;
+ }
}
Index: C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java
===================================================================
--- C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java (revision 466937)
+++ C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java (working copy)
@@ -40,22 +40,33 @@
createPropertyGroup("varName", new String[] { "dataSource" });
- createPropertyGroup("sql", new String[] { "queryType", "query" });
+ createPropertyGroup("sql", new String[] { "queryType", "query", "queryArguments","queryArgumentsTypes" });
PropertyDescriptor p = property("dataSource");
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, "");
+ p = property("queryArguments");
+ p.setValue(NOT_UNDEFINED, Boolean.TRUE);
+ p.setValue(DEFAULT, "");
+
+ p = property("queryArgumentsTypes");
+ p.setValue(NOT_UNDEFINED, Boolean.TRUE);
+ p.setValue(DEFAULT, "");
+
+
p = property("queryType");
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, JDBCSampler.SELECT);
p.setValue(NOT_OTHER,Boolean.TRUE);
- p.setValue(TAGS,new String[]{JDBCSampler.SELECT,JDBCSampler.UPDATE,JDBCSampler.CALLABLE});
+ p.setValue(TAGS,new String[]{JDBCSampler.SELECT,JDBCSampler.UPDATE,JDBCSampler.CALLABLE, JDBCSampler.PREPARED_SELECT, JDBCSampler.PREPARED_UPDATE});
p = property("query");
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, "");
p.setPropertyEditorClass(TextAreaEditor.class);
+
+//
}
}
Index: C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties
===================================================================
--- C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties (revision 466937)
+++ C:/Documents and Settings/ecerulm/workspace32jmeter/JMeter2_2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties (working copy)
@@ -6,4 +6,10 @@
queryType.displayName=Query Type
queryType.shortDescription=Determines if the SQL statement should be run as a select statement or an update statement.
dataSource.displayName=Variable Name
-dataSource.shortDescription=Name of the JMeter variable that the connection pool is bound to.
\ No newline at end of file
+dataSource.shortDescription=Name of the JMeter variable that the connection pool is bound to.
+queryArguments.displayName=Parameters
+queryArguments.shortDescription=SQL parameter values
+queryArgumentsTypes.displayName=Parameter JDBC Type
+queryArgumentsTypes.shortDescription=JDBC Type names form java.sql.Types. VARCHAR, INTEGER, etc.
+
+