KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mchange > v2 > c3p0 > impl > C3P0PooledConnection


1 /*
2  * Distributed as part of c3p0 v.0.9.1
3  *
4  * Copyright (C) 2005 Machinery For Change, Inc.
5  *
6  * Author: Steve Waldman <swaldman@mchange.com>
7  *
8  * This library is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License version 2.1, as
10  * published by the Free Software Foundation.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this software; see the file LICENSE. If not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */

22
23
24 package com.mchange.v2.c3p0.impl;
25
26 import java.lang.reflect.*;
27 import java.sql.*;
28 import java.util.*;
29 import javax.sql.*;
30 import com.mchange.v2.log.*;
31 import com.mchange.v2.sql.*;
32 import com.mchange.v2.sql.filter.*;
33 import com.mchange.v2.c3p0.*;
34 import com.mchange.v2.c3p0.stmt.*;
35 import com.mchange.v2.c3p0.C3P0ProxyConnection;
36 import com.mchange.v2.c3p0.util.ConnectionEventSupport;
37 import com.mchange.v2.lang.ObjectUtils;
38
39 public final class C3P0PooledConnection extends AbstractC3P0PooledConnection
40 {
41     final static MLogger logger = MLog.getLogger( C3P0PooledConnection.class );
42
43     final static Class JavaDoc[] PROXY_CTOR_ARGS = new Class JavaDoc[]{ InvocationHandler.class };
44
45     final static Constructor CON_PROXY_CTOR;
46
47     final static Method RS_CLOSE_METHOD;
48     final static Method STMT_CLOSE_METHOD;
49
50     final static Object JavaDoc[] CLOSE_ARGS;
51
52     final static Set OBJECT_METHODS;
53
54     /**
55      * @deprecated use or rewrite in terms of ReflectUtils.findProxyConstructor()
56      */

57     private static Constructor createProxyConstructor(Class JavaDoc intfc) throws NoSuchMethodException JavaDoc
58     {
59     Class JavaDoc[] proxyInterfaces = new Class JavaDoc[] { intfc };
60     Class JavaDoc proxyCl = Proxy.getProxyClass(C3P0PooledConnection.class.getClassLoader(), proxyInterfaces);
61     return proxyCl.getConstructor( PROXY_CTOR_ARGS );
62     }
63
64     static
65     {
66     try
67         {
68         CON_PROXY_CTOR = createProxyConstructor( ProxyConnection.class );
69
70         Class JavaDoc[] argClasses = new Class JavaDoc[0];
71         RS_CLOSE_METHOD = ResultSet.class.getMethod("close", argClasses);
72         STMT_CLOSE_METHOD = Statement.class.getMethod("close", argClasses);
73
74         CLOSE_ARGS = new Object JavaDoc[0];
75
76         OBJECT_METHODS = Collections.unmodifiableSet( new HashSet( Arrays.asList( Object JavaDoc.class.getMethods() ) ) );
77         }
78     catch (Exception JavaDoc e)
79         {
80         //e.printStackTrace();
81
logger.log(MLevel.SEVERE, "An Exception occurred in static initializer of" + C3P0PooledConnection.class.getName(), e);
82         throw new InternalError JavaDoc("Something is very wrong, or this is a pre 1.3 JVM." +
83                     "We cannot set up dynamic proxies and/or methods!");
84         }
85     }
86
87     //MT: post-constructor constants
88
final ConnectionTester connectionTester;
89     final boolean autoCommitOnClose;
90     final boolean forceIgnoreUnresolvedTransactions;
91     final boolean supports_setTypeMap;
92     final boolean supports_setHoldability;
93     final int dflt_txn_isolation;
94     final String JavaDoc dflt_catalog;
95     final int dflt_holdability;
96
97     //MT: thread-safe
98
final ConnectionEventSupport ces = new ConnectionEventSupport(this);
99
100     //MT: threadsafe, but reassigned (on close)
101
volatile Connection physicalConnection;
102     volatile Exception JavaDoc invalidatingException = null;
103
104     //MT: threadsafe, but reassigned, and a read + reassignment must happen
105
// atomically. protected by this' lock.
106
ProxyConnection exposedProxy;
107
108     //MT: protected by this' lock
109
int connection_status = ConnectionTester.CONNECTION_IS_OKAY;
110
111     /*
112      * contains all unclosed Statements not managed by a StatementCache
113      * associated with the physical connection
114      *
115      * MT: protected by its own lock, not reassigned
116      */

117     final Set uncachedActiveStatements = Collections.synchronizedSet( new HashSet() );
118
119     //MT: Thread-safe, assigned
120
volatile GooGooStatementCache scache;
121     volatile boolean isolation_lvl_nondefault = false;
122     volatile boolean catalog_nondefault = false;
123     volatile boolean holdability_nondefault = false;
124
125     public C3P0PooledConnection(Connection con,
126                 ConnectionTester connectionTester,
127                 boolean autoCommitOnClose,
128                 boolean forceIgnoreUnresolvedTransactions,
129                 ConnectionCustomizer cc,
130                 String JavaDoc pdsIdt) throws SQLException
131     {
132     try
133         {
134         if (cc != null)
135             cc.onAcquire( con, pdsIdt );
136         }
137     catch (Exception JavaDoc e)
138         { throw SqlUtils.toSQLException(e); }
139
140     this.physicalConnection = con;
141     this.connectionTester = connectionTester;
142     this.autoCommitOnClose = autoCommitOnClose;
143     this.forceIgnoreUnresolvedTransactions = forceIgnoreUnresolvedTransactions;
144     this.supports_setTypeMap = C3P0ImplUtils.supportsMethod(con, "setTypeMap", new Class JavaDoc[]{ Map.class });
145     this.supports_setHoldability = C3P0ImplUtils.supportsMethod(con, "setHoldability", new Class JavaDoc[]{ Integer JavaDoc.class });
146     this.dflt_txn_isolation = con.getTransactionIsolation();
147     this.dflt_catalog = con.getCatalog();
148     this.dflt_holdability = (supports_setHoldability ? con.getHoldability() : ResultSet.CLOSE_CURSORS_AT_COMMIT);
149     }
150
151     //used by C3P0PooledConnectionPool
152
Connection getPhysicalConnection()
153     { return physicalConnection; }
154
155     boolean isClosed() throws SQLException
156     { return (physicalConnection == null); }
157
158     void initStatementCache( GooGooStatementCache scache )
159     { this.scache = scache; }
160
161
162     //DEBUG
163
//Exception origGet = null;
164

165     // synchronized to protect exposedProxy
166
public synchronized Connection getConnection()
167     throws SQLException
168     {
169     if ( exposedProxy != null)
170         {
171         //DEBUG
172
//System.err.println("[DOUBLE_GET_TESTER] -- double getting a Connection from " + this );
173
//new Exception("[DOUBLE_GET_TESTER] -- Double-Get Stack Trace").printStackTrace();
174
//origGet.printStackTrace();
175

176 // System.err.println("c3p0 -- Uh oh... getConnection() was called on a PooledConnection when " +
177
// "it had already provided a client with a Connection that has not yet been " +
178
// "closed. This probably indicates a bug in the connection pool!!!");
179

180         logger.warning("c3p0 -- Uh oh... getConnection() was called on a PooledConnection when " +
181                    "it had already provided a client with a Connection that has not yet been " +
182                    "closed. This probably indicates a bug in the connection pool!!!");
183
184         return exposedProxy;
185         }
186     else
187         { return getCreateNewConnection(); }
188     }
189
190     // must be called from sync'ed method to protecte
191
// exposedProxy
192
private Connection getCreateNewConnection()
193     throws SQLException
194     {
195     try
196         {
197         //DEBUG
198
//origGet = new Exception("[DOUBLE_GET_TESTER] -- Orig Get");
199

200         ensureOkay();
201         /*
202          * we reset the physical connection when we close an exposed proxy
203          * no need to do it again when we create one
204          */

205         //reset();
206
return (exposedProxy = createProxyConnection());
207         }
208     catch (SQLException e)
209         { throw e; }
210     catch (Exception JavaDoc e)
211         {
212         //e.printStackTrace();
213
logger.log(MLevel.WARNING, "Failed to acquire connection!", e);
214         throw new SQLException("Failed to acquire connection!");
215         }
216     }
217
218     public void closeAll() throws SQLException
219     {
220     if (scache != null)
221         scache.closeAll( physicalConnection );
222     }
223
224     public void close() throws SQLException
225     { this.close( false ); }
226
227     //TODO: factor out repetitive debugging code
228
private synchronized void close(boolean known_invalid) throws SQLException
229     {
230     //System.err.println("Closing " + this);
231
if ( physicalConnection != null )
232         {
233         try
234             {
235             StringBuffer JavaDoc debugOnlyLog = null;
236             if ( Debug.DEBUG && known_invalid )
237                 {
238                 debugOnlyLog = new StringBuffer JavaDoc();
239                 debugOnlyLog.append("[ exceptions: ");
240                 }
241
242             Exception JavaDoc exc = cleanupUncachedActiveStatements();
243             if (Debug.DEBUG && exc != null)
244                 {
245                 if (known_invalid)
246                     debugOnlyLog.append( exc.toString() + ' ' );
247                 else
248                     logger.log(MLevel.WARNING, "An exception occurred while cleaning up uncached active Statements.", exc);
249                     //exc.printStackTrace();
250
}
251
252             try
253                 {
254                 // we've got to use silentClose() rather than close() here,
255
// 'cuz if there's still an exposedProxy (say a user forgot to
256
// close his Connection) before we close, and we use regular (loud)
257
// close, we will try to check this dead or dying PooledConnection
258
// back into the pool. We only want to do this when close is called
259
// on user proxies, and the underlying PooledConnection might still
260
// be good. The PooledConnection itself should only be closed by the
261
// pool.
262
if (exposedProxy != null)
263                     exposedProxy.silentClose( known_invalid );
264                 }
265             catch (Exception JavaDoc e)
266                 {
267                 if (Debug.DEBUG)
268                     {
269                     if (known_invalid)
270                         debugOnlyLog.append( e.toString() + ' ' );
271                     else
272                         logger.log(MLevel.WARNING, "An exception occurred.", exc);
273                         //e.printStackTrace();
274
}
275                 exc = e;
276                 }
277             try
278                 { this.closeAll(); }
279             catch (Exception JavaDoc e)
280                 {
281                 if (Debug.DEBUG)
282                     {
283                     if (known_invalid)
284                         debugOnlyLog.append( e.toString() + ' ' );
285                     else
286                         logger.log(MLevel.WARNING, "An exception occurred.", exc);
287                         //e.printStackTrace();
288
}
289                 exc = e;
290                 }
291             
292             try { physicalConnection.close(); }
293             catch (Exception JavaDoc e)
294                 {
295                 if (Debug.DEBUG)
296                     {
297                     if (known_invalid)
298                         debugOnlyLog.append( e.toString() + ' ' );
299                     else
300                         logger.log(MLevel.WARNING, "An exception occurred.", exc);
301                         e.printStackTrace();
302                     }
303                 exc = e;
304                 }
305
306             if (exc != null)
307                 {
308                 if (known_invalid)
309                     {
310                     debugOnlyLog.append(" ]");
311                     if (Debug.DEBUG)
312                         {
313 // System.err.print("[DEBUG]" + this + ": while closing a PooledConnection known to be invalid, ");
314
// System.err.println(" some exceptions occurred. This is probably not a problem:");
315
// System.err.println( debugOnlyLog.toString() );
316

317
318                         logger.fine(this + ": while closing a PooledConnection known to be invalid, " +
319                                 " some exceptions occurred. This is probably not a problem: " +
320                                 debugOnlyLog.toString() );
321                         }
322                     }
323                 else
324                     throw new SQLException("At least one error occurred while attempting " +
325                                "to close() the PooledConnection: " + exc);
326                 }
327             if (Debug.TRACE == Debug.TRACE_MAX)
328                 logger.fine("C3P0PooledConnection closed. [" + this + ']');
329                 //System.err.println("C3P0PooledConnection closed. [" + this + ']');
330
}
331         finally
332             { physicalConnection = null; }
333         }
334     }
335
336     public void addConnectionEventListener(ConnectionEventListener listener)
337     { ces.addConnectionEventListener( listener ); }
338
339     public void removeConnectionEventListener(ConnectionEventListener listener)
340     { ces.removeConnectionEventListener( listener ); }
341
342     private void reset() throws SQLException
343     { reset( false ); }
344
345     private void reset( boolean known_resolved_txn ) throws SQLException
346     {
347     ensureOkay();
348     C3P0ImplUtils.resetTxnState( physicalConnection, forceIgnoreUnresolvedTransactions, autoCommitOnClose, known_resolved_txn );
349     if (isolation_lvl_nondefault)
350         {
351         physicalConnection.setTransactionIsolation( dflt_txn_isolation );
352         isolation_lvl_nondefault = false;
353         }
354     if (catalog_nondefault)
355         {
356         physicalConnection.setCatalog( dflt_catalog );
357         catalog_nondefault = false;
358         }
359     if (holdability_nondefault) //we don't test if holdability is supported, 'cuz it can never go nondefault if it's not.
360
{
361         physicalConnection.setHoldability( dflt_holdability );
362         holdability_nondefault = false;
363         }
364
365     try
366         { physicalConnection.setReadOnly( false ); }
367     catch ( Throwable JavaDoc t )
368         {
369         if (logger.isLoggable( MLevel.FINE ))
370             logger.log(MLevel.FINE, "A Throwable occurred while trying to reset the readOnly property of our Connection to false!", t);
371         }
372
373     try
374         { if (supports_setTypeMap) physicalConnection.setTypeMap( Collections.EMPTY_MAP ); }
375     catch ( Throwable JavaDoc t )
376         {
377         if (logger.isLoggable( MLevel.FINE ))
378             logger.log(MLevel.FINE, "A Throwable occurred while trying to reset the typeMap property of our Connection to Collections.EMPTY_MAP!", t);
379         }
380     }
381
382     boolean closeAndRemoveResultSets(Set rsSet)
383     {
384     boolean okay = true;
385     synchronized (rsSet)
386         {
387         for (Iterator ii = rsSet.iterator(); ii.hasNext(); )
388             {
389             ResultSet rs = (ResultSet) ii.next();
390             try
391                 { rs.close(); }
392             catch (SQLException e)
393                 {
394                 if (Debug.DEBUG)
395                     logger.log(MLevel.WARNING, "An exception occurred while cleaning up a ResultSet.", e);
396                     //e.printStackTrace();
397
okay = false;
398                 }
399             finally
400                 { ii.remove(); }
401             }
402         }
403     return okay;
404     }
405
406     void ensureOkay() throws SQLException
407     {
408     if (physicalConnection == null)
409         throw new SQLException( invalidatingException == null ?
410                     "Connection is closed or broken." :
411                     "Connection is broken. Invalidating Exception: " + invalidatingException.toString());
412     }
413
414     boolean closeAndRemoveResourcesInSet(Set s, Method closeMethod)
415     {
416     boolean okay = true;
417     
418     Set temp;
419     synchronized (s)
420         { temp = new HashSet( s ); }
421
422     for (Iterator ii = temp.iterator(); ii.hasNext(); )
423         {
424         Object JavaDoc rsrc = ii.next();
425         try
426             { closeMethod.invoke(rsrc, CLOSE_ARGS); }
427         catch (Exception JavaDoc e)
428             {
429             Throwable JavaDoc t = e;
430             if (t instanceof InvocationTargetException)
431                 t = ((InvocationTargetException) e).getTargetException();
432             logger.log(MLevel.WARNING, "An exception occurred while cleaning up a resource.", t);
433             //t.printStackTrace();
434
okay = false;
435             }
436         finally
437             { s.remove( rsrc ); }
438         }
439
440     // We had to abandon the idea of simply iterating over s directly, because
441
// our resource close methods sometimes try to remove the resource from
442
// its parent Set. This is important (when the user closes the resources
443
// directly), but leads to ConcurrenModificationExceptions while we are
444
// iterating over the Set to close. So, now we iterate over a copy, but remove
445
// from the original Set. Since removal is idempotent, it don't matter if
446
// the close method already provoked a remove. Sucks that we have to copy
447
// the set though.
448
//
449
// Original (direct iteration) version:
450
//
451
// synchronized (s)
452
// {
453
// for (Iterator ii = s.iterator(); ii.hasNext(); )
454
// {
455
// Object rsrc = ii.next();
456
// try
457
// { closeMethod.invoke(rsrc, CLOSE_ARGS); }
458
// catch (Exception e)
459
// {
460
// Throwable t = e;
461
// if (t instanceof InvocationTargetException)
462
// t = ((InvocationTargetException) e).getTargetException();
463
// t.printStackTrace();
464
// okay = false;
465
// }
466
// finally
467
// { ii.remove(); }
468
// }
469
// }
470

471
472     return okay;
473     }
474
475     private SQLException cleanupUncachedActiveStatements()
476     {
477     //System.err.println("IN uncachedActiveStatements.size(): " + uncachedActiveStatements.size());
478

479     boolean okay = closeAndRemoveResourcesInSet(uncachedActiveStatements, STMT_CLOSE_METHOD);
480
481     //System.err.println("OUT uncachedActiveStatements.size(): " + uncachedActiveStatements.size());
482

483     if (okay)
484         return null;
485     else
486         return new SQLException("An exception occurred while trying to " +
487                     "clean up orphaned resources.");
488     }
489
490     ProxyConnection createProxyConnection() throws Exception JavaDoc
491     {
492     // we should always have a separate handler for each proxy connection, so
493
// that object methods behave as expected... the handler covers
494
// all object methods on behalf of the proxy.
495
InvocationHandler handler = new ProxyConnectionInvocationHandler();
496     return (ProxyConnection) CON_PROXY_CTOR.newInstance( new Object JavaDoc[] {handler} );
497     }
498
499     Statement createProxyStatement( Statement innerStmt ) throws Exception JavaDoc
500     { return this.createProxyStatement( false, innerStmt ); }
501
502
503     private static class StatementProxyingSetManagedResultSet extends SetManagedResultSet
504     {
505     private Statement proxyStatement;
506
507     StatementProxyingSetManagedResultSet(Set activeResultSets)
508     { super( activeResultSets ); }
509
510     public void setProxyStatement( Statement proxyStatement )
511     { this.proxyStatement = proxyStatement; }
512
513     public Statement getStatement() throws SQLException
514     { return (proxyStatement == null ? super.getStatement() : proxyStatement); }
515     }
516
517     /*
518      * TODO: factor all this convolution out into
519      * C3P0Statement
520      */

521     Statement createProxyStatement( //final Method cachedStmtProducingMethod,
522
//final Object[] cachedStmtProducingMethodArgs,
523
final boolean inner_is_cached,
524                    final Statement innerStmt) throws Exception JavaDoc
525     {
526     final Set activeResultSets = Collections.synchronizedSet( new HashSet() );
527     final Connection parentConnection = exposedProxy;
528
529     if (Debug.DEBUG && parentConnection == null)
530         {
531 // System.err.print("PROBABLE C3P0 BUG -- ");
532
// System.err.println(this + ": created a proxy Statement when there is no active, exposed proxy Connection???");
533

534         logger.warning("PROBABLE C3P0 BUG -- " +
535                    this + ": created a proxy Statement when there is no active, exposed proxy Connection???");
536
537         }
538
539
540     //we can use this one wrapper under all circumstances
541
//except jdbc3 CallableStatement multiple open ResultSets...
542
//avoid object allocation in statement methods where possible.
543
final StatementProxyingSetManagedResultSet mainResultSet = new StatementProxyingSetManagedResultSet( activeResultSets );
544
545     class WrapperStatementHelper
546     {
547         Statement wrapperStmt;
548         Statement nakedInner;
549
550         public WrapperStatementHelper(Statement wrapperStmt, Statement nakedInner)
551         {
552         this.wrapperStmt = wrapperStmt;
553         this.nakedInner = nakedInner;
554
555         if (! inner_is_cached)
556             uncachedActiveStatements.add( wrapperStmt );
557         }
558
559         private boolean closeAndRemoveActiveResultSets()
560         { return closeAndRemoveResultSets( activeResultSets ); }
561
562         public ResultSet wrap(ResultSet rs)
563         {
564         if (mainResultSet.getInner() == null)
565             {
566             mainResultSet.setInner(rs);
567             mainResultSet.setProxyStatement( wrapperStmt );
568             return mainResultSet;
569             }
570         else
571             {
572             //for the case of multiple open ResultSets
573
StatementProxyingSetManagedResultSet out
574                 = new StatementProxyingSetManagedResultSet( activeResultSets );
575             out.setInner( rs );
576             out.setProxyStatement( wrapperStmt );
577             return out;
578             }
579         }
580
581         public void doClose()
582         throws SQLException
583         {
584         boolean okay = closeAndRemoveActiveResultSets();
585
586         if (inner_is_cached) //this statement was cached
587
scache.checkinStatement( innerStmt );
588         else
589             {
590             innerStmt.close();
591             uncachedActiveStatements.remove( wrapperStmt );
592             }
593
594         if (!okay)
595             throw new SQLException("Failed to close an orphaned ResultSet properly.");
596         }
597
598         public Object JavaDoc doRawStatementOperation(Method m, Object JavaDoc target, Object JavaDoc[] args)
599         throws IllegalAccessException JavaDoc, InvocationTargetException, SQLException
600         {
601         if (target == C3P0ProxyStatement.RAW_STATEMENT)
602             target = nakedInner;
603         for (int i = 0, len = args.length; i < len; ++i)
604             if (args[i] == C3P0ProxyStatement.RAW_STATEMENT)
605             args[i] = nakedInner;
606         
607         Object JavaDoc out = m.invoke(target, args);
608         
609         if (out instanceof ResultSet)
610             out = wrap( (ResultSet) out );
611
612         return out;
613         }
614     }
615
616     if (innerStmt instanceof CallableStatement)
617         {
618         class ProxyCallableStatement extends FilterCallableStatement implements C3P0ProxyStatement
619             {
620             WrapperStatementHelper wsh;
621
622             ProxyCallableStatement(CallableStatement is)
623             {
624                 super( is );
625                 this.wsh = new WrapperStatementHelper(this, is);
626             }
627
628             public Connection getConnection()
629             { return parentConnection; }
630
631             public ResultSet getResultSet() throws SQLException
632             { return wsh.wrap( super.getResultSet() ); }
633             
634             public ResultSet getGeneratedKeys() throws SQLException
635             { return wsh.wrap( super.getGeneratedKeys() ); }
636             
637             public ResultSet executeQuery(String JavaDoc sql) throws SQLException
638             { return wsh.wrap( super.executeQuery(sql) ); }
639             
640             public ResultSet executeQuery() throws SQLException
641             { return wsh.wrap( super.executeQuery() ); }
642             
643             public Object JavaDoc rawStatementOperation(Method m, Object JavaDoc target, Object JavaDoc[] args)
644                 throws IllegalAccessException JavaDoc, InvocationTargetException, SQLException
645             { return wsh.doRawStatementOperation( m, target, args); }
646
647             public void close() throws SQLException
648             { wsh.doClose(); }
649             }
650
651         return new ProxyCallableStatement((CallableStatement) innerStmt );
652         }
653     else if (innerStmt instanceof PreparedStatement)
654         {
655         class ProxyPreparedStatement extends FilterPreparedStatement implements C3P0ProxyStatement
656             {
657             WrapperStatementHelper wsh;
658
659             ProxyPreparedStatement(PreparedStatement ps)
660             {
661                 super( ps );
662                 this.wsh = new WrapperStatementHelper(this, ps);
663             }
664
665             public Connection getConnection()
666             { return parentConnection; }
667
668             public ResultSet getResultSet() throws SQLException
669             { return wsh.wrap( super.getResultSet() ); }
670             
671             public ResultSet getGeneratedKeys() throws SQLException
672             { return wsh.wrap( super.getGeneratedKeys() ); }
673             
674             public ResultSet executeQuery(String JavaDoc sql) throws SQLException
675             { return wsh.wrap( super.executeQuery(sql) ); }
676             
677             public ResultSet executeQuery() throws SQLException
678             { return wsh.wrap( super.executeQuery() ); }
679             
680             public Object JavaDoc rawStatementOperation(Method m, Object JavaDoc target, Object JavaDoc[] args)
681                 throws IllegalAccessException JavaDoc, InvocationTargetException, SQLException
682             { return wsh.doRawStatementOperation( m, target, args); }
683
684             public void close() throws SQLException
685             { wsh.doClose(); }
686             }
687
688         return new ProxyPreparedStatement((PreparedStatement) innerStmt );
689         }
690     else
691         {
692         class ProxyStatement extends FilterStatement implements C3P0ProxyStatement
693             {
694             WrapperStatementHelper wsh;
695
696             ProxyStatement(Statement s)
697             {
698                 super( s );
699                 this.wsh = new WrapperStatementHelper(this, s);
700             }
701
702             public Connection getConnection()
703             { return parentConnection; }
704
705             public ResultSet getResultSet() throws SQLException
706             { return wsh.wrap( super.getResultSet() ); }
707             
708             public ResultSet getGeneratedKeys() throws SQLException
709             { return wsh.wrap( super.getGeneratedKeys() ); }
710             
711             public ResultSet executeQuery(String JavaDoc sql) throws SQLException
712             { return wsh.wrap( super.executeQuery(sql) ); }
713             
714             public Object JavaDoc rawStatementOperation(Method m, Object JavaDoc target, Object JavaDoc[] args)
715                 throws IllegalAccessException JavaDoc, InvocationTargetException, SQLException
716             { return wsh.doRawStatementOperation( m, target, args); }
717
718             public void close() throws SQLException
719             { wsh.doClose(); }
720             }
721
722         return new ProxyStatement( innerStmt );
723         }
724     }
725
726     final class ProxyConnectionInvocationHandler implements InvocationHandler
727     {
728     //MT: ThreadSafe, but reassigned -- protected by this' lock
729
Connection activeConnection = physicalConnection;
730     DatabaseMetaData metaData = null;
731     boolean connection_error_signaled = false;
732         
733     /*
734      * contains all unclosed ResultSets derived from this Connection's metadata
735      * associated with the physical connection
736      *
737      * MT: protected by this' lock
738      */

739     final Set activeMetaDataResultSets = new HashSet();
740         
741     //being careful with doRawConnectionOperation
742
//we initialize lazily, because this will be very rarely used
743
Set doRawResultSets = null;
744
745     boolean txn_known_resolved = true;
746     
747     public String JavaDoc toString()
748     { return "C3P0ProxyConnection [Invocation Handler: " + super.toString() + ']'; }
749         
750     private Object JavaDoc doRawConnectionOperation(Method m, Object JavaDoc target, Object JavaDoc[] args)
751         throws IllegalAccessException JavaDoc, InvocationTargetException, SQLException, Exception JavaDoc
752     {
753         if (activeConnection == null)
754         throw new SQLException("Connection previously closed. You cannot operate on a closed Connection.");
755                 
756         if (target == C3P0ProxyConnection.RAW_CONNECTION)
757         target = activeConnection;
758         for (int i = 0, len = args.length; i < len; ++i)
759         if (args[i] == C3P0ProxyConnection.RAW_CONNECTION)
760             args[i] = activeConnection;
761             
762         Object JavaDoc out = m.invoke(target, args);
763             
764         // we never cache Statements generated by an operation on the raw Connection
765
if (out instanceof Statement)
766         out = createProxyStatement( false, (Statement) out );
767         else if (out instanceof ResultSet)
768         {
769             if (doRawResultSets == null)
770             doRawResultSets = new HashSet();
771             out = new NullStatementSetManagedResultSet( (ResultSet) out, doRawResultSets );
772         }
773         return out;
774     }
775         
776     public synchronized Object JavaDoc invoke(Object JavaDoc proxy, Method m, Object JavaDoc[] args)
777         throws Throwable JavaDoc
778     {
779         if ( OBJECT_METHODS.contains( m ) )
780         return m.invoke( this, args );
781         
782         try
783         {
784             String JavaDoc mname = m.getName();
785             if (activeConnection != null)
786             {
787                 if (mname.equals("rawConnectionOperation"))
788                 {
789                     ensureOkay();
790                     txn_known_resolved = false;
791
792                     return doRawConnectionOperation((Method) args[0], args[1], (Object JavaDoc[]) args[2]);
793                 }
794                 else if (mname.equals("setTransactionIsolation"))
795                 {
796                     ensureOkay();
797
798                     //don't modify txn_known_resolved
799

800                     m.invoke( activeConnection, args );
801
802                     int lvl = ((Integer JavaDoc) args[0]).intValue();
803                     isolation_lvl_nondefault = (lvl != dflt_txn_isolation);
804
805                     //System.err.println("updated txn isolation to " + lvl + ", nondefault level? " + isolation_lvl_nondefault);
806

807                     return null;
808                 }
809                 else if (mname.equals("setCatalog"))
810                 {
811                     ensureOkay();
812
813                     //don't modify txn_known_resolved
814

815                     m.invoke( activeConnection, args );
816
817                     String JavaDoc catalog = (String JavaDoc) args[0];
818                     catalog_nondefault = ObjectUtils.eqOrBothNull(catalog, dflt_catalog);
819
820                     return null;
821                 }
822                 else if (mname.equals("setHoldability"))
823                 {
824                     ensureOkay();
825
826                     //don't modify txn_known_resolved
827

828                     m.invoke( activeConnection, args ); //will throw an exception if setHoldability() not supported...
829

830                     int holdability = ((Integer JavaDoc) args[0]).intValue();
831                     holdability_nondefault = (holdability != dflt_holdability);
832
833                     return null;
834                 }
835                 else if (mname.equals("createStatement"))
836                 {
837                     ensureOkay();
838                     txn_known_resolved = false;
839     
840                     Object JavaDoc stmt = m.invoke( activeConnection, args );
841                     return createProxyStatement( (Statement) stmt );
842                 }
843                 else if (mname.equals("prepareStatement"))
844                 {
845                     ensureOkay();
846                     txn_known_resolved = false;
847     
848                     Object JavaDoc pstmt;
849                     if (scache == null)
850                     {
851                         pstmt = m.invoke( activeConnection, args );
852                         return createProxyStatement( (Statement) pstmt );
853                     }
854                     else
855                     {
856                         pstmt = scache.checkoutStatement( physicalConnection,
857                                           m,
858                                           args );
859                         return createProxyStatement( true,
860                                      (Statement) pstmt );
861                     }
862                 }
863                 else if (mname.equals("prepareCall"))
864                 {
865                     ensureOkay();
866                     txn_known_resolved = false;
867     
868                     Object JavaDoc cstmt;
869                     if (scache == null)
870                     {
871                         cstmt = m.invoke( activeConnection, args );
872                         return createProxyStatement( (Statement) cstmt );
873                     }
874                     else
875                     {
876                         cstmt = scache.checkoutStatement( physicalConnection, m, args );
877                         return createProxyStatement( true,
878                                      (Statement) cstmt );
879                     }
880                 }
881                 else if (mname.equals("getMetaData"))
882                 {
883                     ensureOkay();
884                     txn_known_resolved = false; //views of tables etc. might be txn dependent
885

886                     DatabaseMetaData innerMd = activeConnection.getMetaData();
887                     if (metaData == null)
888                     {
889                         // exposedProxy is protected by C3P0PooledConnection.this' lock
890
synchronized (C3P0PooledConnection.this)
891                         { metaData = new SetManagedDatabaseMetaData(innerMd, activeMetaDataResultSets, exposedProxy); }
892                     }
893                     return metaData;
894                 }
895                 else if (mname.equals("silentClose"))
896                 {
897                     //the PooledConnection doesn't have to be okay
898

899                     doSilentClose( proxy, ((Boolean JavaDoc) args[0]).booleanValue(), this.txn_known_resolved );
900                     return null;
901                 }
902                 else if ( mname.equals("close") )
903                 {
904                     //the PooledConnection doesn't have to be okay
905

906                     Exception JavaDoc e = doSilentClose( proxy, false, this.txn_known_resolved );
907                     if (! connection_error_signaled)
908                     ces.fireConnectionClosed();
909                     //System.err.println("close() called on a ProxyConnection.");
910
if (e != null)
911                     {
912                         // System.err.print("user close exception -- ");
913
// e.printStackTrace();
914
throw e;
915                     }
916                     else
917                     return null;
918                 }
919 // else if ( mname.equals("finalize") ) //REMOVE THIS CASE -- TMP DEBUG
920
// {
921
// System.err.println("Connection apparently finalized!");
922
// return m.invoke( activeConnection, args );
923
// }
924
else
925                 {
926                     ensureOkay();
927                         
928
929                     // we've disabled setting txn_known_resolved to true, ever, because
930
// we failed to deal with the case that clients would work with previously
931
// acquired Statements and ResultSets after a commit(), rollback(), or setAutoCommit().
932
// the new non-reflective proxies have been modified to deal with this case.
933
// here, with soon-to-be-deprecated in "traditional reflective proxies mode"
934
// we are reverting to the conservative, always-presume-you-have-to-rollback
935
// policy.
936

937                     //txn_known_resolved = ( mname.equals("commit") || mname.equals( "rollback" ) || mname.equals( "setAutoCommit" ) );
938
txn_known_resolved = false;
939
940                     return m.invoke( activeConnection, args );
941                 }
942             }
943             else
944             {
945                 if (mname.equals("close") ||
946                 mname.equals("silentClose"))
947                 return null;
948                 else if (mname.equals("isClosed"))
949                 return new Boolean JavaDoc(true);
950                 else
951                 {
952                     throw new SQLException("You can't operate on " +
953                                "a closed connection!!!");
954                 }
955             }
956         }
957         catch (InvocationTargetException e)
958         {
959             Throwable JavaDoc convertMe = e.getTargetException();
960             SQLException sqle = handleMaybeFatalToPooledConnection( convertMe, proxy, false );
961             sqle.fillInStackTrace();
962             throw sqle;
963         }
964     }
965     
966     private Exception JavaDoc doSilentClose(Object JavaDoc proxyConnection, boolean pooled_connection_is_dead)
967     { return doSilentClose( proxyConnection, pooled_connection_is_dead, false ); }
968
969     private Exception JavaDoc doSilentClose(Object JavaDoc proxyConnection, boolean pooled_connection_is_dead, boolean known_resolved_txn)
970     {
971         if ( activeConnection != null )
972         {
973             synchronized ( C3P0PooledConnection.this ) //uh oh... this is a nested lock acq... is there a deadlock hazard here?
974
{
975                 if ( C3P0PooledConnection.this.exposedProxy == proxyConnection )
976                 {
977                     C3P0PooledConnection.this.exposedProxy = null;
978                     //System.err.println("Reset exposed proxy.");
979

980                     //DEBUG
981
//origGet = null;
982
}
983                 else //else case -- DEBUG only
984
logger.warning("(c3p0 issue) doSilentClose( ... ) called on a proxyConnection " +
985                            "other than the current exposed proxy for its PooledConnection. [exposedProxy: " +
986                            exposedProxy + ", proxyConnection: " + proxyConnection);
987 // System.err.println("[DEBUG] WARNING: doSilentClose( ... ) called on a proxyConnection " +
988
// "other than the current exposed proxy for its PooledConnection. [exposedProxy: " +
989
// exposedProxy + ", proxyConnection: " + proxyConnection);
990
}
991                 
992             Exception JavaDoc out = null;
993                 
994             Exception JavaDoc exc1 = null, exc2 = null, exc3 = null, exc4 = null;
995             try
996             {
997                 if (! pooled_connection_is_dead)
998                 C3P0PooledConnection.this.reset(known_resolved_txn);
999             }
1000            catch (Exception JavaDoc e)
1001            {
1002                exc1 = e;
1003                // if (Debug.DEBUG)
1004
// {
1005
// System.err.print("exc1 -- ");
1006
// exc1.printStackTrace();
1007
// }
1008
}
1009                
1010            exc2 = cleanupUncachedActiveStatements();
1011            // if (Debug.DEBUG && exc2 != null)
1012
// {
1013
// System.err.print("exc2 -- ");
1014
// exc2.printStackTrace();
1015
// }
1016
String JavaDoc errSource;
1017            if (doRawResultSets != null)
1018            {
1019                activeMetaDataResultSets.addAll( doRawResultSets );
1020                errSource = "DataBaseMetaData or raw Connection operation";
1021            }
1022            else
1023            errSource = "DataBaseMetaData";
1024
1025            if (!closeAndRemoveResultSets( activeMetaDataResultSets ))
1026            exc3 = new SQLException("Failed to close some " + errSource + " Result Sets.");
1027            // if (Debug.DEBUG && exc3 != null)
1028
// {
1029
// System.err.print("exc3 -- ");
1030
// exc3.printStackTrace();
1031
// }
1032
if (scache != null)
1033            {
1034                try
1035                { scache.checkinAll( physicalConnection ); }
1036                catch ( Exception JavaDoc e )
1037                { exc4 = e; }
1038                // if (Debug.DEBUG && exc4 != null)
1039
// {
1040
// System.err.print("exc4 -- ");
1041
// exc4.printStackTrace();
1042
// }
1043
}
1044                
1045            if (exc1 != null)
1046            {
1047                handleMaybeFatalToPooledConnection( exc1, proxyConnection, true );
1048                out = exc1;
1049            }
1050            else if (exc2 != null)
1051            {
1052                handleMaybeFatalToPooledConnection( exc2, proxyConnection, true );
1053                out = exc2;
1054            }
1055            else if (exc3 != null)
1056            {
1057                handleMaybeFatalToPooledConnection( exc3, proxyConnection, true );
1058                out = exc3;
1059            }
1060            else if (exc4 != null)
1061            {
1062                handleMaybeFatalToPooledConnection( exc4, proxyConnection, true );
1063                out = exc4;
1064            }
1065                
1066            // if (out != null)
1067
// {
1068
// System.err.print("out -- ");
1069
// out.printStackTrace();
1070
// }
1071

1072            activeConnection = null;
1073            return out;
1074        }
1075        else
1076        return null;
1077    }
1078    
1079    private SQLException handleMaybeFatalToPooledConnection( Throwable JavaDoc t, Object JavaDoc proxyConnection, boolean already_closed )
1080    {
1081        //System.err.println("handleMaybeFatalToPooledConnection()");
1082

1083        SQLException sqle = SqlUtils.toSQLException( t );
1084        int status = connectionTester.statusOnException( physicalConnection, sqle );
1085        updateConnectionStatus( status );
1086        if (status != ConnectionTester.CONNECTION_IS_OKAY)
1087        {
1088            if (Debug.DEBUG)
1089            {
1090// System.err.print(C3P0PooledConnection.this + " will no longer be pooled because it has been " +
1091
// "marked invalid by the following Exception: ");
1092
// t.printStackTrace();
1093

1094                logger.log(MLevel.INFO,
1095                       C3P0PooledConnection.this + " will no longer be pooled because it has been marked invalid by an Exception.",
1096                       t );
1097            }
1098                
1099            invalidatingException = sqle;
1100
1101            /*
1102              ------
1103              A users have complained that SQLExceptions ought not close their Connections underneath
1104              them under any circumstance. Signalling the Connection error after updating the Connection
1105              status should be sufficient from the pool's perspective, because the PooledConnection
1106              will be marked broken by the pool and will be destroyed on checkin. I think actually
1107              close()ing the Connection when it appears to be broken rather than waiting for users
1108              to close() it themselves is overly aggressive, so I'm commenting the old behavior out.
1109              The only potential downside to this approach is that users who do not close() in a finally
1110              clause properly might see their close()es skipped by exceptions that previously would
1111              have led to automatic close(). But relying on the automatic close() was never reliable
1112              (since it only ever happened when c3p0 determined a Connection to be absolutely broken),
1113              and is generally speaking a client error that c3p0 ought not be responsible for dealing
1114              with. I think it's right to leave this out. -- swaldman 2004-12-09
1115              ------
1116              
1117              if (! already_closed )
1118                  doSilentClose( proxyConnection, true );
1119            */

1120
1121            if (! connection_error_signaled)
1122            {
1123                ces.fireConnectionErrorOccurred( sqle );
1124                connection_error_signaled = true;
1125            }
1126        }
1127        return sqle;
1128    }
1129    
1130
1131    }
1132
1133    interface ProxyConnection extends C3P0ProxyConnection
1134    { void silentClose( boolean known_invalid ) throws SQLException; }
1135
1136    public synchronized int getConnectionStatus()
1137    { return this.connection_status; }
1138
1139    private synchronized void updateConnectionStatus(int status)
1140    {
1141    switch ( this.connection_status )
1142        {
1143        case ConnectionTester.DATABASE_IS_INVALID:
1144        //can't get worse than this, do nothing.
1145
break;
1146        case ConnectionTester.CONNECTION_IS_INVALID:
1147        if (status == ConnectionTester.DATABASE_IS_INVALID)
1148            doBadUpdate(status);
1149        break;
1150        case ConnectionTester.CONNECTION_IS_OKAY:
1151        if (status != ConnectionTester.CONNECTION_IS_OKAY)
1152            doBadUpdate(status);
1153        break;
1154        default:
1155        throw new InternalError JavaDoc(this + " -- Illegal Connection Status: " + this.connection_status);
1156        }
1157    }
1158
1159    //must be called from sync'ed method
1160
private void doBadUpdate(int new_status)
1161    {
1162    this.connection_status = new_status;
1163    try { this.close( true ); }
1164    catch (SQLException e)
1165        {
1166// System.err.print("Broken Connection Close Error: ");
1167
// e.printStackTrace();
1168

1169        logger.log(MLevel.WARNING, "Broken Connection Close Error. ", e);
1170        }
1171    }
1172}
1173
1174
1175
1176
1177
1178
1179
1180
Popular Tags