Closing a Connection or Statement object twice should be a no-op

instead of throwing an Exception.

Per report from Victor Sergienko.
This commit is contained in:
Kris Jurka 2004-02-24 13:11:45 +00:00
parent 27ae96c2b6
commit 935e6e502d
5 changed files with 60 additions and 23 deletions

View File

@ -26,7 +26,7 @@ import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.util.Vector; import java.util.Vector;
/* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.41.2.3 2004/02/03 05:13:55 jurka Exp $ /* $Header: /cvsroot/pgsql/src/interfaces/jdbc/org/postgresql/jdbc1/Attic/AbstractJdbc1Statement.java,v 1.41.2.4 2004/02/24 13:11:44 jurka Exp $
* This class defines methods of the jdbc1 specification. This class is * This class defines methods of the jdbc1 specification. This class is
* extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2 * extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2
* methods. The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement * methods. The real Statement class (for jdbc1) is org.postgresql.jdbc1.Jdbc1Statement
@ -84,6 +84,8 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
private boolean m_useServerPrepare = false; private boolean m_useServerPrepare = false;
private boolean isClosed = false;
// m_preparedCount is used for naming of auto-cursors and must // m_preparedCount is used for naming of auto-cursors and must
// be synchronized so that multiple threads using the same // be synchronized so that multiple threads using the same
// connection don't stomp over each others cursors. // connection don't stomp over each others cursors.
@ -785,6 +787,10 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
*/ */
public void close() throws SQLException public void close() throws SQLException
{ {
// closing an already closed Statement is a no-op.
if (isClosed)
return;
// Force the ResultSet to close // Force the ResultSet to close
java.sql.ResultSet rs = getResultSet(); java.sql.ResultSet rs = getResultSet();
if (rs != null) if (rs != null)
@ -794,6 +800,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
// Disasociate it from us (For Garbage Collection) // Disasociate it from us (For Garbage Collection)
result = null; result = null;
isClosed = true;
} }
/** /**

View File

@ -14,7 +14,7 @@ import org.postgresql.PGConnection;
* *
* @author Aaron Mulder (ammulder@chariotsolutions.com) * @author Aaron Mulder (ammulder@chariotsolutions.com)
* @author Csaba Nagy (ncsaba@yahoo.com) * @author Csaba Nagy (ncsaba@yahoo.com)
* @version $Revision: 1.7.4.2 $ * @version $Revision: 1.7.4.3 $
*/ */
public class PooledConnectionImpl implements PooledConnection public class PooledConnectionImpl implements PooledConnection
{ {
@ -234,12 +234,17 @@ public class PooledConnectionImpl implements PooledConnection
{ {
return con == null ? Boolean.TRUE : Boolean.FALSE; return con == null ? Boolean.TRUE : Boolean.FALSE;
} }
if (con == null) if (con == null && !method.getName().equals("close"))
{ {
throw new SQLException(automatic ? "Connection has been closed automatically because a new connection was opened for the same PooledConnection or the PooledConnection has been closed" : "Connection has been closed"); throw new SQLException(automatic ? "Connection has been closed automatically because a new connection was opened for the same PooledConnection or the PooledConnection has been closed" : "Connection has been closed");
} }
if (method.getName().equals("close")) if (method.getName().equals("close"))
{ {
// we are already closed and a double close
// is not an error.
if (con == null)
return null;
SQLException ex = null; SQLException ex = null;
if (!con.getAutoCommit()) if (!con.getAutoCommit())
{ {
@ -358,12 +363,12 @@ public class PooledConnectionImpl implements PooledConnection
return method.invoke(st, args); return method.invoke(st, args);
} }
// All the rest is from the Statement interface // All the rest is from the Statement interface
if (st == null || con.isClosed())
{
throw new SQLException("Statement has been closed");
}
if (method.getName().equals("close")) if (method.getName().equals("close"))
{ {
// closing an already closed object is a no-op
if (st == null || con.isClosed())
return null;
try { try {
st.close(); st.close();
} finally { } finally {
@ -372,6 +377,10 @@ public class PooledConnectionImpl implements PooledConnection
} }
return null; return null;
} }
if (st == null || con.isClosed())
{
throw new SQLException("Statement has been closed");
}
else if (method.getName().equals("getConnection")) else if (method.getName().equals("getConnection"))
{ {
return con.getProxy(); // the proxied connection, not a physical connection return con.getProxy(); // the proxied connection, not a physical connection

View File

@ -10,7 +10,7 @@ import java.sql.*;
* *
* PS: Do you know how difficult it is to type on a train? ;-) * PS: Do you know how difficult it is to type on a train? ;-)
* *
* $Id: ConnectionTest.java,v 1.10 2002/10/17 05:33:52 barry Exp $ * $Id: ConnectionTest.java,v 1.10.6.1 2004/02/24 13:11:44 jurka Exp $
*/ */
public class ConnectionTest extends TestCase public class ConnectionTest extends TestCase
@ -347,4 +347,14 @@ public class ConnectionTest extends TestCase
assertTrue(ex.getMessage(), false); assertTrue(ex.getMessage(), false);
} }
} }
/**
* Closing a Connection more than once is not an error.
*/
public void testDoubleClose() throws SQLException
{
Connection con = TestUtil.openDB();
con.close();
con.close();
}
} }

