1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.lib.jdbc;
20
21 import java.io.InputStream;
22 import java.io.Reader;
23 import java.math.BigDecimal;
24 import java.sql.Array;
25 import java.sql.BatchUpdateException;
26 import java.sql.Blob;
27 import java.sql.Clob;
28 import java.sql.Connection;
29 import java.sql.DatabaseMetaData;
30 import java.sql.Date;
31 import java.sql.PreparedStatement;
32 import java.sql.Ref;
33 import java.sql.ResultSet;
34 import java.sql.ResultSetMetaData;
35 import java.sql.SQLException;
36 import java.sql.SQLWarning;
37 import java.sql.Savepoint;
38 import java.sql.Statement;
39 import java.sql.Time;
40 import java.sql.Timestamp;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Calendar;
44 import java.util.Iterator;
45 import java.util.List;
46
47 import org.apache.openjpa.lib.log.Log;
48 import org.apache.openjpa.lib.util.J2DoPrivHelper;
49
50 /**
51 * A {@link ConnectionDecorator} that creates logging connections and
52 * {@link ReportingSQLException}s.
53 *
54 * @author Marc Prud'hommeaux
55 * @nojavadoc
56 */
57 public class LoggingConnectionDecorator implements ConnectionDecorator {
58
59 private static final String SEP = J2DoPrivHelper.getLineSeparator();
60
61 private static final int WARN_IGNORE = 0;
62 private static final int WARN_LOG_TRACE = 1;
63 private static final int WARN_LOG_INFO = 2;
64 private static final int WARN_LOG_WARN = 3;
65 private static final int WARN_LOG_ERROR = 4;
66 private static final int WARN_THROW = 5;
67 private static final int WARN_HANDLE = 6;
68 private static final String[] WARNING_ACTIONS = new String[7];
69
70 static {
71 WARNING_ACTIONS[WARN_IGNORE] = "ignore";
72 WARNING_ACTIONS[WARN_LOG_TRACE] = "trace";
73 WARNING_ACTIONS[WARN_LOG_INFO] = "info";
74 WARNING_ACTIONS[WARN_LOG_WARN] = "warn";
75 WARNING_ACTIONS[WARN_LOG_ERROR] = "error";
76 WARNING_ACTIONS[WARN_THROW] = "throw";
77 WARNING_ACTIONS[WARN_HANDLE] = "handle";
78 }
79
80 private final DataSourceLogs _logs = new DataSourceLogs();
81 private SQLFormatter _formatter;
82 private boolean _prettyPrint;
83 private int _prettyPrintLineLength = 60;
84 private int _warningAction = WARN_IGNORE;
85 private SQLWarningHandler _warningHandler;
86 private boolean _trackParameters = true;
87
88 /**
89 * If set to <code>true</code>, pretty-print SQL by running it
90 * through {@link SQLFormatter#prettyPrint}. If
91 * <code>false</code>, don't pretty-print, and output SQL logs in
92 * a single line. Pretty-printed SQL can be easier for a human to
93 * read, but is harder to parse with tools like grep.
94 */
95 public void setPrettyPrint(boolean prettyPrint) {
96 _prettyPrint = prettyPrint;
97 if (_formatter == null && _prettyPrint) {
98 _formatter = new SQLFormatter();
99 _formatter.setLineLength(_prettyPrintLineLength);
100 } else if (!_prettyPrint)
101 _formatter = null;
102 }
103
104 /**
105 * @see #setPrettyPrint
106 */
107 public boolean getPrettyPrint() {
108 return _prettyPrint;
109 }
110
111 /**
112 * The number of characters to print per line when
113 * pretty-printing of SQL is enabled. Defaults to 60 to provide
114 * some space for any ant-related characters on the left of a
115 * standard 80-character display.
116 */
117 public void setPrettyPrintLineLength(int length) {
118 _prettyPrintLineLength = length;
119 if (_formatter != null)
120 _formatter.setLineLength(length);
121 }
122
123 /**
124 * @see #setPrettyPrintLineLength
125 */
126 public int getPrettyPrintLineLength() {
127 return _prettyPrintLineLength;
128 }
129
130 /**
131 * Whether to track parameters for the purposes of reporting exceptions.
132 */
133 public void setTrackParameters(boolean trackParameters) {
134 _trackParameters = trackParameters;
135 }
136
137 /**
138 * Whether to track parameters for the purposes of reporting exceptions.
139 */
140 public boolean getTrackParameters() {
141 return _trackParameters;
142 }
143
144 /**
145 * What to do with SQL warnings.
146 */
147 public void setWarningAction(String warningAction) {
148 int index = Arrays.asList(WARNING_ACTIONS).indexOf(warningAction);
149 if (index < 0)
150 index = WARN_IGNORE;
151 _warningAction = index;
152 }
153
154 /**
155 * What to do with SQL warnings.
156 */
157 public String getWarningAction() {
158 return WARNING_ACTIONS[_warningAction];
159 }
160
161 /**
162 * What to do with SQL warnings.
163 */
164 public void setWarningHandler(SQLWarningHandler warningHandler) {
165 _warningHandler = warningHandler;
166 }
167
168 /**
169 * What to do with SQL warnings.
170 */
171 public SQLWarningHandler getWarningHandler() {
172 return _warningHandler;
173 }
174
175 /**
176 * The log to write to.
177 */
178 public DataSourceLogs getLogs() {
179 return _logs;
180 }
181
182 public Connection decorate(Connection conn) throws SQLException {
183 return new LoggingConnection(conn);
184 }
185
186 /**
187 * Include SQL in exception.
188 */
189 private SQLException wrap(SQLException sqle, Statement stmnt) {
190 if (sqle instanceof ReportingSQLException)
191 return (ReportingSQLException) sqle;
192 return new ReportingSQLException(sqle, stmnt);
193 }
194
195 /**
196 * Include SQL in exception.
197 */
198 private SQLException wrap(SQLException sqle, String sql) {
199 if (sqle instanceof ReportingSQLException)
200 return (ReportingSQLException) sqle;
201 return new ReportingSQLException(sqle, sql);
202 }
203
204 /**
205 * Interface that allows customization of what to do when
206 * {@link SQLWarning}s occur.
207 */
208 public static interface SQLWarningHandler {
209
210 public void handleWarning(SQLWarning warning) throws SQLException;
211 }
212
213 /**
214 * Logging connection.
215 */
216 private class LoggingConnection extends DelegatingConnection {
217
218 public LoggingConnection(Connection conn) throws SQLException {
219 super(conn);
220 }
221
222 protected PreparedStatement prepareStatement(String sql, boolean wrap)
223 throws SQLException {
224 try {
225 PreparedStatement stmnt = super.prepareStatement(sql, false);
226 return new LoggingPreparedStatement(stmnt, sql);
227 } catch (SQLException se) {
228 throw wrap(se, sql);
229 }
230 }
231
232 protected PreparedStatement prepareStatement(String sql, int rsType,
233 int rsConcur, boolean wrap) throws SQLException {
234 try {
235 PreparedStatement stmnt = super.prepareStatement
236 (sql, rsType, rsConcur, false);
237 return new LoggingPreparedStatement(stmnt, sql);
238 } catch (SQLException se) {
239 throw wrap(se, sql);
240 }
241 }
242
243 protected Statement createStatement(boolean wrap) throws SQLException {
244 Statement stmnt = super.createStatement(false);
245 return new LoggingStatement(stmnt);
246 }
247
248 protected Statement createStatement(int type, int concurrency,
249 boolean wrap) throws SQLException {
250 Statement stmnt = super.createStatement(type, concurrency, false);
251 return new LoggingStatement(stmnt);
252 }
253
254 public void commit() throws SQLException {
255 long start = System.currentTimeMillis();
256 try {
257 super.commit();
258 } finally {
259 if (_logs.isJDBCEnabled())
260 _logs.logJDBC("commit", start, this);
261 handleSQLWarning();
262 }
263 }
264
265 public void rollback() throws SQLException {
266 long start = System.currentTimeMillis();
267 try {
268 super.rollback();
269 } finally {
270 if (_logs.isJDBCEnabled())
271 _logs.logJDBC("rollback", start, this);
272 handleSQLWarning();
273 }
274 }
275
276 public void close() throws SQLException {
277 long start = System.currentTimeMillis();
278 try {
279 super.close();
280 } finally {
281 if (_logs.isJDBCEnabled())
282 _logs.logJDBC("close", start, this);
283 }
284 }
285
286 public Savepoint setSavepoint() throws SQLException {
287 long start = System.currentTimeMillis();
288 try {
289 return super.setSavepoint();
290 } finally {
291 if (_logs.isJDBCEnabled())
292 _logs.logJDBC("savepoint", start, this);
293 handleSQLWarning();
294 }
295 }
296
297 public Savepoint setSavepoint(String name) throws SQLException {
298 long start = System.currentTimeMillis();
299 try {
300 return super.setSavepoint(name);
301 } finally {
302 if (_logs.isJDBCEnabled())
303 _logs.logJDBC("savepoint: " + name, start, this);
304 handleSQLWarning();
305 }
306 }
307
308 public void rollback(Savepoint savepoint) throws SQLException {
309 long start = System.currentTimeMillis();
310 try {
311 super.rollback(savepoint);
312 } finally {
313 if (_logs.isJDBCEnabled()) {
314 String name = null;
315 try {
316 name = savepoint.getSavepointName();
317 } catch (SQLException sqe) {
318 name = String.valueOf(savepoint.getSavepointId());
319 }
320 _logs.logJDBC("rollback: " + name, start, this);
321 }
322 handleSQLWarning();
323 }
324 }
325
326 public void releaseSavepoint(Savepoint savepoint) throws SQLException {
327 long start = System.currentTimeMillis();
328 try {
329 super.releaseSavepoint(savepoint);
330 } finally {
331 if (_logs.isJDBCEnabled()) {
332 String name = null;
333 try {
334 name = savepoint.getSavepointName();
335 } catch (SQLException sqe) {
336 name = String.valueOf(savepoint.getSavepointId());
337 }
338 _logs.logJDBC("release: " + name, start, this);
339 }
340 handleSQLWarning();
341 }
342 }
343
344 protected Statement createStatement(int resultSetType,
345 int resultSetConcurrency, int resultSetHoldability, boolean wrap)
346 throws SQLException {
347 Statement stmnt = super.createStatement(resultSetType,
348 resultSetConcurrency, resultSetHoldability, false);
349 handleSQLWarning();
350 return new LoggingStatement(stmnt);
351 }
352
353 protected PreparedStatement prepareStatement(String sql,
354 int resultSetType, int resultSetConcurrency,
355 int resultSetHoldability, boolean wrap) throws SQLException {
356 try {
357 PreparedStatement stmnt = super.prepareStatement
358 (sql, resultSetType, resultSetConcurrency,
359 resultSetHoldability, false);
360 handleSQLWarning();
361 return new LoggingPreparedStatement(stmnt, sql);
362 } catch (SQLException se) {
363 throw wrap(se, sql);
364 }
365 }
366
367 protected PreparedStatement prepareStatement(String sql,
368 int autoGeneratedKeys, boolean wrap) throws SQLException {
369 try {
370 PreparedStatement stmnt = super.prepareStatement
371 (sql, autoGeneratedKeys, false);
372 handleSQLWarning();
373 return new LoggingPreparedStatement(stmnt, sql);
374 } catch (SQLException se) {
375 throw wrap(se, sql);
376 }
377 }
378
379 protected PreparedStatement prepareStatement(String sql,
380 int[] columnIndexes, boolean wrap) throws SQLException {
381 try {
382 PreparedStatement stmnt = super.prepareStatement
383 (sql, columnIndexes, false);
384 handleSQLWarning();
385 return new LoggingPreparedStatement(stmnt, sql);
386 } catch (SQLException se) {
387 throw wrap(se, sql);
388 }
389 }
390
391 protected PreparedStatement prepareStatement(String sql,
392 String[] columnNames, boolean wrap) throws SQLException {
393 try {
394 PreparedStatement stmnt = super.prepareStatement
395 (sql, columnNames, false);
396 handleSQLWarning();
397 return new LoggingPreparedStatement(stmnt, sql);
398 } catch (SQLException se) {
399 throw wrap(se, sql);
400 }
401 }
402
403 protected DatabaseMetaData getMetaData(boolean wrap)
404 throws SQLException {
405 return new LoggingDatabaseMetaData(super.getMetaData(false));
406 }
407
408 /**
409 * Log time elapsed since given start.
410 */
411 private void logTime(long startTime) throws SQLException {
412 if (_logs.isSQLEnabled())
413 _logs.logSQL("spent", startTime, this);
414 }
415
416 /**
417 * Log time elapsed since given start.
418 */
419 private void logSQL(Statement stmnt) throws SQLException {
420 if (_logs.isSQLEnabled())
421 _logs.logSQL("executing " + stmnt, this);
422 }
423
424 /**
425 * Log time elapsed since given start.
426 */
427 private void logBatchSQL(Statement stmnt) throws SQLException {
428 if (_logs.isSQLEnabled())
429 _logs.logSQL("executing batch " + stmnt, this);
430 }
431
432 /**
433 * Handle any {@link SQLWarning}s on the current {@link Connection}.
434 *
435 * @see #handleSQLWarning(SQLWarning)
436 */
437 private void handleSQLWarning() throws SQLException {
438 if (_warningAction == WARN_IGNORE)
439 return;
440
441 try {
442 handleSQLWarning(getWarnings());
443 } finally {
444 clearWarnings();
445 }
446 }
447
448 /**
449 * Handle any {@link SQLWarning}s on the specified {@link Statement}.
450 *
451 * @see #handleSQLWarning(SQLWarning)
452 */
453 private void handleSQLWarning(Statement stmnt) throws SQLException {
454 if (_warningAction == WARN_IGNORE)
455 return;
456
457 try {
458 handleSQLWarning(stmnt.getWarnings());
459 } finally {
460 stmnt.clearWarnings();
461 }
462 }
463
464 /**
465 * Handle any {@link SQLWarning}s on the specified {@link ResultSet}.
466 *
467 * @see #handleSQLWarning(SQLWarning)
468 */
469 private void handleSQLWarning(ResultSet rs) throws SQLException {
470 if (_warningAction == WARN_IGNORE)
471 return;
472
473 try {
474 handleSQLWarning(rs.getWarnings());
475 } finally {
476 rs.clearWarnings();
477 }
478 }
479
480 /**
481 * Handle the specified {@link SQLWarning} depending on the
482 * setting of the {@link #setWarningAction} attribute.
483 *
484 * @param warning the warning to handle
485 */
486 private void handleSQLWarning(SQLWarning warning) throws SQLException {
487 if (warning == null)
488 return;
489 if (_warningAction == WARN_IGNORE)
490 return;
491
492 Log log = _logs.getJDBCLog();
493 for (; warning != null; warning = warning.getNextWarning()) {
494 switch (_warningAction) {
495 case WARN_LOG_TRACE:
496 if (log.isTraceEnabled())
497 log.trace(warning);
498 break;
499 case WARN_LOG_INFO:
500 if (log.isInfoEnabled())
501 log.info(warning);
502 break;
503 case WARN_LOG_WARN:
504 if (log.isWarnEnabled())
505 log.warn(warning);
506 break;
507 case WARN_LOG_ERROR:
508 if (log.isErrorEnabled())
509 log.error(warning);
510 break;
511 case WARN_THROW:
512 // just throw it as if it were a SQLException
513 throw warning;
514 case WARN_HANDLE:
515 if (_warningHandler != null)
516 _warningHandler.handleWarning(warning);
517 break;
518 default:
519 // ignore
520 break;
521 }
522 }
523 }
524
525 /**
526 * Metadata wrapper that logs actions.
527 */
528 private class LoggingDatabaseMetaData
529 extends DelegatingDatabaseMetaData {
530
531 public LoggingDatabaseMetaData(DatabaseMetaData meta) {
532 super(meta, LoggingConnection.this);
533 }
534
535 public ResultSet getBestRowIdentifier(String catalog,
536 String schema, String table, int scope, boolean nullable)
537 throws SQLException {
538 if (_logs.isJDBCEnabled())
539 _logs.logJDBC("getBestRowIdentifier: "
540 + catalog + ", " + schema + ", " + table,
541 LoggingConnection.this);
542 return super.getBestRowIdentifier(catalog, schema,
543 table, scope, nullable);
544 }
545
546 public ResultSet getCatalogs() throws SQLException {
547 if (_logs.isJDBCEnabled())
548 _logs.logJDBC("getCatalogs", LoggingConnection.this);
549 return super.getCatalogs();
550 }
551
552 public ResultSet getColumnPrivileges(String catalog, String schema,
553 String table, String columnNamePattern) throws SQLException {
554 if (_logs.isJDBCEnabled())
555 _logs.logJDBC("getColumnPrivileges: "
556 + catalog + ", " + schema + ", " + table,
557 LoggingConnection.this);
558 return super.getColumnPrivileges(catalog, schema,
559 table, columnNamePattern);
560 }
561
562 public ResultSet getColumns(String catalog, String schemaPattern,
563 String tableNamePattern, String columnNamePattern)
564 throws SQLException {
565 if (_logs.isJDBCEnabled())
566 _logs.logJDBC("getColumns: "
567 + catalog + ", " + schemaPattern + ", "
568 + tableNamePattern + ", " + columnNamePattern,
569 LoggingConnection.this);
570 return super.getColumns(catalog, schemaPattern,
571 tableNamePattern, columnNamePattern);
572 }
573
574 public ResultSet getCrossReference(String primaryCatalog,
575 String primarySchema, String primaryTable,
576 String foreignCatalog, String foreignSchema,
577 String foreignTable) throws SQLException {
578 if (_logs.isJDBCEnabled())
579 _logs.logJDBC("getCrossReference: "
580 + primaryCatalog + ", " + primarySchema + ", "
581 + primaryTable + ", " + foreignCatalog + ", "
582 + foreignSchema + ", " + foreignSchema,
583 LoggingConnection.this);
584 return super.getCrossReference(primaryCatalog, primarySchema,
585 primaryTable, foreignCatalog, foreignSchema, foreignTable);
586 }
587
588 public ResultSet getExportedKeys(String catalog, String schema,
589 String table) throws SQLException {
590 if (_logs.isJDBCEnabled())
591 _logs.logJDBC("getExportedKeys: "
592 + catalog + ", " + schema + ", " + table,
593 LoggingConnection.this);
594 return super.getExportedKeys(catalog, schema, table);
595 }
596
597 public ResultSet getImportedKeys(String catalog, String schema,
598 String table) throws SQLException {
599 if (_logs.isJDBCEnabled())
600 _logs.logJDBC("getImportedKeys: "
601 + catalog + ", " + schema + ", " + table,
602 LoggingConnection.this);
603 return super.getImportedKeys(catalog, schema, table);
604 }
605
606 public ResultSet getIndexInfo(String catalog, String schema,
607 String table, boolean unique, boolean approximate)
608 throws SQLException {
609 if (_logs.isJDBCEnabled())
610 _logs.logJDBC("getIndexInfo: "
611 + catalog + ", " + schema + ", " + table,
612 LoggingConnection.this);
613 return super.getIndexInfo(catalog, schema, table, unique,
614 approximate);
615 }
616
617 public ResultSet getPrimaryKeys(String catalog, String schema,
618 String table) throws SQLException {
619 if (_logs.isJDBCEnabled())
620 _logs.logJDBC("getPrimaryKeys: "
621 + catalog + ", " + schema + ", " + table,
622 LoggingConnection.this);
623 return super.getPrimaryKeys(catalog, schema, table);
624 }
625
626 public ResultSet getProcedureColumns(String catalog,
627 String schemaPattern, String procedureNamePattern,
628 String columnNamePattern) throws SQLException {
629 if (_logs.isJDBCEnabled())
630 _logs.logJDBC("getProcedureColumns: "
631 + catalog + ", " + schemaPattern + ", "
632 + procedureNamePattern + ", " + columnNamePattern,
633 LoggingConnection.this);
634 return super.getProcedureColumns(catalog, schemaPattern,
635 procedureNamePattern, columnNamePattern);
636 }
637
638 public ResultSet getProcedures(String catalog,
639 String schemaPattern, String procedureNamePattern)
640 throws SQLException {
641 if (_logs.isJDBCEnabled())
642 _logs.logJDBC("getProcedures: "
643 + catalog + ", " + schemaPattern + ", "
644 + procedureNamePattern, LoggingConnection.this);
645 return super.getProcedures(catalog, schemaPattern,
646 procedureNamePattern);
647 }
648
649 public ResultSet getSchemas() throws SQLException {
650 if (_logs.isJDBCEnabled())
651 _logs.logJDBC("getSchemas", LoggingConnection.this);
652 return super.getSchemas();
653 }
654
655 public ResultSet getTablePrivileges(String catalog,
656 String schemaPattern, String tableNamePattern)
657 throws SQLException {
658 if (_logs.isJDBCEnabled())
659 _logs.logJDBC("getTablePrivileges", LoggingConnection.this);
660 return super.getTablePrivileges(catalog, schemaPattern,
661 tableNamePattern);
662 }
663
664 public ResultSet getTables(String catalog, String schemaPattern,
665 String tableNamePattern, String[] types) throws SQLException {
666 if (_logs.isJDBCEnabled())
667 _logs.logJDBC("getTables: "
668 + catalog + ", " + schemaPattern + ", "
669 + tableNamePattern, LoggingConnection.this);
670 return super.getTables(catalog, schemaPattern,
671 tableNamePattern, types);
672 }
673
674 public ResultSet getTableTypes() throws SQLException {
675 if (_logs.isJDBCEnabled())
676 _logs.logJDBC("getTableTypes", LoggingConnection.this);
677 return super.getTableTypes();
678 }
679
680 public ResultSet getTypeInfo() throws SQLException {
681 if (_logs.isJDBCEnabled())
682 _logs.logJDBC("getTypeInfo", LoggingConnection.this);
683 return super.getTypeInfo();
684 }
685
686 public ResultSet getUDTs(String catalog, String schemaPattern,
687 String typeNamePattern, int[] types) throws SQLException {
688 if (_logs.isJDBCEnabled())
689 _logs.logJDBC("getUDTs", LoggingConnection.this);
690 return super.getUDTs(catalog, schemaPattern,
691 typeNamePattern, types);
692 }
693
694 public ResultSet getVersionColumns(String catalog,
695 String schema, String table) throws SQLException {
696 if (_logs.isJDBCEnabled())
697 _logs.logJDBC("getVersionColumns: "
698 + catalog + ", " + schema + ", " + table,
699 LoggingConnection.this);
700 return super.getVersionColumns(catalog, schema, table);
701 }
702 }
703
704 /**
705 * Statement wrapper that logs SQL to the parent data source and
706 * remembers the last piece of SQL to be executed on it.
707 */
708 private class LoggingStatement extends DelegatingStatement {
709
710 private String _sql = null;
711
712 public LoggingStatement(Statement stmnt) throws SQLException {
713 super(stmnt, LoggingConnection.this);
714 }
715
716 public void appendInfo(StringBuffer buf) {
717 if (_sql != null) {
718 buf.append(" ");
719 if (_formatter != null) {
720 buf.append(SEP);
721 buf.append(_formatter.prettyPrint(_sql));
722 } else {
723 buf.append(_sql);
724 }
725 }
726 }
727
728 protected ResultSet wrapResult(ResultSet rs, boolean wrap) {
729 if (!wrap || rs == null)
730 return super.wrapResult(rs, wrap);
731 return new LoggingResultSet(rs, this);
732 }
733
734 public void cancel() throws SQLException {
735 if (_logs.isJDBCEnabled())
736 _logs.logJDBC("cancel " + this, LoggingConnection.this);
737 super.cancel();
738 }
739
740 protected ResultSet executeQuery(String sql, boolean wrap)
741 throws SQLException {
742 _sql = sql;
743 logSQL(this);
744 long start = System.currentTimeMillis();
745 try {
746 return super.executeQuery(sql, wrap);
747 } catch (SQLException se) {
748 throw wrap(se, LoggingStatement.this);
749 } finally {
750 logTime(start);
751 handleSQLWarning(LoggingStatement.this);
752 }
753 }
754
755 public int executeUpdate(String sql) throws SQLException {
756 _sql = sql;
757 logSQL(this);
758 long start = System.currentTimeMillis();
759 try {
760 return super.executeUpdate(sql);
761 } catch (SQLException se) {
762 throw wrap(se, LoggingStatement.this);
763 } finally {
764 logTime(start);
765 handleSQLWarning(LoggingStatement.this);
766 }
767 }
768
769 public boolean execute(String sql) throws SQLException {
770 _sql = sql;
771 logSQL(this);
772 long start = System.currentTimeMillis();
773 try {
774 return super.execute(sql);
775 } catch (SQLException se) {
776 throw wrap(se, LoggingStatement.this);
777 } finally {
778 logTime(start);
779 handleSQLWarning(LoggingStatement.this);
780 }
781 }
782 }
783
784 private class LoggingPreparedStatement
785 extends DelegatingPreparedStatement {
786
787 private final String _sql;
788 private List _params = null;
789 private List _paramBatch = null;
790
791 public LoggingPreparedStatement(PreparedStatement stmnt, String sql)
792 throws SQLException {
793 super(stmnt, LoggingConnection.this);
794 _sql = sql;
795 }
796
797 protected ResultSet wrapResult(ResultSet rs, boolean wrap) {
798 if (!wrap || rs == null)
799 return super.wrapResult(rs, wrap);
800 return new LoggingResultSet(rs, this);
801 }
802
803 protected ResultSet executeQuery(String sql, boolean wrap)
804 throws SQLException {
805 logSQL(this);
806 long start = System.currentTimeMillis();
807 try {
808 return super.executeQuery(sql, wrap);
809 } catch (SQLException se) {
810 throw wrap(se, LoggingPreparedStatement.this);
811 } finally {
812 logTime(start);
813 clearLogParameters(true);
814 handleSQLWarning(LoggingPreparedStatement.this);
815 }
816 }
817
818 public int executeUpdate(String sql) throws SQLException {
819 logSQL(this);
820 long start = System.currentTimeMillis();
821 try {
822 return super.executeUpdate(sql);
823 } catch (SQLException se) {
824 throw wrap(se, LoggingPreparedStatement.this);
825 } finally {
826 logTime(start);
827 clearLogParameters(true);
828 handleSQLWarning(LoggingPreparedStatement.this);
829 }
830 }
831
832 public boolean execute(String sql) throws SQLException {
833 logSQL(this);
834 long start = System.currentTimeMillis();
835 try {
836 return super.execute(sql);
837 } catch (SQLException se) {
838 throw wrap(se, LoggingPreparedStatement.this);
839 } finally {
840 logTime(start);
841 clearLogParameters(true);
842 handleSQLWarning(LoggingPreparedStatement.this);
843 }
844 }
845
846 protected ResultSet executeQuery(boolean wrap) throws SQLException {
847 logSQL(this);
848 long start = System.currentTimeMillis();
849 try {
850 return super.executeQuery(wrap);
851 } catch (SQLException se) {
852 throw wrap(se, LoggingPreparedStatement.this);
853 } finally {
854 logTime(start);
855 clearLogParameters(true);
856 handleSQLWarning(LoggingPreparedStatement.this);
857 }
858 }
859
860 public int executeUpdate() throws SQLException {
861 logSQL(this);
862 long start = System.currentTimeMillis();
863 try {
864 return super.executeUpdate();
865 } catch (SQLException se) {
866 throw wrap(se, LoggingPreparedStatement.this);
867 } finally {
868 logTime(start);
869 clearLogParameters(true);
870 handleSQLWarning(LoggingPreparedStatement.this);
871 }
872 }
873
874 public int[] executeBatch() throws SQLException {
875 logBatchSQL(this);
876 long start = System.currentTimeMillis();
877 try {
878 return super.executeBatch();
879 } catch (SQLException se) {
880 // if the exception is a BatchUpdateException, and
881 // we are tracking parameters, then set the current
882 // parameter set to be the index of the failed
883 // statement so that the ReportingSQLException will
884 // show the correct param
885 if (se instanceof BatchUpdateException
886 && _paramBatch != null && shouldTrackParameters()) {
887 int[] count = ((BatchUpdateException) se).
888 getUpdateCounts();
889 if (count != null && count.length <= _paramBatch.size())
890 {
891 int index = -1;
892 for (int i = 0; i < count.length; i++) {
893 // -3 is Statement.STATEMENT_FAILED, but is
894 // only available in JDK 1.4+
895 if (count[i] == -3) {
896 index = i;
897 break;
898 }
899 }
900
901 // no -3 element: it may be that the server stopped
902 // processing, so the size of the count will be
903 // the index
904 if (index == -1)
905 index = count.length + 1;
906
907 // set the current params to the saved values
908 if (index < _paramBatch.size())
909 _params = (List) _paramBatch.get(index);
910 }
911 }
912 throw wrap(se, LoggingPreparedStatement.this);
913 } finally {
914 logTime(start);
915 clearLogParameters(true);
916 handleSQLWarning(LoggingPreparedStatement.this);
917 }
918 }
919
920 public boolean execute() throws SQLException {
921 logSQL(this);
922 long start = System.currentTimeMillis();
923 try {
924 return super.execute();
925 } catch (SQLException se) {
926 throw wrap(se, LoggingPreparedStatement.this);
927 } finally {
928 logTime(start);
929 clearLogParameters(true);
930 handleSQLWarning(LoggingPreparedStatement.this);
931 }
932 }
933
934 public void cancel() throws SQLException {
935 if (_logs.isJDBCEnabled())
936 _logs.logJDBC("cancel " + this + ": " + _sql,
937 LoggingConnection.this);
938
939 super.cancel();
940 }
941
942 public void setNull(int i1, int i2) throws SQLException {
943 setLogParameter(i1, "null", null);
944 super.setNull(i1, i2);
945 }
946
947 public void setBoolean(int i, boolean b) throws SQLException {
948 setLogParameter(i, b);
949 super.setBoolean(i, b);
950 }
951
952 public void setByte(int i, byte b) throws SQLException {
953 setLogParameter(i, b);
954 super.setByte(i, b);
955 }
956
957 public void setShort(int i, short s) throws SQLException {
958 setLogParameter(i, s);
959 super.setShort(i, s);
960 }
961
962 public void setInt(int i1, int i2) throws SQLException {
963 setLogParameter(i1, i2);
964 super.setInt(i1, i2);
965 }
966
967 public void setLong(int i, long l) throws SQLException {
968 setLogParameter(i, l);
969 super.setLong(i, l);
970 }
971
972 public void setFloat(int i, float f) throws SQLException {
973 setLogParameter(i, f);
974 super.setFloat(i, f);
975 }
976
977 public void setDouble(int i, double d) throws SQLException {
978 setLogParameter(i, d);
979 super.setDouble(i, d);
980 }
981
982 public void setBigDecimal(int i, BigDecimal bd)
983 throws SQLException {
984 setLogParameter(i, "BigDecimal", bd);
985 super.setBigDecimal(i, bd);
986 }
987
988 public void setString(int i, String s) throws SQLException {
989 setLogParameter(i, "String", s);
990 super.setString(i, s);
991 }
992
993 public void setBytes(int i, byte[] b) throws SQLException {
994 setLogParameter(i, "byte[]", b);
995 super.setBytes(i, b);
996 }
997
998 public void setDate(int i, Date d) throws SQLException {
999 setLogParameter(i, "Date", d);
1000 super.setDate(i, d);
1001 }
1002
1003 public void setTime(int i, Time t) throws SQLException {
1004 setLogParameter(i, "Time", t);
1005 super.setTime(i, t);
1006 }
1007
1008 public void setTimestamp(int i, Timestamp t) throws SQLException {
1009 setLogParameter(i, "Timestamp", t);
1010 super.setTimestamp(i, t);
1011 }
1012
1013 public void setAsciiStream(int i1, InputStream is, int i2)
1014 throws SQLException {
1015 setLogParameter(i1, "InputStream", is);
1016 super.setAsciiStream(i1, is, i2);
1017 }
1018
1019 public void setUnicodeStream(int i1, InputStream is, int i2)
1020 throws SQLException {
1021 setLogParameter(i1, "InputStream", is);
1022 super.setUnicodeStream(i2, is, i2);
1023 }
1024
1025 public void setBinaryStream(int i1, InputStream is, int i2)
1026 throws SQLException {
1027 setLogParameter(i1, "InputStream", is);
1028 super.setBinaryStream(i1, is, i2);
1029 }
1030
1031 public void clearParameters() throws SQLException {
1032 clearLogParameters(false);
1033 super.clearParameters();
1034 }
1035
1036 public void setObject(int i1, Object o, int i2, int i3)
1037 throws SQLException {
1038 setLogParameter(i1, "Object", o);
1039 super.setObject(i1, o, i2, i3);
1040 }
1041
1042 public void setObject(int i1, Object o, int i2)
1043 throws SQLException {
1044 setLogParameter(i1, "Object", o);
1045 super.setObject(i1, o, i2);
1046 }
1047
1048 public void setObject(int i, Object o) throws SQLException {
1049 setLogParameter(i, "Object", o);
1050 super.setObject(i, o);
1051 }
1052
1053 public void addBatch() throws SQLException {
1054 if (_logs.isSQLEnabled())
1055 _logs.logSQL("batching " + this, LoggingConnection.this);
1056 long start = System.currentTimeMillis();
1057 try {
1058 super.addBatch();
1059 if (shouldTrackParameters()) {
1060 // make sure our list is initialized
1061 if (_paramBatch == null)
1062 _paramBatch = new ArrayList();
1063 // copy parameters since they will be re-used
1064 if (_params != null)
1065 _paramBatch.add(new ArrayList(_params));
1066 else
1067 _paramBatch.add(null);
1068 }
1069 }
1070 finally {
1071 logTime(start);
1072 }
1073 }
1074
1075 public void setCharacterStream(int i1, Reader r, int i2)
1076 throws SQLException {
1077 setLogParameter(i1, "Reader", r);
1078 super.setCharacterStream(i1, r, i2);
1079 }
1080
1081 public void setRef(int i, Ref r) throws SQLException {
1082 setLogParameter(i, "Ref", r);
1083 super.setRef(i, r);
1084 }
1085
1086 public void setBlob(int i, Blob b) throws SQLException {
1087 setLogParameter(i, "Blob", b);
1088 super.setBlob(i, b);
1089 }
1090
1091 public void setClob(int i, Clob c) throws SQLException {
1092 setLogParameter(i, "Clob", c);
1093 super.setClob(i, c);
1094 }
1095
1096 public void setArray(int i, Array a) throws SQLException {
1097 setLogParameter(i, "Array", a);
1098 super.setArray(i, a);
1099 }
1100
1101 public ResultSetMetaData getMetaData() throws SQLException {
1102 return super.getMetaData();
1103 }
1104
1105 public void setDate(int i, Date d, Calendar c) throws SQLException {
1106 setLogParameter(i, "Date", d);
1107 super.setDate(i, d, c);
1108 }
1109
1110 public void setTime(int i, Time t, Calendar c) throws SQLException {
1111 setLogParameter(i, "Time", t);
1112 super.setTime(i, t, c);
1113 }
1114
1115 public void setTimestamp(int i, Timestamp t, Calendar c)
1116 throws SQLException {
1117 setLogParameter(i, "Timestamp", t);
1118 super.setTimestamp(i, t, c);
1119 }
1120
1121 public void setNull(int i1, int i2, String s) throws SQLException {
1122 setLogParameter(i1, "null", null);
1123 super.setNull(i1, i2, s);
1124 }
1125
1126 protected void appendInfo(StringBuffer buf) {
1127 buf.append(" ");
1128 if (_formatter != null) {
1129 buf.append(SEP);
1130 buf.append(_formatter.prettyPrint(_sql));
1131 buf.append(SEP);
1132 } else {
1133 buf.append(_sql);
1134 }
1135
1136 StringBuffer paramBuf = null;
1137 if (_params != null && !_params.isEmpty()) {
1138 paramBuf = new StringBuffer();
1139 for (Iterator itr = _params.iterator(); itr.hasNext();) {
1140 paramBuf.append(itr.next());
1141 if (itr.hasNext())
1142 paramBuf.append(", ");
1143 }
1144 }
1145
1146 if (paramBuf != null) {
1147 if (!_prettyPrint)
1148 buf.append(" ");
1149 buf.append("[params=").
1150 append(paramBuf.toString()).append("]");
1151 }
1152 super.appendInfo(buf);
1153 }
1154
1155 private void clearLogParameters(boolean batch) {
1156 if (_params != null)
1157 _params.clear();
1158 if (batch && _paramBatch != null)
1159 _paramBatch.clear();
1160 }
1161
1162 private boolean shouldTrackParameters() {
1163 return _trackParameters || _logs.isSQLEnabled();
1164 }
1165
1166 private void setLogParameter(int index, boolean val) {
1167 if (shouldTrackParameters())
1168 setLogParameter(index, "(boolean) " + val);
1169 }
1170
1171 private void setLogParameter(int index, byte val) {
1172 if (shouldTrackParameters())
1173 setLogParameter(index, "(byte) " + val);
1174 }
1175
1176 private void setLogParameter(int index, double val) {
1177 if (shouldTrackParameters())
1178 setLogParameter(index, "(double) " + val);
1179 }
1180
1181 private void setLogParameter(int index, float val) {
1182 if (shouldTrackParameters())
1183 setLogParameter(index, "(float) " + val);
1184 }
1185
1186 private void setLogParameter(int index, int val) {
1187 if (shouldTrackParameters())
1188 setLogParameter(index, "(int) " + val);
1189 }
1190
1191 private void setLogParameter(int index, long val) {
1192 if (shouldTrackParameters())
1193 setLogParameter(index, "(long) " + val);
1194 }
1195
1196 private void setLogParameter(int index, short val) {
1197 if (shouldTrackParameters())
1198 setLogParameter(index, "(short) " + val);
1199 }
1200
1201 private void setLogParameter(int index, String type, Object val) {
1202 if (shouldTrackParameters())
1203 setLogParameter(index, "(" + type + ") " + val);
1204 }
1205
1206 private void setLogParameter(int index, String val) {
1207 if (_params == null)
1208 _params = new ArrayList();
1209 while (_params.size() < index)
1210 _params.add(null);
1211 if (val.length() > 80)
1212 val = val.substring(0, 77) + "...";
1213 _params.set(index - 1, val);
1214 }
1215 }
1216
1217 /**
1218 * Warning-handling result set.
1219 */
1220 private class LoggingResultSet extends DelegatingResultSet {
1221
1222 public LoggingResultSet(ResultSet rs, Statement stmnt) {
1223 super(rs, stmnt);
1224 }
1225
1226 public boolean next() throws SQLException {
1227 try {
1228 return super.next();
1229 } finally {
1230 handleSQLWarning(LoggingResultSet.this);
1231 }
1232 }
1233
1234 public void close() throws SQLException {
1235 try {
1236 super.close();
1237 } finally {
1238 handleSQLWarning(LoggingResultSet.this);
1239 }
1240 }
1241
1242 public void beforeFirst() throws SQLException {
1243 try {
1244 super.beforeFirst();
1245 } finally {
1246 handleSQLWarning(LoggingResultSet.this);
1247 }
1248 }
1249
1250 public void afterLast() throws SQLException {
1251 try {
1252 super.afterLast();
1253 } finally {
1254 handleSQLWarning(LoggingResultSet.this);
1255 }
1256 }
1257
1258 public boolean first() throws SQLException {
1259 try {
1260 return super.first();
1261 } finally {
1262 handleSQLWarning(LoggingResultSet.this);
1263 }
1264 }
1265
1266 public boolean last() throws SQLException {
1267 try {
1268 return super.last();
1269 } finally {
1270 handleSQLWarning(LoggingResultSet.this);
1271 }
1272 }
1273
1274 public boolean absolute(int a) throws SQLException {
1275 try {
1276 return super.absolute(a);
1277 } finally {
1278 handleSQLWarning(LoggingResultSet.this);
1279 }
1280 }
1281
1282 public boolean relative(int a) throws SQLException {
1283 try {
1284 return super.relative(a);
1285 } finally {
1286 handleSQLWarning(LoggingResultSet.this);
1287 }
1288 }
1289
1290 public boolean previous() throws SQLException {
1291 try {
1292 return super.previous();
1293 } finally {
1294 handleSQLWarning(LoggingResultSet.this);
1295 }
1296 }
1297 }
1298 }
1299 }