openjpa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ppod...@apache.org
Subject svn commit: r668814 - in /openjpa/trunk: 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/datacache/ openjpa-persi...
Date Tue, 17 Jun 2008 20:07:13 GMT
Author: ppoddar
Date: Tue Jun 17 13:07:12 2008
New Revision: 668814

URL: http://svn.apache.org/viewvc?rev=668814&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/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java
    openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/common/apps/PObject.java
Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheBehavesIdentical.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/PObject.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?rev=668814&r1=668813&r2=668814&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
(original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
Tue Jun 17 13:07:12 2008
@@ -53,6 +53,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;
@@ -101,7 +102,11 @@
 import org.apache.openjpa.util.GeneralException;
 import org.apache.openjpa.util.InternalException;
 import org.apache.openjpa.util.InvalidStateException;
+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;
@@ -158,16 +163,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);
 
@@ -364,6 +359,9 @@
     // any positive number = batch limit
     public int batchLimit = NO_BATCH;
     
+    public final Map<Integer,Set<String>> sqlStateCodes = 
+    	new HashMap<Integer, Set<String>>();
+                                              
     public DBDictionary() {
         fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{
             "BIGINT", "BIT", "BLOB", "CLOB", "DATE", "DECIMAL", "DISTINCT",
@@ -4109,8 +4107,32 @@
         if (selectWords != null)
             selectWordSet.addAll(Arrays.asList(Strings.split(selectWords
                     .toUpperCase(), ",", 0)));
+        
+        // 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(int errorType, String errorCode) {
+    	if (errorCode == null || errorCode.trim().length() == 0)
+    		return;
+		Set<String> codes = sqlStateCodes.get(errorType);
+    	if (codes == null) {
+    		codes = new HashSet<String>();
+    		codes.add(errorCode.trim());
+    		sqlStateCodes.put(errorType, codes);
+    	} else {
+    		codes.add(errorCode.trim());
+    	}
+    }
+    
     //////////////////////////////////////
     // ConnectionDecorator implementation
     //////////////////////////////////////
@@ -4119,7 +4141,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 {
@@ -4170,7 +4192,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;
     	}
@@ -4179,26 +4201,36 @@
     }
     
     /**
-     * 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 (Integer type : sqlStateCodes.keySet()) {
+    		Set<String> erroStates = sqlStateCodes.get(type);
+    		if (erroStates != null && erroStates.contains(errorState)) {
+    			errorType = type;
+    			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);
+        }
     }
-
+    
     /**
      * Closes the specified {@link DataSource} and releases any
      * resources associated with it.

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java?rev=668814&r1=668813&r2=668814&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java
(original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java
Tue Jun 17 13:07:12 2008
@@ -100,26 +100,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;
-    }
-    
 }

Added: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java?rev=668814&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java
(added)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLErrorCodeReader.java
Tue Jun 17 13:07:12 2008
@@ -0,0 +1,136 @@
+package org.apache.openjpa.jdbc.sql;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.lib.xml.XMLFactory;
+import org.apache.openjpa.util.StoreException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Parses XML content of SQL Error State codes to populate errro codes for
+ * a given Database Dictionary.
+ * 
+ * @author Pinaki Poddar
+ * 
+ */
+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>();
+	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("referential-integrity",
+				StoreException.REFERENTIAL_INTEGRITY);
+
+	}
+	
+	private static final Localizer _loc = 
+		Localizer.forPackage(SQLErrorCodeReader.class);
+	
+	public List<String> getDictionaries(InputStream in) {
+		List<String> result = new ArrayList<String>();
+		DocumentBuilder builder = XMLFactory.getDOMParser(false, false);
+		try {
+			Document doc = builder.parse(in);
+			Element root = doc.getDocumentElement();
+			NodeList nodes = root.getElementsByTagName("dictionary");
+			for (int i = 0; i < nodes.getLength(); i++) {
+				Node node = nodes.item(i);
+				NamedNodeMap attrs = node.getAttributes();
+				Node dictionary = attrs.getNamedItem("class");
+				if (dictionary != null) {
+				   result.add(dictionary.getNodeValue());
+				}
+			}
+		} catch (Throwable e) {
+			if (log.isWarnEnabled()) {
+				log.error(_loc.get("error-code-parse-error"));
+			}
+		} finally {
+			try {
+				in.close();
+			} catch (IOException e) {
+				// ignore
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * Parses given stream of XML content for error codes of the given database
+	 * dictionary name. Populates the given dictionary with the error codes.
+	 * 
+	 */
+	public void parse(InputStream in, String dictName, DBDictionary dict) {
+		if (in == null || dict == null)
+			return;
+		log = dict.conf.getLog(JDBCConfiguration.LOG_JDBC);
+		DocumentBuilder builder = XMLFactory.getDOMParser(false, false);
+		try {
+			Document doc = builder.parse(in);
+			Element root = doc.getDocumentElement();
+			NodeList nodes = root.getElementsByTagName("dictionary");
+			for (int i = 0; i < nodes.getLength(); i++) {
+				Node node = nodes.item(i);
+				NamedNodeMap attrs = node.getAttributes();
+				Node dictionary = attrs.getNamedItem("class");
+				if (dictionary != null 
+				 && dictionary.getNodeValue().equals(dictName)) {
+					readErrorCodes(node, dict);
+				}
+			}
+		} catch (Throwable e) {
+			if (log.isWarnEnabled()) {
+				log.error(_loc.get("error-code-parse-error"));
+			}
+		} finally {
+			try {
+				in.close();
+			} catch (IOException e) {
+				// ignore
+			}
+		}
+	}
+
+	static void readErrorCodes(Node node, DBDictionary dict) {
+		NodeList children = node.getChildNodes();
+		for (int i = 0; i < children.getLength(); i++) {
+			Node child = children.item(i);
+			short nodeType = child.getNodeType();
+			if (nodeType == Node.ELEMENT_NODE) {
+				String errorType = child.getNodeName();
+				if (storeErrorTypes.containsKey(errorType)) {
+					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());
+						}
+					}
+				}
+			}
+		}
+	}
+}

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java?rev=668814&r1=668813&r2=668814&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java
(original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java
Tue Jun 17 13:07:12 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);
-    }
 }

