    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 &quot;real&quot; 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 &quot;real&quot; 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    }