net.sf.log4jdbc.sql.jdbcapi.PreparedStatementSpy Maven / Gradle / Ivy
/**
* Copyright 2007-2012 Arthur Blake
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.log4jdbc.sql.jdbcapi;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import net.sf.log4jdbc.log.SpyLogDelegator;
import net.sf.log4jdbc.sql.rdbmsspecifics.RdbmsSpecifics;
/**
* Wraps a PreparedStatement and reports method calls, returns and exceptions.
*
* MODIFICATIONS FOR LOG4J2:
* This class now overrides Statement.getGeneratedKeys()
* in order to use the convenient method Statement.getGeneratedKeys(String)
,
* by providing the String returned by dumpedSql()
.
*
* @author Arthur Blake
*/
public class PreparedStatementSpy extends StatementSpy implements PreparedStatement
{
/**
* holds list of bind variables for tracing
*/
protected final List argTrace = new ArrayList();
// a way to turn on and off type help...
// todo: make this a configurable parameter
// todo, debug arrays and streams in a more useful manner.... if possible
private static final boolean showTypeHelp = false;
/**
* Store an argument (bind variable) into the argTrace list (above) for later dumping.
*
* @param i index of argument being set.
* @param typeHelper optional additional info about the type that is being set in the arg
* @param arg argument being bound.
*/
protected void argTraceSet(int i, String typeHelper, Object arg)
{
String tracedArg;
try
{
tracedArg = rdbmsSpecifics.formatParameterObject(arg);
}
catch (Throwable t)
{
// rdbmsSpecifics should NEVER EVER throw an exception!!
// but just in case it does, we trap it.
log.debug("rdbmsSpecifics threw an exception while trying to format a " +
"parameter object [" + arg + "] this is very bad!!! (" +
t.getMessage() + ")");
// backup - so that at least we won't harm the application using us
tracedArg = arg==null?"null":arg.toString();
}
i--; // make the index 0 based
synchronized (argTrace)
{
// if an object is being inserted out of sequence, fill up missing values with null...
while (i >= argTrace.size())
{
argTrace.add(argTrace.size(), null);
}
if (!showTypeHelp)
{
argTrace.set(i, tracedArg);
}
else
{
argTrace.set(i, typeHelper + tracedArg);
}
}
}
protected String dumpedSql()
{
StringBuffer dumpSql = new StringBuffer();
int lastPos = 0;
int Qpos = sql.indexOf('?', lastPos); // find position of first question mark
int argIdx = 0;
String arg;
while (Qpos != -1)
{
// get stored argument
synchronized (argTrace)
{
try
{
arg = argTrace.get(argIdx);
}
catch (IndexOutOfBoundsException e)
{
arg = "?";
}
}
if (arg == null)
{
arg = "?";
}
argIdx++;
dumpSql.append(sql.substring(lastPos, Qpos)); // dump segment of sql up to question mark.
lastPos = Qpos + 1;
Qpos = sql.indexOf('?', lastPos);
dumpSql.append(arg);
}
if (lastPos < sql.length())
{
dumpSql.append(sql.substring(lastPos, sql.length())); // dump last segment
}
return dumpSql.toString();
}
protected void reportAllReturns(String methodCall, String msg)
{
log.methodReturned(this, methodCall, msg);
}
/**
* The real PreparedStatement that this PreparedStatementSpy wraps.
*/
protected PreparedStatement realPreparedStatement;
/**
* Get the real PreparedStatement that this PreparedStatementSpy wraps.
*
* @return the real PreparedStatement that this PreparedStatementSpy wraps.
*/
public PreparedStatement getRealPreparedStatement()
{
return realPreparedStatement;
}
/**
* RdbmsSpecifics for formatting SQL for the given RDBMS.
*/
protected RdbmsSpecifics rdbmsSpecifics;
/**
* Create a PreparedStatementSpy (JDBC 4 version) for logging activity of another PreparedStatement.
*
* @param sql SQL for the prepared statement that is being spied upon.
* @param connectionSpy ConnectionSpy that was called to produce this PreparedStatement.
* @param realPreparedStatement The actual PreparedStatement that is being spied upon.
* @param logDelegator The SpyLogDelegator
used by
* this PreparedStatementSpy
and all resources obtained
* from it (ResultSet
s)
*/
public PreparedStatementSpy(String sql, ConnectionSpy connectionSpy,
PreparedStatement realPreparedStatement, SpyLogDelegator logDelegator)
{
super(connectionSpy, realPreparedStatement, logDelegator); // does null check for us
this.sql = sql;
this.realPreparedStatement = realPreparedStatement;
rdbmsSpecifics = connectionSpy.getRdbmsSpecifics();
}
public String getClassType()
{
return "PreparedStatement";
}
// forwarding methods
public void setTime(int parameterIndex, Time x) throws SQLException
{
String methodCall = "setTime(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(Time)", x);
try
{
realPreparedStatement.setTime(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException
{
String methodCall = "setTime(" + parameterIndex + ", " + x + ", " + cal + ")";
argTraceSet(parameterIndex, "(Time)", x);
try
{
realPreparedStatement.setTime(parameterIndex, x, cal);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException
{
String methodCall = "setCharacterStream(" + parameterIndex + ", " + reader + ", " + length + ")";
argTraceSet(parameterIndex, "(Reader)", "");
try
{
realPreparedStatement.setCharacterStream(parameterIndex, reader, length);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setNull(int parameterIndex, int sqlType) throws SQLException
{
String methodCall = "setNull(" + parameterIndex + ", " + sqlType + ")";
argTraceSet(parameterIndex, null, null);
try
{
realPreparedStatement.setNull(parameterIndex, sqlType);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException
{
String methodCall = "setNull(" + paramIndex + ", " + sqlType + ", " + typeName + ")";
argTraceSet(paramIndex, null, null);
try
{
realPreparedStatement.setNull(paramIndex, sqlType, typeName);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setRef(int i, Ref x) throws SQLException
{
String methodCall = "setRef(" + i + ", " + x + ")";
argTraceSet(i, "(Ref)", x);
try
{
realPreparedStatement.setRef(i, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setBoolean(int parameterIndex, boolean x) throws SQLException
{
String methodCall = "setBoolean(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(boolean)", x?Boolean.TRUE:Boolean.FALSE);
try
{
realPreparedStatement.setBoolean(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setBlob(int i, Blob x) throws SQLException
{
String methodCall = "setBlob(" + i + ", " + x + ")";
argTraceSet(i, "(Blob)",
x==null?null:(""));
try
{
realPreparedStatement.setBlob(i, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setClob(int i, Clob x) throws SQLException
{
String methodCall = "setClob(" + i + ", " + x + ")";
argTraceSet(i, "(Clob)",
x==null?null:(""));
try
{
realPreparedStatement.setClob(i, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setArray(int i, Array x) throws SQLException
{
String methodCall = "setArray(" + i + ", " + x + ")";
argTraceSet(i, "(Array)", "");
try
{
realPreparedStatement.setArray(i, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setByte(int parameterIndex, byte x) throws SQLException
{
String methodCall = "setByte(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(byte)", new Byte(x));
try
{
realPreparedStatement.setByte(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
/**
* @deprecated
*/
public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException
{
String methodCall = "setUnicodeStream(" + parameterIndex + ", " + x + ", " + length + ")";
argTraceSet(parameterIndex, "(Unicode InputStream)", "");
try
{
realPreparedStatement.setUnicodeStream(parameterIndex, x, length);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setShort(int parameterIndex, short x) throws SQLException
{
String methodCall = "setShort(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(short)", new Short(x));
try
{
realPreparedStatement.setShort(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public boolean execute() throws SQLException
{
String methodCall = "execute()";
String dumpedSql = dumpedSql();
reportSql(dumpedSql, methodCall);
long tstart = System.currentTimeMillis();
try
{
boolean result = realPreparedStatement.execute();
reportSqlTiming(System.currentTimeMillis() - tstart, dumpedSql, methodCall);
return reportReturn(methodCall, result);
}
catch (SQLException s)
{
reportException(methodCall, s, dumpedSql, System.currentTimeMillis() - tstart);
throw s;
}
}
public void setInt(int parameterIndex, int x) throws SQLException
{
String methodCall = "setInt(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(int)", new Integer(x));
try
{
realPreparedStatement.setInt(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setLong(int parameterIndex, long x) throws SQLException
{
String methodCall = "setLong(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(long)", new Long(x));
try
{
realPreparedStatement.setLong(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setFloat(int parameterIndex, float x) throws SQLException
{
String methodCall = "setFloat(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(float)", new Float(x));
try
{
realPreparedStatement.setFloat(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setDouble(int parameterIndex, double x) throws SQLException
{
String methodCall = "setDouble(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(double)", new Double(x));
try
{
realPreparedStatement.setDouble(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException
{
String methodCall = "setBigDecimal(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(BigDecimal)", x);
try
{
realPreparedStatement.setBigDecimal(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setURL(int parameterIndex, URL x) throws SQLException
{
String methodCall = "setURL(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(URL)", x);
try
{
realPreparedStatement.setURL(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setString(int parameterIndex, String x) throws SQLException
{
String methodCall = "setString(" + parameterIndex + ", \"" + x + "\")";
argTraceSet(parameterIndex, "(String)", x);
try
{
realPreparedStatement.setString(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setBytes(int parameterIndex, byte[] x) throws SQLException
{
//todo: dump array?
String methodCall = "setBytes(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(byte[])", "");
try
{
realPreparedStatement.setBytes(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setDate(int parameterIndex, Date x) throws SQLException
{
String methodCall = "setDate(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(Date)", x);
try
{
realPreparedStatement.setDate(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public ParameterMetaData getParameterMetaData() throws SQLException
{
String methodCall = "getParameterMetaData()";
try
{
return (ParameterMetaData) reportReturn(methodCall, realPreparedStatement.getParameterMetaData());
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
}
public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException
{
String methodCall = "setDate(" + parameterIndex + ", " + x + ", " + cal + ")";
argTraceSet(parameterIndex, "(Date)", x);
try
{
realPreparedStatement.setDate(parameterIndex, x, cal);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public ResultSet executeQuery() throws SQLException
{
String methodCall = "executeQuery()";
String dumpedSql = dumpedSql();
reportSql(dumpedSql, methodCall);
long tstart = System.currentTimeMillis();
try
{
ResultSet r = realPreparedStatement.executeQuery();
reportSqlTiming(System.currentTimeMillis() - tstart, dumpedSql, methodCall);
ResultSetSpy rsp = new ResultSetSpy(this, r, this.log);
return (ResultSet) reportReturn(methodCall, rsp);
}
catch (SQLException s)
{
reportException(methodCall, s, dumpedSql, System.currentTimeMillis() - tstart);
throw s;
}
}
private String getTypeHelp(Object x)
{
if (x==null)
{
return "(null)";
}
return "(" + x.getClass().getName() + ")";
}
public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException
{
String methodCall = "setObject(" + parameterIndex + ", " + x + ", " + targetSqlType + ", " + scale + ")";
argTraceSet(parameterIndex, getTypeHelp(x), x);
try
{
realPreparedStatement.setObject(parameterIndex, x, targetSqlType, scale);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException
{
String methodCall = "setObject(" + parameterIndex + ", " + x + ", " + targetSqlType + ")";
argTraceSet(parameterIndex, getTypeHelp(x), x);
try
{
realPreparedStatement.setObject(parameterIndex, x, targetSqlType);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setObject(int parameterIndex, Object x) throws SQLException
{
String methodCall = "setObject(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, getTypeHelp(x), x);
try
{
realPreparedStatement.setObject(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException
{
String methodCall = "setTimestamp(" + parameterIndex + ", " + x + ")";
argTraceSet(parameterIndex, "(Date)", x);
try
{
realPreparedStatement.setTimestamp(parameterIndex, x);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException
{
String methodCall = "setTimestamp(" + parameterIndex + ", " + x + ", " + cal + ")";
argTraceSet(parameterIndex, "(Timestamp)", x);
try
{
realPreparedStatement.setTimestamp(parameterIndex, x, cal);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public int executeUpdate() throws SQLException
{
String methodCall = "executeUpdate()";
String dumpedSql = dumpedSql();
reportSql(dumpedSql, methodCall);
long tstart = System.currentTimeMillis();
try
{
int result = realPreparedStatement.executeUpdate();
reportSqlTiming(System.currentTimeMillis() - tstart, dumpedSql, methodCall);
return reportReturn(methodCall, result);
}
catch (SQLException s)
{
reportException(methodCall, s, dumpedSql, System.currentTimeMillis() - tstart);
throw s;
}
}
@Override
public ResultSet getGeneratedKeys() throws SQLException
{
return this.getGeneratedKeys(dumpedSql());
}
public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException
{
String methodCall = "setAsciiStream(" + parameterIndex + ", " + x + ", " + length + ")";
argTraceSet(parameterIndex, "(Ascii InputStream)", "");
try
{
realPreparedStatement.setAsciiStream(parameterIndex, x, length);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException
{
String methodCall = "setBinaryStream(" + parameterIndex + ", " + x + ", " + length + ")";
argTraceSet(parameterIndex, "(Binary InputStream)", "");
try
{
realPreparedStatement.setBinaryStream(parameterIndex, x, length);
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public void clearParameters() throws SQLException
{
String methodCall = "clearParameters()";
synchronized (argTrace)
{
argTrace.clear();
}
try
{
realPreparedStatement.clearParameters();
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
public ResultSetMetaData getMetaData() throws SQLException
{
String methodCall = "getMetaData()";
try
{
return (ResultSetMetaData) reportReturn(methodCall, realPreparedStatement.getMetaData());
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
}
public void addBatch() throws SQLException
{
String methodCall = "addBatch()";
currentBatch.add(dumpedSql());
try
{
realPreparedStatement.addBatch();
}
catch (SQLException s)
{
reportException(methodCall, s);
throw s;
}
reportReturn(methodCall);
}
}