You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ka...@apache.org on 2007/02/07 18:36:40 UTC

svn commit: r504630 - in /db/derby/code/trunk/java: client/org/apache/derby/client/am/ testing/org/apache/derbyTesting/functionTests/tests/derbynet/ testing/org/apache/derbyTesting/junit/

Author: kahatlen
Date: Wed Feb  7 09:36:38 2007
New Revision: 504630

URL: http://svn.apache.org/viewvc?view=rev&rev=504630
Log:
DERBY-790: SQLException used by the networked interface to Derby is not serializable

Patch contributed by Francois Orsini.

Modified:
    db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlException.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/SqlExceptionTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseJDBCTestCase.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java

Modified: db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlException.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlException.java?view=diff&rev=504630&r1=504629&r2=504630
==============================================================================
--- db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlException.java (original)
+++ db/derby/code/trunk/java/client/org/apache/derby/client/am/SqlException.java Wed Feb  7 09:36:38 2007
@@ -74,8 +74,9 @@
 //
 public class SqlException extends Exception implements Diagnosable {
     protected static final int DEFAULT_ERRCODE = 99999;
-    protected Sqlca sqlca_ = null; // for engine generated errors only
+    protected transient Sqlca sqlca_ = null; // for engine generated errors only
     protected String message_ = null;
+    protected String cachedMessage_ = null;
     private String batchPositionLabel_; // for batched exceptions only
     protected String sqlstate_ = null;
     protected int errorcode_ = DEFAULT_ERRCODE;
@@ -387,8 +388,22 @@
             return wrappedException_.getMessage();
         }
         
