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 }