Closing a Connection or Statement object twice should be a no-op
authorKris Jurka <books@ejurka.com>
Tue, 24 Feb 2004 13:11:45 +0000 (13:11 +0000)
committerKris Jurka <books@ejurka.com>
Tue, 24 Feb 2004 13:11:45 +0000 (13:11 +0000)
instead of throwing an Exception.

Per report from Victor Sergienko.

src/interfaces/jdbc/org/postgresql/jdbc1/AbstractJdbc1Statement.java
src/interfaces/jdbc/org/postgresql/jdbc2/optional/PooledConnectionImpl.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/ConnectionTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/ConnectionPoolTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/optional/PoolingDataSourceTest.java

index 50c214a30d9d8c13bc72332939b05ef440bd63f0..0addc9e47f9bf05a85eabe284ff317d53be1e0b3 100644 (file)
@@ -26,7 +26,7 @@ import java.sql.Timestamp;
 import java.sql.Types;
 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
  * extended by org.postgresql.jdbc2.AbstractJdbc2Statement which adds the jdbc2
  * 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 isClosed = false;
+
     // m_preparedCount is used for naming of auto-cursors and must
     // be synchronized so that multiple threads using the same
     // connection don't stomp over each others cursors.
@@ -785,6 +787,10 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
     */
    public void close() throws SQLException
    {
+       // closing an already closed Statement is a no-op.
+       if (isClosed)
+           return;
+
        // Force the ResultSet to close
        java.sql.ResultSet rs = getResultSet();
        if (rs != null)
@@ -794,6 +800,7 @@ public abstract class AbstractJdbc1Statement implements BaseStatement
 
        // Disasociate it from us (For Garbage Collection)
        result = null;
+       isClosed = true;
    }
 
    /**
index a8be118d083eda9cdf9ea042f853650eacf3592a..5047a956f2adf253858aa54be517ab7fd4f05aba 100644 (file)
@@ -14,7 +14,7 @@ import org.postgresql.PGConnection;
  *
  * @author Aaron Mulder (ammulder@chariotsolutions.com)
  * @author Csaba Nagy (ncsaba@yahoo.com)
- * @version $Revision: 1.7.4.2 $
+ * @version $Revision: 1.7.4.3 $
  */
 public class PooledConnectionImpl implements PooledConnection
 {
@@ -234,12 +234,17 @@ public class PooledConnectionImpl implements PooledConnection
            {
                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");
            }
            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;
                if (!con.getAutoCommit())
                {
@@ -358,12 +363,12 @@ public class PooledConnectionImpl implements PooledConnection
                 return method.invoke(st, args);
             }
             // 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"))
             {
+                // closing an already closed object is a no-op
+                if (st == null || con.isClosed())
+                    return null;
+
                 try {
                     st.close();
                 } finally {
@@ -372,6 +377,10 @@ public class PooledConnectionImpl implements PooledConnection
                 }
                 return null;
             }
+            if (st == null || con.isClosed())
+            {
+                throw new SQLException("Statement has been closed");
+            }
             else if (method.getName().equals("getConnection"))
             {
                 return con.getProxy(); // the proxied connection, not a physical connection
index 1d79463eb1693d8e0c310a39662a2459ccaf74d7..2a28124f168b41364e738c0961a161ebc00c4958 100644 (file)
@@ -10,7 +10,7 @@ import java.sql.*;
  *
  * 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
@@ -347,4 +347,14 @@ public class ConnectionTest extends TestCase
            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();
+   }
 }
index 6c226d0219aa200b9d73bde0c195f7499578072e..662f7b6264dfe0bc1b517eaa6c26cf1ccc7918b6 100644 (file)
@@ -12,7 +12,7 @@ import java.io.*;
  * interface to the PooledConnection is through the CPDS.
  *
  * @author Aaron Mulder (ammulder@chariotsolutions.com)
- * @version $Revision: 1.6.4.3 $
+ * @version $Revision: 1.6.4.4 $
  */
 public class ConnectionPoolTest extends BaseDataSourceTest
 {
@@ -131,13 +131,6 @@ public class ConnectionPoolTest extends BaseDataSourceTest
            }
            catch (SQLException e)
            {}
-           try
-           {
-               con.close();
-               fail("Original connection wrapper should be closed when new connection wrapper is generated");
-           }
-           catch (SQLException e)
-           {}
            con2.close();
            pc.close();
        }
@@ -194,13 +187,8 @@ public class ConnectionPoolTest extends BaseDataSourceTest
            con.close();
            assertTrue(cc.getCount() == 2);
            assertTrue(cc.getErrorCount() == 0);
-           try
-           {
+           // a double close shouldn't fire additional events
                con.close();
-               fail("Should not be able to close a connection wrapper twice");
-           }
-           catch (SQLException e)
-           {}
            assertTrue(cc.getCount() == 2);
            assertTrue(cc.getErrorCount() == 0);
            pc.close();
index 701f835332e2f9472113158ffca2ea3949cf64d2..75bb4ac2e7bc1ab667e6f87efcdaf8954d3af9cb 100644 (file)
@@ -1,6 +1,7 @@
 package org.postgresql.test.jdbc2.optional;
 
 import java.sql.SQLException;
+import java.sql.Statement;
 import org.postgresql.test.TestUtil;
 import org.postgresql.jdbc2.optional.PoolingDataSource;
 import org.postgresql.jdbc2.optional.BaseDataSource;
@@ -9,7 +10,7 @@ import org.postgresql.jdbc2.optional.BaseDataSource;
  * Minimal tests for pooling DataSource.  Needs many more.
  *
  * @author Aaron Mulder (ammulder@chariotsolutions.com)
- * @version $Revision: 1.1.6.1 $
+ * @version $Revision: 1.1.6.2 $
  */
 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();
+    }
 }