commons-issues mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Phil Steitz (JIRA)" <>
Subject [jira] [Commented] (DBCP-414) PoolablePreparedStatement can get closed while it's being used
Date Tue, 15 Apr 2014 20:46:22 GMT


Phil Steitz commented on DBCP-414:

See comments on the duplicate issue, DBCP-415

> PoolablePreparedStatement can get closed while it's being used
> --------------------------------------------------------------
>                 Key: DBCP-414
>                 URL:
>             Project: Commons Dbcp
>          Issue Type: Bug
>    Affects Versions: 2.0
>         Environment: DBCP 2.0
>            Reporter: Pasi Eronen
> I've found a case where a PoolablePreparedStatement that's currently in active use can
get closed (resulting in "PoolablePreparedStatement with address: ... is closed" SQLException
to the application), from a finalizer of another DelegatingPreparedStatement (that's obviously
not in use, since it's being garbage collected). 
> Details:
> 0. Starting point: application has a Connection (it's a PoolGuardConnectionWrapper wrapping
PoolableConnection wrapping PoolingConnection wrapping com.mysql.jdbc.JDBC4Connection), statement
pooling is enabled, and prepareStatement has never been called
> 1. Application calls conn.prepareStatement. The statement pool is empty, so this ends
up in PoolingConnection.makeObject, which then calls MySQL to get a PreparedStatement (stmt1),
and wraps it in PoolablePreparedStatement (stmt2). PoolableConnection.prepareStatement wraps
it in new DelegatingPreparedStatement (stmt3), and PoolGuardConnectionWrapper wraps it again
> 2. Application does something with stmt4, and eventually calls stmt4.close(). The call
ends up in stmt2.close (in PoolablePreparedStatement) which returns stmt2 to the statement
pool. Stmt2.passivate() gets called, setting the _closed flag to true.
> 3. Application forgets all about stmt4
> 4. Later, application calls conn.prepareStatement (with same SQL) again. The result is
the old MySQL PreparedStatement (stmt1) wrapped in the old PoolablePreparedStatement (stmt2)
wrapped in new DelegatingPreparedStatement stmt5 wrapped in new DelegatingPreparedStatement
stmt6.  Stmt2.activate() gets called, setting the _closed flag to false.
> 5. Now, garbage collection gets run, and stmt4 gets gc'd. stmt4.finalize() calls stmt4.close()
which calls stmt3.close() which calls stmt2.close(). Since stmt2 is not closed any more (_closed
was just set to false above), it gets closed and returned to the pool.
> 6. Applications calls something in stmt6, which ends up in stmt2, and stmt2.checkOpen()
throws exception java.sql.SQLException: org.apache.commons.dbcp2.PoolablePreparedStatement
with address: "com.mysql.jdbc.ServerPreparedStatement[12] - select 1" is closed.
> PoolGuardConnectionWrapper prevents the application from accidentally calling stmt4.close()
later again, but this doesn't prevent the finalizer.... DBCP 1.4 did not have any finalizers,
so it doesn't have this bug. 
> I guess DelegatingStatement.close() should check isClosed(), and in the finally block,
also do setDelegate(null) (like PoolGuardConnectionWrapper does), to make sure the delegate
(which might be in active use by someone else) can never get called via this DelegatingStatement
again (although most methods in DelegatingStatement start with checkOpen(), not all of them

This message was sent by Atlassian JIRA

View raw message