+        // The Net JDBC message is retrieved and cached if we have a valid
+        // SQLCA handle.
+        // It is possible that we don't have one in case of a serialized
+        // SqlException for instance. In this case, we set the message to the
+        // last one cached previously (if any available).
+        // For serialized SqlException, we can serialize the SQLCA as the
+        // object handle would become invalid, upon deserialization, causing
+        // the connection and JDBC not being retrievable (hence why it is
+        // being cached here).
         if (sqlca_ != null) {
-            message_ = ((Sqlca) sqlca_).getJDBCMessage();
+            cachedMessage_ = message_ = ((Sqlca) sqlca_).getJDBCMessage();
+        }
+        else if (cachedMessage_ != null) {
+            // SQLCA is no longer valid, set the message to the previously
+            // cached one
+            message_ = cachedMessage_;
         }
         
         if (batchPositionLabel_ != null) {

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/SqlExceptionTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/SqlExceptionTest.java?view=diff&rev=504630&r1=504629&r2=504630
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/SqlExceptionTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/SqlExceptionTest.java Wed Feb  7 09:36:38 2007
@@ -19,7 +19,7 @@
  */
 package org.apache.derbyTesting.functionTests.tests.derbynet;
 
-import org.apache.derbyTesting.junit.BaseTestCase;
+import org.apache.derbyTesting.junit.BaseJDBCTestCase;
 import org.apache.derbyTesting.junit.JDBC;
 
 import junit.framework.Test;
@@ -28,16 +28,26 @@
 import org.apache.derby.client.am.SqlException;
 import org.apache.derby.client.am.ClientMessageId;
 import org.apache.derby.shared.common.reference.SQLState;
+import java.sql.Connection;
+import java.sql.Statement;
+import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.io.IOException;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 
 /**
  * This is used for testing the SqlException class.  This test can be added
  * to.  My itch right now is to verify that exception chaining is working
  * correctly.
+ *
+ * This test also verifies that a SQLException object generated out of the
+ * derby network client driver can be serialized (DERBY-790).
  */
 
-public class SqlExceptionTest extends BaseTestCase
+public class SqlExceptionTest extends BaseJDBCTestCase
 {    
     public SqlExceptionTest(String name)
     {
@@ -88,6 +98,88 @@
         
         assertNotNull(javae.getNextException());
         assertEquals(javae.getNextException().getSQLState(), "08000");
+    }
+
+    /**
+     * Verify that a SQLException generated by the derby network client
+     * driver can be serialized (DERBY-790).
+     */
+    public void testSerializedException() throws Exception {
+
+        try {
+            Connection conn = getConnection();
+            Statement stmt = conn.createStatement();
+            // generate some exception by inserting some duplicate
+            // primary keys in the same batch
+            // This will generate some chained / nested transactions
+            // as well
+            String insertData = "INSERT INTO tableWithPK values " +
+                "(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)";
+            stmt.addBatch(insertData);
+            stmt.addBatch(insertData);
+            stmt.addBatch(insertData);
+            stmt.executeBatch();
+
+            // In case the statement completes successfully which is not
+            // expected
+            fail("Unexpected: SQL statement should have failed");
+        } catch (SQLException se) {
+            // Verify the SQLException can be serialized (DERBY-790)
+            SQLException se_ser = recreateSQLException(se);
+            // and that the original and serialized exceptions are equals
+            assertSQLState("Unexpected SQL State", se.getSQLState(), se_ser);
+            assertSQLExceptionEquals(se, se_ser);
+        }
+    }
+
+    /**
+     * Set up the connection to the database.
+     */
+    public void setUp() throws Exception {
+        Connection conn = getConnection();
+        String createTableWithPK = "CREATE TABLE tableWithPK (" +
+                "c1 int primary key," +
+                "c2 int)";
+        Statement stmt = conn.createStatement();
+        stmt.execute(createTableWithPK);
+        stmt.close();
+        conn.close();
+    }
+
+    /**
+     * Drop the table
+     */
+    public void tearDown() throws Exception {
+        Connection conn = getConnection();
+        Statement stmt = conn.createStatement();
+        stmt.executeUpdate("DROP TABLE tableWithPK");
+        stmt.close();
+        conn.close();
+        super.tearDown();
+    }
+
+    /**
+     * Recreate a SQLException by serializing the passed-in one and
+     * deserializing it into a new one that we're returning.
+     */
+    private SQLException recreateSQLException(SQLException se)
+    throws Exception
+    {
+        SQLException recreatedDS = null;
+
+        // Serialize and recreate (deserialize) the passed-in Exception
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(baos);
+        oos.writeObject(se);
+        oos.flush();
+        oos.close();
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        ObjectInputStream ois = new ObjectInputStream(bais);
+        recreatedDS = (SQLException) ois.readObject();
+        ois.close();
+        assertNotNull(recreatedDS);
+
+        return recreatedDS;
     }
 
     public static Test suite() {

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseJDBCTestCase.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseJDBCTestCase.java?view=diff&rev=504630&r1=504629&r2=504630
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseJDBCTestCase.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseJDBCTestCase.java Wed Feb  7 09:36:38 2007
@@ -33,6 +33,7 @@
 
 import junit.framework.AssertionFailedError;
 
+import org.apache.derby.iapi.services.info.JVMInfo;
 import org.apache.derby.tools.ij;
 
 
@@ -684,6 +685,79 @@
             if (haveRS)
                 JDBC.assertDrainResults(st.getResultSet(), -1);
             haveRS = st.getMoreResults();
+        }
+    }
+
+    /**
+     * Assert that the two (2) passed-in SQLException's are equals and
+     * not just '=='.
+     *
+     * @param se1 first SQLException to compare
+     * @param se2 second SQLException to compare
+     */
+    public static void assertSQLExceptionEquals(SQLException se1,
+                                                SQLException se2) {
+        // Ensure non-null SQLException's are being passed.
+        assertNotNull(
+            "Passed-in SQLException se1 cannot be null",
+            se1);
+        assertNotNull(
+            "Passed-in SQLException se2 cannot be null",
+            se2);
+
+        // Now verify that the passed-in SQLException's are of the same type
+        assertEquals("SQLException class types are different",
+                     se1.getClass().getName(), se2.getClass().getName());
+
+        // Here we check that the detailed message of both
+        // SQLException's is the same
+        assertEquals(
+                "Detailed messages of the SQLException's are different",
+                 se1.getMessage(), se2.getMessage());
+
+        // Now if we're running in a java runtime that supports chained
+        // exception, then let's compare these 2 SQLException's and
+        // whatever chained SQLException there can be through the beauty
+        // of recursion
+        if (JVMInfo.JDK_ID >= JVMInfo.J2SE_14)
+        {
+            // Here we check that the detailed message of both
+            // SQLException's throwable "cause" is the same.
+            // getCause() was introduced as part of Java 4.
+            // Save the SQLException
+            Throwable se1Cause = null, se2Cause = null;
+            Method m = null;
+            try
+            {
+                m = Throwable.class.getMethod("getCause", new Class[] {});
+                se1Cause = (Throwable) m.invoke(se1, new Object[] {});
+            }
+            catch (Throwable t)
+            {
+                // Throwable.getCause() should have succeeded
+                fail("Unexpected error: " + t.getMessage());
+            }
+            if (se1Cause != (Throwable) null)
+            {
+                try
+                {
+                    se2Cause = (Throwable) m.invoke(se2, new Object[] {});
+                }
+                catch (Throwable t)
+                {
+                    // Throwable.getCause() should have succeeded
+                    fail("Unexpected error: " + t.getMessage());
+                }
+                assertThrowableEquals(se1Cause, se2Cause);
+            }
+            else // se2.getCause() should not return any Cause then
+                assertNull(se2Cause);
+
+            if (se1.getNextException() != null)
+            {
+                assertSQLExceptionEquals(se1.getNextException(),
+                                         se2.getNextException());
+            }
         }
     }
 

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java?view=diff&rev=504630&r1=504629&r2=504630
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/BaseTestCase.java Wed Feb  7 09:36:38 2007
@@ -311,4 +311,31 @@
         r1.close();
         r2.close();
     }
+
+    /**
+     * Assert that the detailed messages of the 2 passed-in Throwable's are
+     * equal (rather than '=='), as well as their class types.
+     *
+     * @param t1 first throwable to compare
+     * @param t2 second throwable to compare
+     */
+    public static void assertThrowableEquals(Throwable t1,
+                                             Throwable t2) {
+        // Ensure non-null throwable's are being passed.
+        assertNotNull(
+            "Passed-in throwable t1 cannot be null to assert detailed message",
+            t1);
+        assertNotNull(
+            "Passed-in throwable t2 cannot be null to assert detailed message",
+            t2);
+
+        // Now verify that the passed-in throwable are of the same type
+        assertEquals("Throwable class types are different",
+                     t1.getClass().getName(), t2.getClass().getName());
+
+        // Here we finally check that the detailed message of both
+        // throwable's is the same
+        assertEquals("Detailed messages of the throwable's are different",
+                     t1.getMessage(), t2.getMessage());
+    }
 } // End class BaseTestCase