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.*;
019 import java.util.Map;
020 import java.util.Properties;
021
022 /**
023 * Wraps a JDBC Connection and reports method calls, returns and exceptions.
024 *
025 * This version is for jdbc 4.0.
026 *
027 * @see ConnectionSpy for the jdbc 3 version.
028 *
029 * @author Arthur Blake
030 */
031 public class ConnectionSpy implements Connection, Spy
032 {
033 private Connection realConnection;
034
035 private SpyLogDelegator log;
036
037 private int connectionNumber;
038 private static int lastConnectionNumber = 0;
039 private static final Object connectionNumberLock = new Object();
040
041 /**
042 * Create a new ConnectionSpy that wraps a given Connection.
043 *
044 * @param realConnection "real" Connection that this ConnectionSpy wraps.
045 */
046 public ConnectionSpy(Connection realConnection)
047 {
048 setRdbmsSpecifics(DriverSpy.defaultRdbmsSpecifics); // just in case it's not initialized
049 if (realConnection == null)
050 {
051 throw new IllegalArgumentException("Must pass in a non null real Connection");
052 }
053 this.realConnection = realConnection;
054 log = SpyLogFactory.getSpyLogDelegator();
055
056 synchronized (connectionNumberLock)
057 {
058 connectionNumber = ++lastConnectionNumber;
059 }
060 }
061
062 /**
063 * Create a new ConnectionSpy that wraps a given Connection.
064 *
065 * @param realConnection "real" Connection that this ConnectionSpy wraps.
066 * @param rdbmsSpecifics the RdbmsSpecifics object for formatting logging appropriate for the Rdbms used.
067 */
068 public ConnectionSpy(Connection realConnection, RdbmsSpecifics rdbmsSpecifics)
069 {
070 setRdbmsSpecifics(rdbmsSpecifics);
071 if (realConnection == null)
072 {
073 throw new IllegalArgumentException("Must pass in a non null real Connection");
074 }
075 this.realConnection = realConnection;
076 log = SpyLogFactory.getSpyLogDelegator();
077
078 synchronized (connectionNumberLock)
079 {
080 connectionNumber = ++lastConnectionNumber;
081 }
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 //todo: dump the array here?
384 String methodCall = "prepareStatement(" + sql + ", " + columnIndexes + ")";
385 try
386 {
387 PreparedStatement statement = realConnection.prepareStatement(sql, columnIndexes);
388 return (PreparedStatement) reportReturn(methodCall, new PreparedStatementSpy(sql, this, statement));
389 }
390 catch (SQLException s)
391 {
392 reportException(methodCall, s, sql);
393 throw s;
394 }
395 }
396
397 public Savepoint setSavepoint(String name) throws SQLException
398 {
399 String methodCall = "setSavepoint(" + name + ")";
400 try
401 {
402 return (Savepoint) reportReturn(methodCall, realConnection.setSavepoint(name));
403 }
404 catch (SQLException s)
405 {
406 reportException(methodCall, s);
407 throw s;
408 }
409 }
410
411 public PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException
412 {
413 //todo: dump the array here?
414 String methodCall = "prepareStatement(" + sql + ", " + columnNames + ")";
415 try
416 {
417 PreparedStatement statement = realConnection.prepareStatement(sql, columnNames);
418 return (PreparedStatement) reportReturn(methodCall, new PreparedStatementSpy(sql, this, statement));
419 }
420 catch (SQLException s)
421 {
422 reportException(methodCall, s, sql);
423 throw s;
424 }
425 }
426
427 public Clob createClob() throws SQLException {
428 String methodCall = "createClob()";
429 try
430 {
431 return (Clob) reportReturn(methodCall, realConnection.createClob());
432 }
433 catch (SQLException s)
434 {
435 reportException(methodCall, s);
436 throw s;
437 }
438 }
439
440 public Blob createBlob() throws SQLException {
441 String methodCall = "createBlob()";
442 try
443 {
444 return (Blob) reportReturn(methodCall, realConnection.createBlob());
445 }
446 catch (SQLException s)
447 {
448 reportException(methodCall, s);
449 throw s;
450 }
451 }
452
453 public NClob createNClob() throws SQLException {
454 String methodCall = "createNClob()";
455 try
456 {
457 return (NClob) reportReturn(methodCall, realConnection.createNClob());
458 }
459 catch (SQLException s)
460 {
461 reportException(methodCall, s);
462 throw s;
463 }
464 }
465
466 public SQLXML createSQLXML() throws SQLException {
467 String methodCall = "createSQLXML()";
468 try
469 {
470 return (SQLXML) reportReturn(methodCall, realConnection.createSQLXML());
471 }
472 catch (SQLException s)
473 {
474 reportException(methodCall, s);
475 throw s;
476 }
477 }
478
479 public boolean isValid(int timeout) throws SQLException {
480 String methodCall = "isValid(" + timeout + ")";
481 try
482 {
483 return reportReturn(methodCall,realConnection.isValid(timeout));
484 }
485 catch (SQLException s)
486 {
487 reportException(methodCall, s);
488 throw s;
489 }
490 }
491
492 public void setClientInfo(String name, String value) throws SQLClientInfoException {
493 String methodCall = "setClientInfo(" + name + ", " + value + ")";
494 try
495 {
496 realConnection.setClientInfo(name,value);
497 }
498 catch (SQLClientInfoException s)
499 {
500 reportException(methodCall, s);
501 throw s;
502 }
503 reportReturn(methodCall);
504 }
505
506 public void setClientInfo(Properties properties) throws SQLClientInfoException {
507 // todo: dump properties?
508 String methodCall = "setClientInfo(" + properties + ")";
509 try
510 {
511 realConnection.setClientInfo(properties);
512 }
513 catch (SQLClientInfoException s)
514 {
515 reportException(methodCall, s);
516 throw s;
517 }
518 reportReturn(methodCall);
519 }
520
521 public String getClientInfo(String name) throws SQLException {
522 String methodCall = "getClientInfo(" + name + ")";
523 try
524 {
525 return (String) reportReturn(methodCall,realConnection.getClientInfo(name));
526 }
527 catch (SQLException s)
528 {
529 reportException(methodCall, s);
530 throw s;
531 }
532 }
533
534 public Properties getClientInfo() throws SQLException {
535 String methodCall = "getClientInfo()";
536 try
537 {
538 return (Properties) reportReturn(methodCall,realConnection.getClientInfo());
539 }
540 catch (SQLException s)
541 {
542 reportException(methodCall, s);
543 throw s;
544 }
545 }
546
547 public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
548 //todo: dump elements?
549 String methodCall = "createArrayOf(" + typeName + ", " + elements +")";
550 try
551 {
552 return (Array) reportReturn(methodCall,realConnection.createArrayOf(typeName,elements));
553 }
554 catch (SQLException s)
555 {
556 reportException(methodCall, s);
557 throw s;
558 }
559 }
560
561 public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
562 //todo: dump attributes?
563 String methodCall = "createStruct(" + typeName + ", " + attributes +")";
564 try
565 {
566 return (Struct) reportReturn(methodCall,realConnection.createStruct(typeName, attributes));
567 }
568 catch (SQLException s)
569 {
570 reportException(methodCall, s);
571 throw s;
572 }
573 }
574
575 public boolean isReadOnly() throws SQLException
576 {
577 String methodCall = "isReadOnly()";
578 try
579 {
580 return reportReturn(methodCall,realConnection.isReadOnly());
581 }
582 catch (SQLException s)
583 {
584 reportException(methodCall, s);
585 throw s;
586 }
587 }
588
589 public void setHoldability(int holdability) throws SQLException
590 {
591 String methodCall = "setHoldability(" + holdability + ")";
592 try
593 {
594 realConnection.setHoldability(holdability);
595 }
596 catch (SQLException s)
597 {
598 reportException(methodCall, s);
599 throw s;
600 }
601 reportReturn(methodCall);
602 }
603
604 public CallableStatement prepareCall(String sql) throws SQLException
605 {
606 String methodCall = "prepareCall(" + sql + ")";
607 try
608 {
609 CallableStatement statement = realConnection.prepareCall(sql);
610 return (CallableStatement) reportReturn(methodCall, new CallableStatementSpy(sql, this, statement));
611 }
612 catch (SQLException s)
613 {
614 reportException(methodCall, s, sql);
615 throw s;
616 }
617 }
618
619 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException
620 {
621 String methodCall = "prepareCall(" + sql + ", " + resultSetType + ", " + resultSetConcurrency + ")";
622 try
623 {
624 CallableStatement statement = realConnection.prepareCall(sql, resultSetType, resultSetConcurrency);
625 return (CallableStatement) reportReturn(methodCall, new CallableStatementSpy(sql, this, statement));
626 }
627 catch (SQLException s)
628 {
629 reportException(methodCall, s, sql);
630 throw s;
631 }
632 }
633
634 public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
635 int resultSetHoldability) throws SQLException
636 {
637 String methodCall = "prepareCall(" + sql + ", " + resultSetType + ", " + resultSetConcurrency + ", " + resultSetHoldability + ")";
638 try
639 {
640 CallableStatement statement = realConnection.prepareCall(sql, resultSetType, resultSetConcurrency,
641 resultSetHoldability);
642 return (CallableStatement) reportReturn(methodCall, new CallableStatementSpy(sql, this, statement));
643 }
644 catch (SQLException s)
645 {
646 reportException(methodCall, s, sql);
647 throw s;
648 }
649 }
650
651 public void setCatalog(String catalog) throws SQLException
652 {
653 String methodCall = "setCatalog(" + catalog + ")";
654 try
655 {
656 realConnection.setCatalog(catalog);
657 }
658 catch (SQLException s)
659 {
660 reportException(methodCall, s);
661 throw s;
662 }
663 reportReturn(methodCall);
664 }
665
666 public String nativeSQL(String sql) throws SQLException
667 {
668 String methodCall = "nativeSQL(" + sql + ")";
669 try
670 {
671 return (String) reportReturn(methodCall, realConnection.nativeSQL(sql));
672 }
673 catch (SQLException s)
674 {
675 reportException(methodCall, s, sql);
676 throw s;
677 }
678 }
679
680 public Map<String,Class<?>> getTypeMap() throws SQLException
681 {
682 String methodCall = "getTypeMap()";
683 try
684 {
685 return (Map<String,Class<?>>) reportReturn(methodCall, realConnection.getTypeMap());
686 }
687 catch (SQLException s)
688 {
689 reportException(methodCall, s);
690 throw s;
691 }
692 }
693
694 public void setAutoCommit(boolean autoCommit) throws SQLException
695 {
696 String methodCall = "setAutoCommit(" + autoCommit + ")";
697 try
698 {
699 realConnection.setAutoCommit(autoCommit);
700 }
701 catch (SQLException s)
702 {
703 reportException(methodCall, s);
704 throw s;
705 }
706 reportReturn(methodCall);
707 }
708
709 public String getCatalog() throws SQLException
710 {
711 String methodCall = "getCatalog()";
712 try
713 {
714 return (String) reportReturn(methodCall, realConnection.getCatalog());
715 }
716 catch (SQLException s)
717 {
718 reportException(methodCall, s);
719 throw s;
720 }
721 }
722
723 public void setTypeMap(java.util.Map<String,Class<?>> map) throws SQLException
724 {
725 //todo: dump map??
726 String methodCall = "setTypeMap(" + map + ")";
727 try
728 {
729 realConnection.setTypeMap(map);
730 }
731 catch (SQLException s)
732 {
733 reportException(methodCall, s);
734 throw s;
735 }
736 reportReturn(methodCall);
737 }
738
739 public void setTransactionIsolation(int level) throws SQLException
740 {
741 String methodCall = "setTransactionIsolation(" + level + ")";
742 try
743 {
744 realConnection.setTransactionIsolation(level);
745 }
746 catch (SQLException s)
747 {
748 reportException(methodCall, s);
749 throw s;
750 }
751 reportReturn(methodCall);
752 }
753
754 public boolean getAutoCommit() throws SQLException
755 {
756 String methodCall = "getAutoCommit()";
757 try
758 {
759 return reportReturn(methodCall, realConnection.getAutoCommit());
760 }
761 catch (SQLException s)
762 {
763 reportException(methodCall, s);
764 throw s;
765 }
766 }
767
768 public int getHoldability() throws SQLException
769 {
770 String methodCall = "getHoldability()";
771 try
772 {
773 return reportReturn(methodCall, realConnection.getHoldability());
774 }
775 catch (SQLException s)
776 {
777 reportException(methodCall, s);
778 throw s;
779 }
780 }
781
782 public int getTransactionIsolation() throws SQLException
783 {
784 String methodCall = "getTransactionIsolation()";
785 try
786 {
787 return reportReturn(methodCall, realConnection.getTransactionIsolation());
788 }
789 catch (SQLException s)
790 {
791 reportException(methodCall, s);
792 throw s;
793 }
794 }
795
796 public void commit() throws SQLException
797 {
798 String methodCall = "commit()";
799 try
800 {
801 realConnection.commit();
802 }
803 catch (SQLException s)
804 {
805 reportException(methodCall, s);
806 throw s;
807 }
808 reportReturn(methodCall);
809 }
810
811 public void rollback() throws SQLException
812 {
813 String methodCall = "rollback()";
814 try
815 {
816 realConnection.rollback();
817 }
818 catch (SQLException s)
819 {
820 reportException(methodCall, s);
821 throw s;
822 }
823 reportReturn(methodCall);
824 }
825
826 public void close() throws SQLException
827 {
828 String methodCall = "close()";
829 try
830 {
831 realConnection.close();
832 }
833 catch (SQLException s)
834 {
835 reportException(methodCall, s);
836 throw s;
837 }
838 reportReturn(methodCall);
839 }
840
841 public <T> T unwrap(Class<T> iface) throws SQLException {
842 String methodCall = "unwrap(" + (iface==null?"null":iface.getName()) + ")";
843 try
844 {
845 //todo: double check this logic
846 return (T)reportReturn(methodCall, (iface != null && (iface == Connection.class || iface == Spy.class))?(T)this:realConnection.unwrap(iface));
847 }
848 catch (SQLException s)
849 {
850 reportException(methodCall,s);
851 throw s;
852 }
853 }
854
855 public boolean isWrapperFor(Class<?> iface) throws SQLException
856 {
857 String methodCall = "isWrapperFor(" + (iface==null?"null":iface.getName()) + ")";
858 try
859 {
860 return reportReturn(methodCall, (iface != null && (iface == Connection.class || iface == Spy.class)) ||
861 realConnection.isWrapperFor(iface));
862 }
863 catch (SQLException s)
864 {
865 reportException(methodCall,s);
866 throw s;
867 }
868 }
869 }