View File

@ -12,7 +12,7 @@ import java.io.*;
* interface to the PooledConnection is through the CPDS. * interface to the PooledConnection is through the CPDS.
* *
* @author Aaron Mulder (ammulder@chariotsolutions.com) * @author Aaron Mulder (ammulder@chariotsolutions.com)
* @version $Revision: 1.6.4.3 $ * @version $Revision: 1.6.4.4 $
*/ */
public class ConnectionPoolTest extends BaseDataSourceTest public class ConnectionPoolTest extends BaseDataSourceTest
{ {
@ -131,13 +131,6 @@ public class ConnectionPoolTest extends BaseDataSourceTest
} }
catch (SQLException e) catch (SQLException e)
{} {}
try
{
con.close();
fail("Original connection wrapper should be closed when new connection wrapper is generated");
}
catch (SQLException e)
{}
con2.close(); con2.close();
pc.close(); pc.close();
} }
@ -194,13 +187,8 @@ public class ConnectionPoolTest extends BaseDataSourceTest
con.close(); con.close();
assertTrue(cc.getCount() == 2); assertTrue(cc.getCount() == 2);
assertTrue(cc.getErrorCount() == 0); assertTrue(cc.getErrorCount() == 0);
try // a double close shouldn't fire additional events
{
con.close(); con.close();
fail("Should not be able to close a connection wrapper twice");
}
catch (SQLException e)
{}
assertTrue(cc.getCount() == 2); assertTrue(cc.getCount() == 2);
assertTrue(cc.getErrorCount() == 0); assertTrue(cc.getErrorCount() == 0);
pc.close(); pc.close();

View File

@ -1,6 +1,7 @@
package org.postgresql.test.jdbc2.optional; package org.postgresql.test.jdbc2.optional;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import org.postgresql.test.TestUtil; import org.postgresql.test.TestUtil;
import org.postgresql.jdbc2.optional.PoolingDataSource; import org.postgresql.jdbc2.optional.PoolingDataSource;
import org.postgresql.jdbc2.optional.BaseDataSource; import org.postgresql.jdbc2.optional.BaseDataSource;
@ -9,7 +10,7 @@ import org.postgresql.jdbc2.optional.BaseDataSource;
* Minimal tests for pooling DataSource. Needs many more. * Minimal tests for pooling DataSource. Needs many more.
* *
* @author Aaron Mulder (ammulder@chariotsolutions.com) * @author Aaron Mulder (ammulder@chariotsolutions.com)
* @version $Revision: 1.1.6.1 $ * @version $Revision: 1.1.6.2 $
*/ */
public class PoolingDataSourceTest extends BaseDataSourceTest public class PoolingDataSourceTest extends BaseDataSourceTest
{ {
@ -96,4 +97,26 @@ public class PoolingDataSourceTest extends BaseDataSourceTest
{ {
} }
} }
/**
* Closing a Connection twice is not an error.
*/
public void testDoubleConnectionClose() throws SQLException
{
con = getDataSourceConnection();
con.close();
con.close();
}
/**
* Closing a Statement twice is not an error.
*/
public void testDoubleStatementClose() throws SQLException
{
con = getDataSourceConnection();
Statement stmt = con.createStatement();
stmt.close();
stmt.close();
con.close();
}
} }