001 /**
002 * Copyright 2007-2008 Arthur Blake
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package net.sf.log4jdbc;
017
018 import java.sql.CallableStatement;
019 import java.sql.Connection;
020 import java.sql.DatabaseMetaData;
021 import java.sql.PreparedStatement;
022 import java.sql.SQLException;
023 import java.sql.SQLWarning;
024 import java.sql.Savepoint;
025 import java.sql.Statement;
026
027 /**
028 * Wraps a JDBC Connection and reports method calls, returns and exceptions.
029 *
030 * @author Arthur Blake
031 */
032 public class ConnectionSpy implements Connection, Spy
033 {
034 private Connection realConnection;
035
036 private SpyLogDelegator log;
037
038 private int connectionNumber;
039 private static int lastConnectionNumber = 0;
040 private static final Object connectionNumberLock = new Object();
041
042 /**
043 * Create a new ConnectionSpy that wraps a given Connection.
044 *
045 * @param realConnection "real" Connection that this ConnectionSpy wraps.
046 */
047 public ConnectionSpy(Connection realConnection)
048 {
049 setRdbmsSpecifics(DriverSpy.defaultRdbmsSpecifics); // just in case it's not initialized
050 if (realConnection == null)
051 {
052 throw new IllegalArgumentException("Must pass in a non null real Connection");
053 }
054 this.realConnection = realConnection;
055 log = SpyLogFactory.getSpyLogDelegator();
056
057 synchronized (connectionNumberLock)
058 {
059 connectionNumber = ++lastConnectionNumber;
060 }
061 }
062
063 /**
064 * Create a new ConnectionSpy that wraps a given Connection.
065 *
066 * @param realConnection "real" Connection that this ConnectionSpy wraps.
067 * @param rdbmsSpecifics the RdbmsSpecifics object for formatting logging appropriate for the Rdbms used.
068 */
069 public ConnectionSpy(Connection realConnection, RdbmsSpecifics rdbmsSpecifics)
070 {
071 setRdbmsSpecifics(rdbmsSpecifics);
072 if (realConnection == null)
073 {
074 throw new IllegalArgumentException("Must pass in a non null real Connection");
075 }
076 this.realConnection = realConnection;
077 log = SpyLogFactory.getSpyLogDelegator();
078
079 synchronized (connectionNumberLock)
080 {
081 connectionNumber = ++lastConnectionNumber;
082 }
083 }
084
085 private RdbmsSpecifics rdbmsSpecifics;
086
087 /**
088 * Set the RdbmsSpecifics object for formatting logging appropriate for the Rdbms used on this connection.
089 *
090 * @param rdbmsSpecifics the RdbmsSpecifics object for formatting logging appropriate for the Rdbms used.
091 */
092 void setRdbmsSpecifics(RdbmsSpecifics rdbmsSpecifics)
093 {
094 this.rdbmsSpecifics = rdbmsSpecifics;
095 }
096
097 /**
098 * Get the RdbmsSpecifics object for formatting logging appropriate for the Rdbms used on this connection.
099 *
100 * @return the RdbmsSpecifics object for formatting logging appropriate for the Rdbms used.
101 */
102 RdbmsSpecifics getRdbmsSpecifics()
103 {
104 return rdbmsSpecifics;
105 }
106
107 public int getConnectionNumber()
108 {
109 return connectionNumber;
110 }
111
112 public String getClassType()
113 {
114 return "Connection";
115 }
116
117 protected void reportException(String methodCall, SQLException exception, String sql)
118 {
119 log.exceptionOccured(this, methodCall, exception, sql, -1L);
120 }
121
122 protected void reportException(String methodCall, SQLException exception)
123 {
124 log.exceptionOccured(this, methodCall, exception, null, -1L);
125 }
126
127 protected void reportAllReturns(String methodCall, String returnValue)
128 {
129 log.methodReturned(this, methodCall, returnValue);
130 }
131
132 private boolean reportReturn(String methodCall, boolean value)
133 {
134 reportAllReturns(methodCall, "" + value);
135 return value;
136 }
137
138 private int reportReturn(String methodCall, int value)
139 {
140 reportAllReturns(methodCall, "" + value);
141 return value;
142 }
143
144 private Object reportReturn(String methodCall, Object value)
145 {
146 reportAllReturns(methodCall, "" + value);
147 return value;
148 }
149
150 private void reportReturn(String methodCall)
151 {
152 reportAllReturns(methodCall, "");
153 }
154
155 // forwarding methods
156
157 public boolean isClosed() throws SQLException
158 {
159 String methodCall = "isClosed()";
160 try
161 {
162 return reportReturn(methodCall, (realConnection.isClosed()));
163 }
164 catch (SQLException s)
165 {
166 reportException(methodCall, s);
167 throw s;
168 }
169 }
170
171 public SQLWarning getWarnings() throws SQLException
172 {
173 String methodCall = "getWarnings()";
174 try
175 {
176 return (SQLWarning) reportReturn(methodCall, realConnection.getWarnings());
177 }
178 catch (SQLException s)
179 {
180 reportException(methodCall, s);
181 throw s;
182 }
183 }
184
185 public Savepoint setSavepoint() throws SQLException
186 {
187 String methodCall = "setSavepoint()";
188 try
189 {
190 return (Savepoint) reportReturn(methodCall, realConnection.setSavepoint());
191 }
192 catch (SQLException s)
193 {
194 reportException(methodCall, s);
195 throw s;
196 }
197 }
198
199 public void releaseSavepoint(Savepoint savepoint) throws SQLException
200 {
201 String methodCall = "releaseSavepoint(" + savepoint + ")";
202 try
203 {
204 realConnection.releaseSavepoint(savepoint);
205 }
206 catch (SQLException s)
207 {
208 reportException(methodCall, s);
209 throw s;
210 }
211 reportReturn(methodCall);
212 }
213
214 public void rollback(Savepoint savepoint) throws SQLException
215 {
216 String methodCall = "rollback(" + savepoint + ")";
217 try
218 {
219 realConnection.rollback(savepoint);
220 }
221 catch (SQLException s)
222 {
223 reportException(methodCall, s);
224 throw s;
225 }
226 reportReturn(methodCall);
227 }
228
229 public DatabaseMetaData getMetaData() throws SQLException
230 {
231 String methodCall = "getMetaData()";
232 try
233 {
234 return (DatabaseMetaData) reportReturn(methodCall, realConnection.getMetaData());
235 }
236 catch (SQLException s)
237 {
238 reportException(methodCall, s);
239 throw s;
240 }
241 }
242
243 public void clearWarnings() throws SQLException
244 {
245 String methodCall = "clearWarnings()";
246 try
247 {
248 realConnection.clearWarnings();
249 }
250 catch (SQLException s)
251 {
252 reportException(methodCall, s);
253 throw s;
254 }
255 reportReturn(methodCall);
256 }
257
258 public Statement createStatement() throws SQLException
259 {
260 String methodCall = "createStatement()";
261 try
262 {
263 Statement statement = realConnection.createStatement();
264 return (Statement) reportReturn(methodCall, new StatementSpy(this, statement));
265 }
266 catch (SQLException s)
267 {
268 reportException(methodCall, s);
269 throw s;
270 }
271 }
272
273 public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException
274 {
275 String methodCall = "createStatement(" + resultSetType + ", " + resultSetConcurrency + ")";
276 try
277 {
278 Statement statement = realConnection.createStatement(resultSetType, resultSetConcurrency);
279 return (Statement) reportReturn(methodCall, new StatementSpy(this, statement));
280 }
281 catch (SQLException s)
282 {
283 reportException(methodCall, s);
284 throw s;
285 }
286 }
287
288 public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException
289 {
290 String methodCall = "createStatement(" + resultSetType + ", " + resultSetConcurrency + ", " + resultSetHoldability + ")";
291 try
292 {
293 Statement statement = realConnection.createStatement(resultSetType, resultSetConcurrency,
294 resultSetHoldability);
295 return (Statement) reportReturn(methodCall, new StatementSpy(this, statement));
296 }
297 catch (SQLException s)
298 {
299 reportException(methodCall, s);
300 throw s;
301 }
302 }
303
304 public void setReadOnly(boolean readOnly) throws SQLException
305 {
306 String methodCall = "setReadOnly(" + readOnly + ")";
307 try
308 {
309 realConnection.setReadOnly(readOnly);
310 }
311 catch (SQLException s)
312 {
313 reportException(methodCall, s);
314 throw s;
315 }
316 reportReturn(methodCall);
317 }
318
319 public PreparedStatement prepareStatement(String sql) throws SQLException
320 {
321 String methodCall = "prepareStatement(" + sql + ")";
322 try
323 {
324 PreparedStatement statement = realConnection.prepareStatement(sql);
325 return (PreparedStatement) reportReturn(methodCall, new PreparedStatementSpy(sql, this, statement));
326 }
327 catch (SQLException s)
328 {
329 reportException(methodCall, s, sql);
330 throw s;
331 }
332 }
333
334 public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException
335 {
336 String methodCall = "prepareStatement(" + sql + ", " + autoGeneratedKeys + ")";
337 try
338 {
339 PreparedStatement statement = realConnection.prepareStatement(sql, autoGeneratedKeys);
340 return (PreparedStatement) reportReturn(methodCall, new PreparedStatementSpy(sql, this, statement));
341 }
342 catch (SQLException s)
343 {
344 reportException(methodCall, s, sql);
345 throw s;
346 }
347 }
348
349 public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
350 {
351 String methodCall = "prepareStatement(" + sql + ", " + resultSetType + ", " + resultSetConcurrency + ")";
352 try
353 {
354 PreparedStatement statement = realConnection.prepareStatement(sql, resultSetType, resultSetConcurrency);
355 return (PreparedStatement) reportReturn(methodCall, new PreparedStatementSpy(sql, this, statement));
356 }
357 catch (SQLException s)
358 {
359 reportException(methodCall, s, sql);
360 throw s;
361 }
362 }
363
364 public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
365 int resultSetHoldability) throws SQLException
366 {
367 String methodCall = "prepareStatement(" + sql + ", " + resultSetType + ", " + resultSetConcurrency + ", " + resultSetHoldability + ")";
368 try
369 {
370 PreparedStatement statement = realConnection.prepareStatement(sql, resultSetType, resultSetConcurrency,
371 resultSetHoldability);
372 return (PreparedStatement) reportReturn(methodCall, new PreparedStatementSpy(sql, this, statement));
373 }
374 catch (SQLException s)
375 {
376 reportException(methodCall, s, sql);
377 throw s;
378 }
379 }
380
381 public PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException
382 {
383 String methodCall = "prepareStatement(" + sql + ", " + columnIndexes + ")";
384 try
385 {
386 PreparedStatement statement = realConnection.prepareStatement(sql, columnIndexes);
387 return (PreparedStatement) reportReturn(methodCall, new PreparedStatementSpy(sql, this, statement));
388 }
389 catch (SQLException s)
390 {
391 reportException(methodCall, s, sql);
392 throw s;
393 }
394 }
395
396 public Savepoint setSavepoint(String name) throws SQLException
397 {
398 String methodCall = "setSavepoint(" + name + ")";
399 try
400 {
401 return (Savepoint) reportReturn(methodCall, realConnection.setSavepoint(name));
402 }
403 catch (SQLException s)
404 {
405 reportException(methodCall, s);
406 throw s;
407 }
408 }
409
410 public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException
411 {
412 String methodCall = "prepareStatement(" + sql + ", " + columnNames + ")";
413 try
414 {
415 PreparedStatement statement = realConnection.prepareStatement(sql, columnNames);
416 return (PreparedStatement) reportReturn(methodCall, new PreparedStatementSpy(sql, this, statement));
417 }
418 catch (SQLException s)
419 {
420 reportException(methodCall, s, sql);
421 throw s;
422 }
423 }
424
425 public boolean isReadOnly() throws SQLException
426 {
427 String methodCall = "isReadOnly()";
428 try
429 {
430 return reportReturn(methodCall,realConnection.isReadOnly());
431 }
432 catch (SQLException s)
433 {
434 reportException(methodCall, s);
435 throw s;
436 }
437 }
438
439 public void setHoldability(int holdability) throws SQLException
440 {
441 String methodCall = "setHoldability(" + holdability + ")";
442 try
443 {
444 realConnection.setHoldability(holdability);
445 }
446 catch (SQLException s)
447 {
448 reportException(methodCall, s);
449 throw s;
450 }
451 reportReturn(methodCall);
452 }
453
454 public CallableStatement prepareCall(String sql) throws SQLException
455 {
456 String methodCall = "prepareCall(" + sql + ")";
457 try
458 {
459 CallableStatement statement = realConnection.prepareCall(sql);
460 return (CallableStatement) reportReturn(methodCall, new CallableStatementSpy(sql, this, statement));
461 }
462 catch (SQLException s)
463 {
464 reportException(methodCall, s, sql);
465 throw s;
466 }
467 }
468
469 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
470 {
471 String methodCall = "prepareCall(" + sql + ", " + resultSetType + ", " + resultSetConcurrency + ")";
472 try
473 {
474 CallableStatement statement = realConnection.prepareCall(sql, resultSetType, resultSetConcurrency);
475 return (CallableStatement) reportReturn(methodCall, new CallableStatementSpy(sql, this, statement));
476 }
477 catch (SQLException s)
478 {
479 reportException(methodCall, s, sql);
480 throw s;
481 }
482 }
483
484 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
485 int resultSetHoldability) throws SQLException
486 {
487 String methodCall = "prepareCall(" + sql + ", " + resultSetType + ", " + resultSetConcurrency + ", " + resultSetHoldability + ")";
488 try
489 {
490 CallableStatement statement = realConnection.prepareCall(sql, resultSetType, resultSetConcurrency,
491 resultSetHoldability);
492 return (CallableStatement) reportReturn(methodCall, new CallableStatementSpy(sql, this, statement));
493 }
494 catch (SQLException s)
495 {
496 reportException(methodCall, s, sql);
497 throw s;
498 }
499 }
500
501 public void setCatalog(String catalog) throws SQLException
502 {
503 String methodCall = "setCatalog(" + catalog + ")";
504 try
505 {
506 realConnection.setCatalog(catalog);
507 }
508 catch (SQLException s)
509 {
510 reportException(methodCall, s);
511 throw s;
512 }
513 reportReturn(methodCall);
514 }
515
516 public String nativeSQL(String sql) throws SQLException
517 {
518 String methodCall = "nativeSQL(" + sql + ")";
519 try
520 {
521 return (String) reportReturn(methodCall, realConnection.nativeSQL(sql));
522 }
523 catch (SQLException s)
524 {
525 reportException(methodCall, s, sql);
526 throw s;
527 }
528 }
529
530 public java.util.Map getTypeMap() throws SQLException
531 {
532 String methodCall = "getTypeMap()";
533 try
534 {
535 return (java.util.Map) reportReturn(methodCall, realConnection.getTypeMap());
536 }
537 catch (SQLException s)
538 {
539 reportException(methodCall, s);
540 throw s;
541 }
542 }
543
544 public void setAutoCommit(boolean autoCommit) throws SQLException
545 {
546 String methodCall = "setAutoCommit(" + autoCommit + ")";
547 try
548 {
549 realConnection.setAutoCommit(autoCommit);
550 }
551 catch (SQLException s)
552 {
553 reportException(methodCall, s);
554 throw s;
555 }
556 reportReturn(methodCall);
557 }
558
559 public String getCatalog() throws SQLException
560 {
561 String methodCall = "getCatalog()";
562 try
563 {
564 return (String) reportReturn(methodCall, realConnection.getCatalog());
565 }
566 catch (SQLException s)
567 {
568 reportException(methodCall, s);
569 throw s;
570 }
571 }
572
573 public void setTypeMap(java.util.Map map) throws SQLException
574 {
575 //todo: dump map?
576 String methodCall = "setTypeMap(" + map + ")";
577 try
578 {
579 realConnection.setTypeMap(map);
580 }
581 catch (SQLException s)
582 {
583 reportException(methodCall, s);
584 throw s;
585 }
586 reportReturn(methodCall);
587 }
588
589 public void setTransactionIsolation(int level) throws SQLException
590 {
591 String methodCall = "setTransactionIsolation(" + level + ")";
592 try
593 {
594 realConnection.setTransactionIsolation(level);
595 }
596 catch (SQLException s)
597 {
598 reportException(methodCall, s);
599 throw s;
600 }
601 reportReturn(methodCall);
602 }
603
604 public boolean getAutoCommit() throws SQLException
605 {
606 String methodCall = "getAutoCommit()";
607 try
608 {
609 return reportReturn(methodCall, realConnection.getAutoCommit());
610 }
611 catch (SQLException s)
612 {
613 reportException(methodCall, s);
614 throw s;
615 }
616 }
617
618 public int getHoldability() throws SQLException
619 {
620 String methodCall = "getHoldability()";
621 try
622 {
623 return reportReturn(methodCall, realConnection.getHoldability());
624 }
625 catch (SQLException s)
626 {
627 reportException(methodCall, s);
628 throw s;
629 }
630 }
631
632 public int getTransactionIsolation() throws SQLException
633 {
634 String methodCall = "getTransactionIsolation()";
635 try
636 {
637 return reportReturn(methodCall, realConnection.getTransactionIsolation());
638 }
639 catch (SQLException s)
640 {
641 reportException(methodCall, s);
642 throw s;
643 }
644 }
645
646 public void commit() throws SQLException
647 {
648 String methodCall = "commit()";
649 try
650 {
651 realConnection.commit();
652 }
653 catch (SQLException s)
654 {
655 reportException(methodCall, s);
656 throw s;
657 }
658 reportReturn(methodCall);
659 }
660
661 public void rollback() throws SQLException
662 {
663 String methodCall = "rollback()";
664 try
665 {
666 realConnection.rollback();
667 }
668 catch (SQLException s)
669 {
670 reportException(methodCall, s);
671 throw s;
672 }
673 reportReturn(methodCall);
674 }
675
676 public void close() throws SQLException
677 {
678 String methodCall = "close()";
679 try
680 {
681 realConnection.close();
682 }
683 catch (SQLException s)
684 {
685 reportException(methodCall, s);
686 throw s;
687 }
688 reportReturn(methodCall);
689 }
690 }