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 }