You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by ss...@apache.org on 2008/09/06 03:27:31 UTC

svn commit: r692601 - in /openjpa/branches/0.9.7-r547073: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/ openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/excepti...

Author: ssegu
Date: Fri Sep  5 18:27:31 2008
New Revision: 692601

URL: http://svn.apache.org/viewvc?rev=692601&view=rev
Log:
OPENJPA-458: Amplified SQL Exception processing. Introduced a new sql-error-state-codes.xml to specify database specific error code for different types of Store exceptions. This helps to narrow SQL exception to a specific errors which bubble up to user application. 

Added:
    openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java
      - copied, changed from r668814, openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java
    openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml
      - copied unchanged from r668814, openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml
Modified:
    openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
    openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java
    openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java
    openjpa/branches/0.9.7-r547073/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/PObject.java
    openjpa/branches/0.9.7-r547073/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java
    openjpa/branches/0.9.7-r547073/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java

Modified: openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?rev=692601&r1=692600&r2=692601&view=diff
==============================================================================
--- openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java (original)
+++ openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java Fri Sep  5 18:27:31 2008
@@ -51,6 +51,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
@@ -93,7 +94,12 @@
 import org.apache.openjpa.meta.FieldMetaData;
 import org.apache.openjpa.meta.JavaTypes;
 import org.apache.openjpa.util.GeneralException;
+import org.apache.openjpa.util.LockException;
+import org.apache.openjpa.util.ObjectExistsException;
+import org.apache.openjpa.util.ObjectNotFoundException;
 import org.apache.openjpa.util.OpenJPAException;
+import org.apache.openjpa.util.OptimisticException;
+import org.apache.openjpa.util.ReferentialIntegrityException;
 import org.apache.openjpa.util.Serialization;
 import org.apache.openjpa.util.StoreException;
 import org.apache.openjpa.util.UnsupportedException;
@@ -143,16 +149,6 @@
     private static final String ZERO_TIMESTAMP_STR =
         "'" + new Timestamp(0) + "'";
 
-    public static final List EMPTY_STRING_LIST = Arrays.asList(new String[]{});
-    public static final List[] SQL_STATE_CODES = 
-    	{EMPTY_STRING_LIST,                     // 0: Default
-    	 Arrays.asList(new String[]{"41000"}),  // 1: LOCK
-    	 EMPTY_STRING_LIST,                     // 2: OBJECT_NOT_FOUND
-    	 EMPTY_STRING_LIST,                     // 3: OPTIMISTIC
-    	 Arrays.asList(new String[]{"23000"}),  // 4: REFERENTIAL_INTEGRITY
-    	 EMPTY_STRING_LIST                      // 5: OBJECT_EXISTS
-    	}; 
-                                              
     private static final Localizer _loc = Localizer.forPackage
         (DBDictionary.class);
 
@@ -323,6 +319,8 @@
     private Method _setString = null;
     private Method _setCharStream = null;
 
+    public final Map sqlStateCodes = new HashMap();
+
     public DBDictionary() {
         fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{
             "BIGINT", "BIT", "BLOB", "CLOB", "DATE", "DECIMAL", "DISTINCT",
@@ -3727,8 +3725,32 @@
         // if user has unset sequence sql, null it out so we know sequences
         // aren't supported
         nextSequenceQuery = StringUtils.trimToNull(nextSequenceQuery);
-    }
 
+        // initialize the error codes
+        SQLErrorCodeReader codeReader = new SQLErrorCodeReader();
+        String rsrc = "sql-error-state-codes.xml";
+        InputStream stream = getClass().getResourceAsStream(rsrc);
+        String dictionaryClassName = getClass().getName();
+        if (stream == null) { // User supplied dictionary but no error codes xml
+        	stream = DBDictionary.class.getResourceAsStream(rsrc); // use default
+        	dictionaryClassName = getClass().getSuperclass().getName();
+        }
+        codeReader.parse(stream, dictionaryClassName, this);
+    }
+
+    public void addErrorCode(Integer errorType, String errorCode) {
+    	if (errorCode == null || errorCode.trim().length() == 0)
+    		return;
+		Set codes = (Set) sqlStateCodes.get(errorType);
+    	if (codes == null) {
+    		codes = new HashSet();
+    		codes.add(errorCode.trim());
+    		sqlStateCodes.put(errorType, codes);
+    	} else {
+    		codes.add(errorCode.trim());
+    	}
+    }
+    
     //////////////////////////////////////
     // ConnectionDecorator implementation
     //////////////////////////////////////
