    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    }