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