@@ -3737,7 +3759,7 @@
      * Decorate the given connection if needed. Some databases require special
      * handling for JDBC bugs. This implementation issues any
      * {@link #initializationSQL} that has been set for the dictionary but
-     * does not decoreate the connection.
+     * does not decorate the connection.
      */
     public Connection decorate(Connection conn)
         throws SQLException {
@@ -3788,7 +3810,7 @@
     public OpenJPAException newStoreException(String msg, SQLException[] causes,
         Object failed) {
     	if (causes != null && causes.length > 0) {
-    		OpenJPAException ret = SQLExceptions.narrow(msg, causes[0], this);
+    		OpenJPAException ret = narrow(msg, causes[0]);
     		ret.setFailedObject(failed).setNestedThrowables(causes);
     		return ret;
     	}
@@ -3797,24 +3819,35 @@
     }
     
     /**
-     * Gets the list of String, each represents an error that can help 
-     * to narrow down a SQL exception to specific type of StoreException.<br>
-     * For example, error code <code>"23000"</code> represents referential
-     * integrity violation and hence can be narrowed down to 
-     * {@link ReferentialIntegrityException} rather than more general
-     * {@link StoreException}.<br>
-     * JDBC Drivers are not uniform in return values of SQLState for the same
-     * error and hence each database specific Dictionary can specialize.<br>
-     * 
-     * 
-     * @return an <em>unmodifiable</em> list of Strings representing supposedly 
-     * uniform SQL States for a given type of StoreException. 
-     * Default behavior is to return an empty list.
-     */
-    public List/*<String>*/ getSQLStates(int exceptionType) {
-    	if (exceptionType>=0 && exceptionType<SQL_STATE_CODES.length)
-    		return SQL_STATE_CODES[exceptionType];
-    	return EMPTY_STRING_LIST;
+     * Gets the subtype of StoreException by matching the given SQLException's
+     * error state code to the list of error codes supplied by the dictionary.
+     * Returns -1 if no matching code can be found.
+     */
+    OpenJPAException narrow(String msg, SQLException ex) {
+    	String errorState = ex.getSQLState();
+    	int errorType = StoreException.GENERAL;
+    	for (Iterator iter = sqlStateCodes.keySet().iterator(); iter.hasNext(); ) {
+        	Integer type = (Integer) iter.next();
+    		Set erroStates = (Set) sqlStateCodes.get(type);
+    		if (erroStates != null && erroStates.contains(errorState)) {
+    			errorType = type.intValue();
+    			break;
+    		}
+    	}
+    	switch (errorType) {
+	    	case StoreException.LOCK: 
+	            return new LockException(msg);
+	    	case StoreException.OBJECT_EXISTS:
+	            return new ObjectExistsException(msg);
+	    	case StoreException.OBJECT_NOT_FOUND:
+	            return new ObjectNotFoundException(msg);
+	    	case StoreException.OPTIMISTIC:
+	            return new OptimisticException(msg);
+	    	case StoreException.REFERENTIAL_INTEGRITY: 
+	            return new ReferentialIntegrityException(msg);
+	        default:
+	            return new StoreException(msg);
+        }
     }
 
     /**

Modified: openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java?rev=692601&r1=692600&r2=692601&view=diff
==============================================================================
--- openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java (original)
+++ openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java Fri Sep  5 18:27:31 2008
@@ -98,26 +98,4 @@
             }
         }
     }
-    
-    /**
-     * Adds extra SQLState code that Derby JDBC Driver uses. In JDBC 4.0,
-     * SQLState will follow either XOPEN or SQL 2003 convention. A compliant
-     * driver can be queries via DatabaseMetaData.getSQLStateType() to detect
-     * the convention type.<br>
-     * This method is overwritten to highlight that a) the SQL State is ideally
-     * uniform across JDBC Drivers but not practically and b) the overwritten
-     * method must crate a new list to return as the super classes list is
-     * unmodifable.
-     */
-    public List getSQLStates(int exceptionType) {
-    	List original = super.getSQLStates(exceptionType);
-    	if (exceptionType == StoreException.LOCK) {
-    		// Can not add new codes to unmodifable list of the super class
-    		List newStates = new ArrayList(original);
-    		newStates.add("40XL1");
-    		return newStates;
-    	}
-    	return original;
-    }
-    
 }

Copied: openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java (from r668814, openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java)
URL: http://svn.apache.org/viewvc/openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java?p2=openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java&p1=openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java&r1=668814&r2=692601&rev=692601&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java (original)
+++ openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java Fri Sep  5 18:27:31 2008
@@ -1,3 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
 package org.apache.openjpa.jdbc.sql;
 
 import java.io.IOException;
@@ -32,24 +50,25 @@
 public class SQLErrorCodeReader {
 	private Log log = null;
 	public static final String ERROR_CODE_DELIMITER = ",";
-	public static final Map<String, Integer> storeErrorTypes = 
-		new HashMap<String, Integer>();
+	public static final Map storeErrorTypes = new HashMap();
 	static {
-		storeErrorTypes.put("lock", StoreException.LOCK);
-		storeErrorTypes.put("object-exists", StoreException.OBJECT_EXISTS);
-		storeErrorTypes
-				.put("object-not-found", StoreException.OBJECT_NOT_FOUND);
-		storeErrorTypes.put("optimistic", StoreException.OPTIMISTIC);
+		storeErrorTypes.put("lock", new Integer(StoreException.LOCK));
+		storeErrorTypes.put("object-exists", 
+				new Integer(StoreException.OBJECT_EXISTS));
+		storeErrorTypes.put("object-not-found", 
+				new Integer(StoreException.OBJECT_NOT_FOUND));
+		storeErrorTypes.put("optimistic", 
+				new Integer(StoreException.OPTIMISTIC));
 		storeErrorTypes.put("referential-integrity",
-				StoreException.REFERENTIAL_INTEGRITY);
+				new Integer(StoreException.REFERENTIAL_INTEGRITY));
 
 	}
 	
 	private static final Localizer _loc = 
 		Localizer.forPackage(SQLErrorCodeReader.class);
 	
-	public List<String> getDictionaries(InputStream in) {
-		List<String> result = new ArrayList<String>();
+	public List getDictionaries(InputStream in) {
+		List result = new ArrayList();
 		DocumentBuilder builder = XMLFactory.getDOMParser(false, false);
 		try {
 			Document doc = builder.parse(in);
@@ -124,9 +143,10 @@
 					String errorCodes = child.getTextContent();
 					if (!StringUtils.isEmpty(errorCodes)) {
 						String[] codes = errorCodes.split(ERROR_CODE_DELIMITER);
-						for (String code : codes) {
-							dict.addErrorCode(storeErrorTypes.get(errorType),
-									code.trim());
+						for (int k = 0; k < codes.length; k++ ) {
+							dict.addErrorCode(
+								(Integer) storeErrorTypes.get(errorType),
+								codes[k].trim());
 						}
 					}
 				}

Modified: openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java
URL: http://svn.apache.org/viewvc/openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java?rev=692601&r1=692600&r2=692601&view=diff
==============================================================================
--- openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java (original)
+++ openjpa/branches/0.9.7-r547073/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java Fri Sep  5 18:27:31 2008
@@ -117,32 +117,4 @@
         }
         return (SQLException[]) errs.toArray(new SQLException[errs.size()]);
     }
-    
-    /**
-     * Narrows the given SQLException to a specific type of 
-     * {@link StoreException#getSubtype() StoreException} by analyzing the
-     * SQLState code supplied by SQLException. Each database-specific 
-     * {@link DBDictionary dictionary} can supply a set of error codes that will
-     * map to a specific specific type of StoreException via 
-     * {@link DBDictionary#getSQLStates(int) getSQLStates()} method.
-     * The default behavior is to return generic {@link StoreException 
-     * StoreException}.
-     */
-    public static OpenJPAException narrow(String msg, SQLException se, 
-    		DBDictionary dict) {
-        String e = se.getSQLState();
-        if (dict.getSQLStates(StoreException.LOCK).contains(e)) 
-            return new LockException(msg);
-        else if (dict.getSQLStates(StoreException.OBJECT_EXISTS).contains(e))
-            return new ObjectExistsException(msg);
-        else if (dict.getSQLStates(StoreException.OBJECT_NOT_FOUND).contains(e))
-            return new ObjectNotFoundException(msg);
-        else if (dict.getSQLStates(StoreException.OPTIMISTIC).contains(e))
-            return new OptimisticException(msg);
-        else if (dict.getSQLStates(StoreException.REFERENTIAL_INTEGRITY)
-        		.contains(e)) 
-            return new ReferentialIntegrityException(msg);
-        else
-            return new StoreException(msg);
-    }
 }

Modified: openjpa/branches/0.9.7-r547073/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/PObject.java
URL: http://svn.apache.org/viewvc/openjpa/branches/0.9.7-r547073/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/PObject.java?rev=692601&r1=692600&r2=692601&view=diff
==============================================================================
--- openjpa/branches/0.9.7-r547073/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/PObject.java (original)
+++ openjpa/branches/0.9.7-r547073/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/PObject.java Fri Sep  5 18:27:31 2008
@@ -33,7 +33,6 @@
 @Entity
 public class PObject {
 	@Id
-	@GeneratedValue
 	private long id;
 	private String name;
 	@Version
@@ -47,6 +46,10 @@
 		this.name = name;
 	}
 	
+	public void setId(long id) {
+		this.id = id;
+	}
+
 	public long getId() {
 		return id;
 	}

Modified: openjpa/branches/0.9.7-r547073/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java
URL: http://svn.apache.org/viewvc/openjpa/branches/0.9.7-r547073/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java?rev=692601&r1=692600&r2=692601&view=diff
==============================================================================
--- openjpa/branches/0.9.7-r547073/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java (original)
+++ openjpa/branches/0.9.7-r547073/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java Fri Sep  5 18:27:31 2008
@@ -18,17 +18,32 @@
  */
 package org.apache.openjpa.persistence.exception;
 
+import java.io.InputStream;
 import java.sql.SQLException;
+import java.util.List;
 
+import javax.persistence.EntityExistsException;
 import javax.persistence.EntityManager;
+import javax.persistence.EntityNotFoundException;
 import javax.persistence.OptimisticLockException;
+import javax.persistence.TransactionRequiredException;
+
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.jdbc.sql.SQLErrorCodeReader;
 import org.apache.openjpa.persistence.test.SingleEMFTestCase;
 
 /**
  * Tests proper JPA exceptions are raised by the implementation. 
+ * Actual runtime type of the raised exception is a subclass of JPA-defined 
+ * exception.
+ * The raised exception may nest the expected exception. 
+ * 
+ * @author Pinaki Poddar
  */
 public class TestException extends SingleEMFTestCase {
-    public void setUp() {
+	private static long ID_COUNTER = System.currentTimeMillis();
+    
+	public void setUp() {
         super.setUp(PObject.class);
     }
     
@@ -36,16 +51,17 @@
 	 * Tests that when Optimistic transaction consistency is violated, the
 	 * exception thrown is an instance of javax.persistence.OptimisticException.
 	 */
-	public void testThrowsJPADefinedOptimisticException() {
+	public void testThrowsOptimisticException() {
 		EntityManager em1 = emf.createEntityManager();
 		EntityManager em2 = emf.createEntityManager();
 		assertNotEquals(em1, em2);
 		
 		em1.getTransaction().begin();
 		PObject pc = new PObject();
+		long id = ++ID_COUNTER;
+		pc.setId(id);
 		em1.persist(pc);
 		em1.getTransaction().commit();
-		Object id = pc.getId();
 		em1.clear();
 		
 		em1.getTransaction().begin();
@@ -61,47 +77,121 @@
 		try {
 			pc2.setName("Modified in TXN2");
 			em2.flush();
-			fail("Expected optimistic exception on flush");
+			fail("Expected " + OptimisticLockException.class);
 		} catch (Throwable t) {
-			if (!isExpectedException(t, OptimisticLockException.class)) {
-				print(t);
-				fail(t.getCause().getClass() + " is not " + 
-						OptimisticLockException.class);
-			}
+			assertException(t, OptimisticLockException.class);
 		}
 		
 		em1.getTransaction().commit();
 		try {
 			em2.getTransaction().commit();
-			fail("Expected optimistic exception on commit");
+			fail("Expected " + OptimisticLockException.class);
+		} catch (Throwable t) {
+			assertException(t, OptimisticLockException.class);
+		}
+	}
+	
+	public void testThrowsEntityExistsException() {
+		EntityManager em = emf.createEntityManager();
+		
+		em.getTransaction().begin();
+		PObject pc = new PObject();
+		long id = ++ID_COUNTER;
+		pc.setId(id);
+		em.persist(pc);
+		em.getTransaction().commit();
+		em.clear();
+		
+		em.getTransaction().begin();
+		PObject pc2 = new PObject();
+		pc2.setId(id);
+		em.persist(pc2);
+		try {
+			em.getTransaction().commit();
+			fail("Expected " + EntityExistsException.class);
+		} catch (Throwable t) {
+			assertException(t, EntityExistsException.class);
+		}
+	}
+	
+	public void testThrowsEntityNotFoundException() {
+		EntityManager em = emf.createEntityManager();
+		
+		em.getTransaction().begin();
+		PObject pc = new PObject();
+		long id = ++ID_COUNTER;
+		pc.setId(id);
+		em.persist(pc);
+		em.getTransaction().commit();
+		
+		EntityManager em2 = emf.createEntityManager();
+		em2.getTransaction().begin();
+		PObject pc2 = em2.find(PObject.class, id);
+		assertNotNull(pc2);
+		em2.remove(pc2);
+		em2.getTransaction().commit();
+		
+		try {
+			em.refresh(pc);
+			fail("Expected " + EntityNotFoundException.class);
 		} catch (Throwable t) {
-			if (!isExpectedException(t, OptimisticLockException.class)) {
-				print(t);
-				fail(t.getCause().getClass() + " is not " + 
-						OptimisticLockException.class);
+			assertException(t, EntityNotFoundException.class);
+		}
+	}
+	
+	public void testErrorCodeConfigurationHasAllKnownDictionaries() {
+		SQLErrorCodeReader reader = new SQLErrorCodeReader();
+		InputStream in = DBDictionary.class.getResourceAsStream
+			("sql-error-state-codes.xml");
+		assertNotNull(in);
+		List<String> names = reader.getDictionaries(in);
+		assertTrue(names.size()>=18);
+		for (String name:names) {
+			try {
+				Class.forName(name, false, Thread.currentThread()
+							.getContextClassLoader());
+			} catch (Throwable t) {
+				fail("DB dictionary " + name + " can not be loaded");
+				t.printStackTrace();
 			}
 		}
 	}
 	
+	/**
+	 * Asserts that the given expected type of the exception is equal to or a
+	 * subclass of the given throwable or any of its nested exception.
+	 * Otherwise fails assertion and prints the given throwable and its nested
+	 * exception on the console. 
+	 */
+	void assertException(Throwable t, Class expectedType) {
+		if (!isExpectedException(t, expectedType)) {
+			t.printStackTrace();
+			print(t, 0);
+			fail(t + " or its cause is not instanceof " + expectedType);
+		}
+	}
+	
+	/**
+	 * Affirms if the given expected type of the exception is equal to or a
+	 * subclass of the given throwable or any of its nested exception.
+	 */
 	boolean isExpectedException(Throwable t, Class expectedType) {
-		if (t == null) return false;
+		if (t == null) 
+			return false;
 		if (expectedType.isAssignableFrom(t.getClass()))
 				return true;
-		if (t.getCause()==t) return false;
 		return isExpectedException(t.getCause(), expectedType);
 	}
 	
-	void print(Throwable t) {
-		print(t, 0);
-	}
-	
 	void print(Throwable t, int tab) {
 		if (t == null) return;
 		for (int i=0; i<tab*4;i++) System.out.print(" ");
 		String sqlState = (t instanceof SQLException) ? 
-			"(SQLState=" + ((SQLException)t).getSQLState() + ":" + t.getMessage() + ")":"";
+			"(SQLState=" + ((SQLException)t).getSQLState() + ":" 
+				+ t.getMessage() + ")" : "";
 		System.out.println(t.getClass().getName() + sqlState);
-		if (t.getCause()==t) return;
+		if (t.getCause() == t) 
+			return;
 		print(t.getCause(), tab+1);
 	}
 }

Modified: openjpa/branches/0.9.7-r547073/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java
URL: http://svn.apache.org/viewvc/openjpa/branches/0.9.7-r547073/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java?rev=692601&r1=692600&r2=692601&view=diff
==============================================================================
--- openjpa/branches/0.9.7-r547073/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java (original)
+++ openjpa/branches/0.9.7-r547073/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java Fri Sep  5 18:27:31 2008
@@ -22,7 +22,10 @@
 
 import org.apache.openjpa.kernel.Broker;
 import org.apache.openjpa.util.Exceptions;
+import org.apache.openjpa.util.ObjectExistsException;
+import org.apache.openjpa.util.ObjectNotFoundException;
 import org.apache.openjpa.util.OpenJPAException;
+import org.apache.openjpa.util.OptimisticException;
 import org.apache.openjpa.util.RuntimeExceptionTranslator;
 import org.apache.openjpa.util.StoreException;
 import org.apache.openjpa.util.UserException;
@@ -64,7 +67,7 @@
                     try {
                         throwing = true;
                         if (em.isOpen() && em.isActive())
-                            em.setRollbackOnly(re);
+                            em.setRollbackOnly(ex);
                     } finally {
                         // handle re-entrancy
                         throwing = false;
@@ -139,24 +142,26 @@
      */
     private static Throwable translateStoreException(OpenJPAException ke) {
         Exception e;
-        switch (ke.getSubtype()) {
-            case StoreException.OBJECT_NOT_FOUND:
+        Throwable cause = (ke.getNestedThrowables() != null 
+                        && ke.getNestedThrowables().length == 1)
+                         ? ke.getNestedThrowables()[0] : null;
+        if (ke.getSubtype() == StoreException.OBJECT_NOT_FOUND 
+         || cause instanceof ObjectNotFoundException) {
                 e = new org.apache.openjpa.persistence.EntityNotFoundException
                     (ke.getMessage(), getNestedThrowables(ke),
                         getFailedObject(ke), ke.isFatal());
-                break;
-            case StoreException.OPTIMISTIC:
-            case StoreException.LOCK:
-                e = new org.apache.openjpa.persistence.OptimisticLockException
+        } else if (ke.getSubtype() == StoreException.OPTIMISTIC 
+        		|| ke.getSubtype() == StoreException.LOCK
+        		|| cause instanceof OptimisticException) {
+            	e = new org.apache.openjpa.persistence.OptimisticLockException
                     (ke.getMessage(), getNestedThrowables(ke),
                         getFailedObject(ke), ke.isFatal());
-                break;
-            case StoreException.OBJECT_EXISTS:
+        } else if (ke.getSubtype() == StoreException.OBJECT_EXISTS
+        		|| cause instanceof ObjectExistsException) {
                 e = new org.apache.openjpa.persistence.EntityExistsException
                     (ke.getMessage(), getNestedThrowables(ke),
                         getFailedObject(ke), ke.isFatal());
-                break;
-            default:
+        } else {
                 e = new org.apache.openjpa.persistence.PersistenceException
                     (ke.getMessage(), getNestedThrowables(ke),
                         getFailedObject(ke), ke.isFatal());