diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingCallableStatement.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingCallableStatement.java index a83569683..62ab28d07 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingCallableStatement.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/DelegatingCallableStatement.java @@ -63,7 +63,7 @@ public class DelegatingCallableStatement _del = null; } - private ResultSet wrapResult(boolean wrap, ResultSet rs) { + protected ResultSet wrapResult(boolean wrap, ResultSet rs) { if (!wrap) return rs; @@ -102,7 +102,7 @@ public class DelegatingCallableStatement } public String toString() { - StringBuffer buf = new StringBuffer("prepstmnt ").append(hashCode()); + StringBuffer buf = new StringBuffer("callstmnt ").append(hashCode()); appendInfo(buf); return buf.toString(); } diff --git a/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java index c403e8038..111eebc4d 100644 --- a/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java +++ b/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java @@ -24,6 +24,7 @@ import java.math.BigDecimal; import java.sql.Array; import java.sql.BatchUpdateException; import java.sql.Blob; +import java.sql.CallableStatement; import java.sql.Clob; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -250,6 +251,16 @@ public class LoggingConnectionDecorator implements ConnectionDecorator { Statement stmnt = super.createStatement(type, concurrency, false); return new LoggingStatement(stmnt); } + + protected CallableStatement prepareCall(String sql, boolean wrap) + throws SQLException { + try { + CallableStatement stmt = super.prepareCall(sql, wrap); + return new LoggingCallableStatement(stmt, sql); + } catch (SQLException se) { + throw wrap(se, sql); + } + } public void commit() throws SQLException { long start = System.currentTimeMillis(); @@ -892,7 +903,7 @@ public class LoggingConnectionDecorator implements ConnectionDecorator { for (int i = 0; i < count.length; i++) { // -3 is Statement.STATEMENT_FAILED, but is // only available in JDK 1.4+ - if (count[i] == -3) { + if (count[i] == Statement.EXECUTE_FAILED) { index = i; break; } @@ -1295,5 +1306,442 @@ public class LoggingConnectionDecorator implements ConnectionDecorator { } } } - } + + /** + * CallableStatement decorated with logging. + * Similar to {@link LoggingPreparedStatement} but can not be extended + * due to the existing delegation hierarchy. + */ + private class LoggingCallableStatement extends + DelegatingCallableStatement { + private final String _sql; + private List _params = null; + private List _paramBatch = null; + + public LoggingCallableStatement(CallableStatement stmt, String sql) + throws SQLException { + super(stmt, LoggingConnection.this); + _sql = sql; + } + + protected ResultSet wrapResult(ResultSet rs, boolean wrap) { + if (!wrap || rs == null) + return super.wrapResult(wrap, rs); + return new LoggingResultSet(rs, this); + } + + protected ResultSet executeQuery(String sql, boolean wrap) + throws SQLException { + logSQL(this); + long start = System.currentTimeMillis(); + try { + return super.executeQuery(sql, wrap); + } catch (SQLException se) { + throw wrap(se, LoggingCallableStatement.this); + } finally { + logTime(start); + clearLogParameters(true); + handleSQLWarning(LoggingCallableStatement.this); + } + } + + public int executeUpdate(String sql) throws SQLException { + logSQL(this); + long start = System.currentTimeMillis(); + try { + return super.executeUpdate(sql); + } catch (SQLException se) { + throw wrap(se, LoggingCallableStatement.this); + } finally { + logTime(start); + clearLogParameters(true); + handleSQLWarning(LoggingCallableStatement.this); + } + } + + public boolean execute(String sql) throws SQLException { + logSQL(this); + long start = System.currentTimeMillis(); + try { + return super.execute(sql); + } catch (SQLException se) { + throw wrap(se, LoggingCallableStatement.this); + } finally { + logTime(start); + clearLogParameters(true); + handleSQLWarning(LoggingCallableStatement.this); + } + } + + protected ResultSet executeQuery(boolean wrap) throws SQLException { + logSQL(this); + long start = System.currentTimeMillis(); + try { + return super.executeQuery(wrap); + } catch (SQLException se) { + throw wrap(se, LoggingCallableStatement.this); + } finally { + logTime(start); + clearLogParameters(true); + handleSQLWarning(LoggingCallableStatement.this); + } + } + + public int executeUpdate() throws SQLException { + logSQL(this); + long start = System.currentTimeMillis(); + try { + return super.executeUpdate(); + } catch (SQLException se) { + throw wrap(se, LoggingCallableStatement.this); + } finally { + logTime(start); + clearLogParameters(true); + handleSQLWarning(LoggingCallableStatement.this); + } + } + + public int[] executeBatch() throws SQLException { + logBatchSQL(this); + long start = System.currentTimeMillis(); + try { + return super.executeBatch(); + } catch (SQLException se) { + // if the exception is a BatchUpdateException, and + // we are tracking parameters, then set the current + // parameter set to be the index of the failed + // statement so that the ReportingSQLException will + // show the correct param + if (se instanceof BatchUpdateException + && _paramBatch != null && shouldTrackParameters()) { + int[] count = ((BatchUpdateException) se). + getUpdateCounts(); + if (count != null && count.length <= _paramBatch.size()) + { + int index = -1; + for (int i = 0; i < count.length; i++) { + // -3 is Statement.STATEMENT_FAILED, but is + // only available in JDK 1.4+ + if (count[i] == Statement.EXECUTE_FAILED) { + index = i; + break; + } + } + + // no -3 element: it may be that the server stopped + // processing, so the size of the count will be + // the index + if (index == -1) + index = count.length + 1; + + // set the current params to the saved values + if (index < _paramBatch.size()) + _params = (List) _paramBatch.get(index); + } + } + throw wrap(se, LoggingCallableStatement.this); + } finally { + logTime(start); + clearLogParameters(true); + handleSQLWarning(LoggingCallableStatement.this); + } + } + + public boolean execute() throws SQLException { + logSQL(this); + long start = System.currentTimeMillis(); + try { + return super.execute(); + } catch (SQLException se) { + throw wrap(se, LoggingCallableStatement.this); + } finally { + logTime(start); + clearLogParameters(true); + handleSQLWarning(LoggingCallableStatement.this); + } + } + + public void cancel() throws SQLException { + if (_logs.isJDBCEnabled()) + _logs.logJDBC("cancel " + this + ": " + _sql, + LoggingConnection.this); + + super.cancel(); + } + + public void setNull(int i1, int i2) throws SQLException { + setLogParameter(i1, "null", null); + super.setNull(i1, i2); + } + + public void setBoolean(int i, boolean b) throws SQLException { + setLogParameter(i, b); + super.setBoolean(i, b); + } + + public void setByte(int i, byte b) throws SQLException { + setLogParameter(i, b); + super.setByte(i, b); + } + + public void setShort(int i, short s) throws SQLException { + setLogParameter(i, s); + super.setShort(i, s); + } + + public void setInt(int i1, int i2) throws SQLException { + setLogParameter(i1, i2); + super.setInt(i1, i2); + } + + public void setLong(int i, long l) throws SQLException { + setLogParameter(i, l); + super.setLong(i, l); + } + + public void setFloat(int i, float f) throws SQLException { + setLogParameter(i, f); + super.setFloat(i, f); + } + + public void setDouble(int i, double d) throws SQLException { + setLogParameter(i, d); + super.setDouble(i, d); + } + + public void setBigDecimal(int i, BigDecimal bd) + throws SQLException { + setLogParameter(i, "BigDecimal", bd); + super.setBigDecimal(i, bd); + } + + public void setString(int i, String s) throws SQLException { + setLogParameter(i, "String", s); + super.setString(i, s); + } + + public void setBytes(int i, byte[] b) throws SQLException { + setLogParameter(i, "byte[]", b); + super.setBytes(i, b); + } + + public void setDate(int i, Date d) throws SQLException { + setLogParameter(i, "Date", d); + super.setDate(i, d); + } + + public void setTime(int i, Time t) throws SQLException { + setLogParameter(i, "Time", t); + super.setTime(i, t); + } + + public void setTimestamp(int i, Timestamp t) throws SQLException { + setLogParameter(i, "Timestamp", t); + super.setTimestamp(i, t); + } + + public void setAsciiStream(int i1, InputStream is, int i2) + throws SQLException { + setLogParameter(i1, "InputStream", is); + super.setAsciiStream(i1, is, i2); + } + + public void setUnicodeStream(int i1, InputStream is, int i2) + throws SQLException { + setLogParameter(i1, "InputStream", is); + super.setUnicodeStream(i2, is, i2); + } + + public void setBinaryStream(int i1, InputStream is, int i2) + throws SQLException { + setLogParameter(i1, "InputStream", is); + super.setBinaryStream(i1, is, i2); + } + + public void clearParameters() throws SQLException { + clearLogParameters(false); + super.clearParameters(); + } + + public void setObject(int i1, Object o, int i2, int i3) + throws SQLException { + setLogParameter(i1, "Object", o); + super.setObject(i1, o, i2, i3); + } + + public void setObject(int i1, Object o, int i2) + throws SQLException { + setLogParameter(i1, "Object", o); + super.setObject(i1, o, i2); + } + + public void setObject(int i, Object o) throws SQLException { + setLogParameter(i, "Object", o); + super.setObject(i, o); + } + + public void addBatch() throws SQLException { + if (_logs.isSQLEnabled()) + _logs.logSQL("batching " + this, LoggingConnection.this); + long start = System.currentTimeMillis(); + try { + super.addBatch(); + if (shouldTrackParameters()) { + // make sure our list is initialized + if (_paramBatch == null) + _paramBatch = new ArrayList(); + // copy parameters since they will be re-used + if (_params != null) + _paramBatch.add(new ArrayList(_params)); + else + _paramBatch.add(null); + } + } + finally { + logTime(start); + } + } + + public void setCharacterStream(int i1, Reader r, int i2) + throws SQLException { + setLogParameter(i1, "Reader", r); + super.setCharacterStream(i1, r, i2); + } + + public void setRef(int i, Ref r) throws SQLException { + setLogParameter(i, "Ref", r); + super.setRef(i, r); + } + + public void setBlob(int i, Blob b) throws SQLException { + setLogParameter(i, "Blob", b); + super.setBlob(i, b); + } + + public void setClob(int i, Clob c) throws SQLException { + setLogParameter(i, "Clob", c); + super.setClob(i, c); + } + + public void setArray(int i, Array a) throws SQLException { + setLogParameter(i, "Array", a); + super.setArray(i, a); + } + + public ResultSetMetaData getMetaData() throws SQLException { + return super.getMetaData(); + } + + public void setDate(int i, Date d, Calendar c) throws SQLException { + setLogParameter(i, "Date", d); + super.setDate(i, d, c); + } + + public void setTime(int i, Time t, Calendar c) throws SQLException { + setLogParameter(i, "Time", t); + super.setTime(i, t, c); + } + + public void setTimestamp(int i, Timestamp t, Calendar c) + throws SQLException { + setLogParameter(i, "Timestamp", t); + super.setTimestamp(i, t, c); + } + + public void setNull(int i1, int i2, String s) throws SQLException { + setLogParameter(i1, "null", null); + super.setNull(i1, i2, s); + } + + protected void appendInfo(StringBuffer buf) { + buf.append(" "); + if (_formatter != null) { + buf.append(SEP); + buf.append(_formatter.prettyPrint(_sql)); + buf.append(SEP); + } else { + buf.append(_sql); + } + + StringBuffer paramBuf = null; + if (_params != null && !_params.isEmpty()) { + paramBuf = new StringBuffer(); + for (Iterator itr = _params.iterator(); itr.hasNext();) { + paramBuf.append(itr.next()); + if (itr.hasNext()) + paramBuf.append(", "); + } + } + + if (paramBuf != null) { + if (!_prettyPrint) + buf.append(" "); + buf.append("[params="). + append(paramBuf.toString()).append("]"); + } + super.appendInfo(buf); + } + + protected void clearLogParameters(boolean batch) { + if (_params != null) + _params.clear(); + if (batch && _paramBatch != null) + _paramBatch.clear(); + } + + private boolean shouldTrackParameters() { + return _trackParameters || _logs.isSQLEnabled(); + } + + private void setLogParameter(int index, boolean val) { + if (shouldTrackParameters()) + setLogParameter(index, "(boolean) " + val); + } + + private void setLogParameter(int index, byte val) { + if (shouldTrackParameters()) + setLogParameter(index, "(byte) " + val); + } + + private void setLogParameter(int index, double val) { + if (shouldTrackParameters()) + setLogParameter(index, "(double) " + val); + } + + private void setLogParameter(int index, float val) { + if (shouldTrackParameters()) + setLogParameter(index, "(float) " + val); + } + + private void setLogParameter(int index, int val) { + if (shouldTrackParameters()) + setLogParameter(index, "(int) " + val); + } + + private void setLogParameter(int index, long val) { + if (shouldTrackParameters()) + setLogParameter(index, "(long) " + val); + } + + private void setLogParameter(int index, short val) { + if (shouldTrackParameters()) + setLogParameter(index, "(short) " + val); + } + + private void setLogParameter(int index, String type, Object val) { + if (shouldTrackParameters()) + setLogParameter(index, "(" + type + ") " + val); + } + + private void setLogParameter(int index, String val) { + if (_params == null) + _params = new ArrayList(); + while (_params.size() < index) + _params.add(null); + if (val.length() > 80) + val = val.substring(0, 77) + "..."; + _params.set(index - 1, val); + } + } + } }