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    }