Author: sebb
Date: Sat Apr 14 18:52:38 2007
New Revision: 528906
URL: http://svn.apache.org/viewvc?view=rev&rev=528906
Log:
Bug 40825 - Add JDBC prepared statement support
Modified:
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties
Modified:
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java
URL:
http://svn.apache.org/viewvc/jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java?view=diff&rev=528906&r1=528905&r2=528906
==============================================================================
---
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java
(original)
+++
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java
Sat Apr 14 18:52:38 2007
@@ -18,12 +18,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;
@@ -43,16 +50,61 @@
public class JDBCSampler extends AbstractSampler implements TestBean {
private static final Logger log = LoggingManager.getLoggerForClass();
+ private static final int MAX_ENTRIES = 200;
+
+ private static final Map mapJdbcNameToInt;
+
+ static {
+ // based on e291. Getting the Name of a JDBC Type from javaalmanac.com
+ // http://javaalmanac.com/egs/java.sql/JdbcInt2Str.html
+ mapJdbcNameToInt = new HashMap();
+
+ //Get all fields in java.sql.Types and store the corresponding
int values
+ Field[] fields = java.sql.Types.class.getFields();
+ for (int i=0; i<fields.length; i++) {
+ try {
+ String name = fields[i].getName();
+ Integer value = (Integer)fields[i].get(null);
+ mapJdbcNameToInt.put(name.toLowerCase(),value);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
// 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 SELECT = "Select Statement"; // $NON-NLS-1$
+ static final String UPDATE = "Update Statement"; // $NON-NLS-1$
+ static final String CALLABLE = "Callable Statement"; // $NON-NLS-1$
+ static final String PREPARED_SELECT = "Prepared Select Statement"; //
$NON-NLS-1$
+ static final String PREPARED_UPDATE = "Prepared Update Statement"; //
$NON-NLS-1$
- private String query = "";
+ private String query = ""; // $NON-NLS-1$
- private String dataSource = "";
+ private String dataSource = ""; // $NON-NLS-1$
private String queryType = SELECT;
+ private String queryArguments = ""; // $NON-NLS-1$
+ private String queryArgumentsTypes = ""; // $NON-NLS-1$
+
+ /**
+ * Cache of PreparedStatements stored in a per-connection basis. Each
entry of this
+ * cache is another Map mapping the statement 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.
@@ -68,7 +120,7 @@
res.setSamplerData(toString());
res.setDataType(SampleResult.TEXT);
// Bug 31184 - make sure encoding is specified
- res.setDataEncoding(System.getProperty("file.encoding"));
+ res.setDataEncoding(System.getProperty("file.encoding")); //
$NON-NLS-1$
// Assume we will be successful
res.setSuccessful(true);
@@ -79,7 +131,6 @@
log.debug("DataSourceComponent: " + pool);
Connection conn = null;
Statement stmt = null;
- CallableStatement cs = null;
try {
@@ -89,11 +140,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());
@@ -102,25 +153,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);
@@ -132,7 +190,6 @@
res.setResponseMessage(ex.toString());
res.setSuccessful(false);
} finally {
- close(cs);
close(stmt);
close(conn);
}
@@ -141,6 +198,107 @@
return res;
}
+ private String resultSetsToString(PreparedStatement pstmt, boolean
result) throws SQLException {
+ StringBuffer sb = new StringBuffer();
+ sb.append("\n"); // $NON-NLS-1$
+ 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"); // $NON-NLS-1$
+ } 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 (getQueryArguments().trim().length()==0) {
+ return;
+ }
+ String[] arguments = getQueryArguments().split(","); //
$NON-NLS-1$
+ String[] argumentsTypes = getQueryArgumentsTypes().split(",");
// $NON-NLS-1$
+ 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];
+ int targetSqlType = getJdbcType(argumentType);
+ pstmt.setObject(i+1, argument, targetSqlType);
+ }
+ }
+
+
+ private static int getJdbcType(String jdbcType) {
+ return
((Integer)mapJdbcNameToInt.get(jdbcType.toLowerCase())).intValue();
+ }
+
+
+ private CallableStatement getCallableStatement(Connection conn) throws
SQLException {
+ return (CallableStatement) getPreparedStatement(conn,true);
+
+ }
+ private PreparedStatement getPreparedStatement(Connection conn) throws
SQLException {
+ return getPreparedStatement(conn,false);
+ }
+
+ private PreparedStatement getPreparedStatement(Connection conn, boolean
callable) throws SQLException {
+ Map preparedStatementMap = (Map) perConnCache.get(conn);
+ if (null == preparedStatementMap ) {
+ // MRU PreparedStatements cache.
+ preparedStatementMap = new LinkedHashMap(MAX_ENTRIES) {
+ protected boolean
removeEldestEntry(java.util.Map.Entry arg0) {
+ final int theSize = size();
+ if (theSize > MAX_ENTRIES) {
+ Object value = arg0.getValue();
+ if (value instanceof
PreparedStatement) {
+ PreparedStatement pstmt
= (PreparedStatement) value;
+ close(pstmt);
+ }
+ 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();
+ close(pstmt);
+ }
+ }
+
/**
* Gets a Data object from a ResultSet.
*
@@ -185,15 +343,7 @@
try {
if (s != null) s.close();
} catch (SQLException e) {
- log.warn("Error closing Statement", e);
- }
- }
-
- public static void close(CallableStatement cs) {
- try {
- if (cs != null) cs.close();
- } catch (SQLException e) {
- log.warn("Error closing CallableStatement", e);
+ log.warn("Error closing Statement " + s.toString(), e);
}
}
@@ -211,9 +361,9 @@
public String toString() {
StringBuffer sb = new StringBuffer(80);
- sb.append("[");
+ sb.append("["); // $NON-NLS-1$
sb.append(getQueryType());
- sb.append("] ");
+ sb.append("] "); // $NON-NLS-1$
sb.append(getQuery());
return sb.toString();
}
@@ -253,5 +403,21 @@
*/
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;
}
}
Modified:
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java
URL:
http://svn.apache.org/viewvc/jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java?view=diff&rev=528906&r1=528905&r2=528906
==============================================================================
---
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java
(original)
+++
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java
Sat Apr 14 18:52:38 2007
@@ -39,21 +39,37 @@
public JDBCSamplerBeanInfo() {
super(JDBCSampler.class);
- createPropertyGroup("varName", new String[] { "dataSource" });
+ createPropertyGroup("varName", // $NON-NLS-1$
+ new String[]{"dataSource" }); // $NON-NLS-1$
- createPropertyGroup("sql", new String[] { "queryType", "query"
});
+ createPropertyGroup("sql", // $NON-NLS-1$
+ new String[] {
+ "queryType", // $NON-NLS-1$
+ "query", // $NON-NLS-1$
+ "queryArguments", // $NON-NLS-1$
+ "queryArgumentsTypes" // $NON-NLS-1$
+ });
- PropertyDescriptor p = property("dataSource");
+ PropertyDescriptor p = property("dataSource"); // $NON-NLS-1$
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, "");
- p = property("queryType");
+ p = property("queryArguments"); // $NON-NLS-1$
+ p.setValue(NOT_UNDEFINED, Boolean.TRUE);
+ p.setValue(DEFAULT, "");
+
+ p = property("queryArgumentsTypes"); // $NON-NLS-1$
+ p.setValue(NOT_UNDEFINED, Boolean.TRUE);
+ p.setValue(DEFAULT, "");
+
+
+ p = property("queryType"); // $NON-NLS-1$
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 = property("query"); // $NON-NLS-1$
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, "");
p.setPropertyEditorClass(TextAreaEditor.class);
Modified:
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties
URL:
http://svn.apache.org/viewvc/jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties?view=diff&rev=528906&r1=528905&r2=528906
==============================================================================
---
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties
(original)
+++
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties
Sat Apr 14 18:52:38 2007
@@ -6,4 +6,9 @@
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=Parameter values
+queryArguments.shortDescription=SQL parameter values
+queryArgumentsTypes.displayName=Parameter types
+queryArgumentsTypes.shortDescription=JDBC Type names from java.sql.Types.
VARCHAR, INTEGER, etc.
+
Modified:
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties
URL:
http://svn.apache.org/viewvc/jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties?view=diff&rev=528906&r1=528905&r2=528906
==============================================================================
---
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties
(original)
+++
jakarta/jmeter/branches/rel-2-2/src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources_es.properties
Sat Apr 14 18:52:38 2007
@@ -8,3 +8,8 @@
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
+
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]