Added: openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml?rev=668814&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml
(added)
+++ openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml
Tue Jun 17 13:07:12 2008
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.   
+-->
+<!-- ======================================================================= -->
+<!-- Lists SQL Error State codes for specific type of faults per database    -->
+<!-- dictionary.                                                             -->
+<!-- SQL Exceptions raised by JDBC should carry standard error state code    -->
+<!-- but they hardly do. Hence, the error state codes for specific type of   -->
+<!-- database error can be listed here. These codes help to narrow down the  -->
+<!-- cause of failure at JDBC layer and bubbles up as more intelligible      -->
+<!-- exception to the application.                                           -->
+<!-- ======================================================================= -->
+<sql-state-codes>
+
+	<dictionary class="org.apache.openjpa.jdbc.sql.DB2Dictionary">
+		<lock>-911,-913</lock>
+		<referential-integrity>-407,-530,-531,-532,-543,-544,-545,-603,-667,-803</referential-integrity>
+		<object-exists></object-exists>
+		<object-not-found></object-not-found>
+		<optimistic></optimistic>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.DerbyDictionary">
+		<lock>40001</lock>
+		<referential-integrity>22001,22005,23502,23503,23513,X0Y32</referential-integrity>
+		<object-exists>23505</object-exists>
+		<object-not-found></object-not-found>
+		<optimistic>40XL1,40001</optimistic>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.SQLServerDictionary">
+		<lock>1205</lock>
+		<referential-integrity>544,2601,2627,8114,8115</referential-integrity>
+		<optimistic>1205</optimistic>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.SybaseDictionary">
+		<lock>1205</lock>
+		<referential-integrity>423,511,515,530,547,2601,2615,2714</referential-integrity>
+		<object-exists></object-exists>
+		<object-not-found></object-not-found>
+		<optimistic>1205</optimistic>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.AccessDictionary">
+		<lock>40001</lock>
+		<referential-integrity>22001,22005,23502,23503,23513,X0Y32</referential-integrity>
+		<object-exists>23505,456c</object-exists>
+		<object-not-found></object-not-found>
+		<optimistic>40XL1,40001</optimistic>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.CacheDictionary">
+		<lock>40001</lock>
+		<referential-integrity>22001,22005,23502,23503,23513,X0Y32</referential-integrity>
+		<object-exists>23505,456c</object-exists>
+		<object-not-found></object-not-found>
+		<optimistic>40XL1,40001</optimistic>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.EmpressDictionary">
+		<lock>40001</lock>
+		<referential-integrity>22001,22005,23502,23503,23513,X0Y32</referential-integrity>
+		<object-exists>23505,456c</object-exists>
+		<object-not-found></object-not-found>
+		<optimistic>40XL1,40001</optimistic>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.FoxProDictionary">
+		<lock>40001</lock>
+		<referential-integrity>22001,22005,23502,23503,23513,X0Y32</referential-integrity>
+		<object-exists>23505,456c</object-exists>
+		<object-not-found></object-not-found>
+		<optimistic>40XL1,40001</optimistic>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.H2Dictionary">
+		<referential-integrity>22003,22012,22025,23000,23001</referential-integrity>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.HSQLDictionary">
+		<referential-integrity>-9</referential-integrity>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.InformixDictionary">
+		<referential-integrity>-239,-268,-692,-11030</referential-integrity>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.InterbaseDictionary">
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.FirebirdDictionary">
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.JDataStoreDictionary">
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.MySQLDictionary">
+		<lock>1213</lock>
+		<referential-integrity>630,839,840,893,1062,1169,1215,1216,1217,1451,1452,1557</referential-integrity>
+		<object-exists>23000</object-exists>
+		<object-not-found></object-not-found>
+		<optimistic>41000,1205,1213</optimistic>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.OracleDictionary">
+		<lock></lock>
+		<referential-integrity>1,1400,1722,2291,2292</referential-integrity>
+		<object-exists></object-exists>
+		<object-not-found></object-not-found>
+		<optimistic></optimistic>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.PointbaseDictionary">
+		<lock></lock>
+		<referential-integrity>22001,22005,23502,23503,23513,X0Y32</referential-integrity>
+		<object-exists></object-exists>
+		<object-not-found></object-not-found>
+		<optimistic></optimistic>
+	</dictionary>
+	
+	<dictionary class="org.apache.openjpa.jdbc.sql.PostgresDictionary">
+		<lock>55P03,40P01</lock>
+		<referential-integrity>23000,23502,23503,23505,23514</referential-integrity>
+		<object-exists></object-exists>
+		<object-not-found></object-not-found>
+		<optimistic>55P03</optimistic>
+	</dictionary>
+	
+</sql-state-codes>
\ No newline at end of file

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheBehavesIdentical.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheBehavesIdentical.java?rev=668814&r1=668813&r2=668814&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheBehavesIdentical.java
(original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/TestDataCacheBehavesIdentical.java
Tue Jun 17 13:07:12 2008
@@ -30,7 +30,7 @@
 import org.apache.openjpa.persistence.cache.common.apps.BidirectionalOne2OneOwned;
 import org.apache.openjpa.persistence.cache.common.apps.BidirectionalOne2OneOwner;
 import org.apache.openjpa.persistence.common.utils.AbstractTestCase;
-import org.apache.openjpa.persistence.exception.PObject;
+import org.apache.openjpa.persistence.datacache.common.apps.PObject;
 
 /**
  * Tests various application behavior with or without DataCache.

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/common/apps/PObject.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/common/apps/PObject.java?rev=668814&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/common/apps/PObject.java
(added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/datacache/common/apps/PObject.java
Tue Jun 17 13:07:12 2008
@@ -0,0 +1,57 @@
+/*
+ * 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.persistence.datacache.common.apps;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Version;
+
+/**
+ * A Simple entity for testing. Has a version field for testing optimistic
+ * concurrent usage.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@Entity
+public class PObject {
+	@Id
+	@GeneratedValue
+	private long id;
+	private String name;
+	@Version
+	private int version;
+	
+	public String getName() {
+		return name;
+	}
+	
+	public void setName(String name) {
+		this.name = name;
+	}
+	
+	public long getId() {
+		return id;
+	}
+	
+	public int getVersion() {
+		return version;
+	}
+}

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/PObject.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/PObject.java?rev=668814&r1=668813&r2=668814&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/PObject.java
(original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/PObject.java
Tue Jun 17 13:07:12 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/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java?rev=668814&r1=668813&r2=668814&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java
(original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/exception/TestException.java
Tue Jun 17 13:07:12 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, CLEAR_TABLES);
     }
     
@@ -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/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java?rev=668814&r1=668813&r2=668814&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java
(original)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java
Tue Jun 17 13:07:12 2008
@@ -143,24 +143,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());



Mime
View raw message