OPENJPA-1565: Raise correct time out exception

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@922288 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Pinaki Poddar 2010-03-12 15:08:16 +00:00
parent 572d593427
commit 4be5a5a194
17 changed files with 323 additions and 158 deletions

View File

@ -35,6 +35,7 @@ import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.StoreContext; import org.apache.openjpa.kernel.StoreContext;
import org.apache.openjpa.kernel.VersionLockManager; import org.apache.openjpa.kernel.VersionLockManager;
import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.util.Exceptions;
import org.apache.openjpa.util.LockException; import org.apache.openjpa.util.LockException;
/** /**
@ -117,8 +118,7 @@ public class PessimisticLockManager
JDBCFetchConfiguration fetch = _store.getFetchConfiguration(); JDBCFetchConfiguration fetch = _store.getFetchConfiguration();
if (dict.simulateLocking) if (dict.simulateLocking)
return; return;
dict.assertSupport(dict.supportsSelectForUpdate, dict.assertSupport(dict.supportsSelectForUpdate, "SupportsSelectForUpdate");
"SupportsSelectForUpdate");
Object id = sm.getObjectId(); Object id = sm.getObjectId();
ClassMapping mapping = (ClassMapping) sm.getMetaData(); ClassMapping mapping = (ClassMapping) sm.getMetaData();
@ -137,7 +137,7 @@ public class PessimisticLockManager
checkLock(rs, sm, timeout); checkLock(rs, sm, timeout);
} }
} catch (SQLException se) { } catch (SQLException se) {
throw SQLExceptions.getStore(se, dict, level); throw SQLExceptions.getStore(se, Exceptions.toString(sm.getPersistenceCapable()), dict, level);
} finally { } finally {
if (stmnt != null) if (stmnt != null)
try { stmnt.close(); } catch (SQLException se) {} try { stmnt.close(); } catch (SQLException se) {}

View File

@ -923,22 +923,18 @@ public class DB2Dictionary
} }
@Override @Override
protected Boolean matchErrorState(int subtype, Set<String> errorStates, protected boolean isFatalException(int subtype, SQLException ex) {
SQLException ex) {
Boolean recoverable = null;
String errorState = ex.getSQLState(); String errorState = ex.getSQLState();
if (errorStates.contains(errorState)) { int errorCode = ex.getErrorCode();
recoverable = Boolean.FALSE; if (subtype == StoreException.LOCK && "57033".equals(errorState)
if (subtype == StoreException.LOCK && errorState.equals("57033") && ex.getMessage().indexOf("80") != -1) {
&& ex.getMessage().indexOf("80") != -1) { return false;
recoverable = Boolean.TRUE;
} else if ((subtype == StoreException.QUERY &&
errorState.equals("57014")) &&
(ex.getErrorCode() == -952 || ex.getErrorCode() == -905)) {
recoverable = Boolean.TRUE;
}
} }
return recoverable; if ((subtype == StoreException.QUERY && "57014".equals(errorState) &&
(errorCode == -952 || errorCode == -905))) {
return false;
}
return super.isFatalException(subtype, ex);
} }
@Override @Override

View File

@ -110,6 +110,7 @@ import org.apache.openjpa.lib.util.Localizer.Message;
import org.apache.openjpa.meta.FieldMetaData; import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes; import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.ValueStrategies; import org.apache.openjpa.meta.ValueStrategies;
import org.apache.openjpa.util.ExceptionInfo;
import org.apache.openjpa.util.GeneralException; import org.apache.openjpa.util.GeneralException;
import org.apache.openjpa.util.InternalException; import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.InvalidStateException; import org.apache.openjpa.util.InvalidStateException;
@ -4777,10 +4778,9 @@ public class DBDictionary
* be determined by the implementation. This may take into account * be determined by the implementation. This may take into account
* DB-specific exception information in <code>causes</code>. * DB-specific exception information in <code>causes</code>.
*/ */
public OpenJPAException newStoreException(String msg, SQLException[] causes, public OpenJPAException newStoreException(String msg, SQLException[] causes, Object failed) {
Object failed) {
if (causes != null && causes.length > 0) { if (causes != null && causes.length > 0) {
OpenJPAException ret = narrow(msg, causes[0]); OpenJPAException ret = narrow(msg, causes[0], failed);
ret.setFailedObject(failed).setNestedThrowables(causes); ret.setFailedObject(failed).setNestedThrowables(causes);
return ret; return ret;
} }
@ -4789,36 +4789,29 @@ public class DBDictionary
} }
/** /**
* Gets the subtype of StoreException by matching the given SQLException's * Gets the category of StoreException by matching the given SQLException's
* error state code to the list of error codes supplied by the dictionary. * error state code to the list of error codes supplied by the dictionary.
* Returns -1 if no matching code can be found. *
* @return a StoreException of {@link ExceptionInfo#GENERAL general} category
* if the given SQL Exception can not be further categorized.
*
* @see #matchErrorState(Map, SQLException)
*/ */
OpenJPAException narrow(String msg, SQLException ex) { OpenJPAException narrow(String msg, SQLException ex, Object failed) {
Boolean recoverable = null; int errorType = matchErrorState(sqlStateCodes, ex);
int errorType = StoreException.GENERAL;
for (Integer type : sqlStateCodes.keySet()) {
Set<String> errorStates = sqlStateCodes.get(type);
if (errorStates != null) {
recoverable = matchErrorState(type, errorStates, ex);
if (recoverable != null) {
errorType = type;
break;
}
}
}
StoreException storeEx; StoreException storeEx;
switch (errorType) { switch (errorType) {
case StoreException.LOCK: case StoreException.LOCK:
storeEx = new LockException(msg); storeEx = new LockException(failed);
break; break;
case StoreException.OBJECT_EXISTS: case StoreException.OBJECT_EXISTS:
storeEx = new ObjectExistsException(msg); storeEx = new ObjectExistsException(msg);
break; break;
case StoreException.OBJECT_NOT_FOUND: case StoreException.OBJECT_NOT_FOUND:
storeEx = new ObjectNotFoundException(msg); storeEx = new ObjectNotFoundException(failed);
break; break;
case StoreException.OPTIMISTIC: case StoreException.OPTIMISTIC:
storeEx = new OptimisticException(msg); storeEx = new OptimisticException(failed);
break; break;
case StoreException.REFERENTIAL_INTEGRITY: case StoreException.REFERENTIAL_INTEGRITY:
storeEx = new ReferentialIntegrityException(msg); storeEx = new ReferentialIntegrityException(msg);
@ -4829,24 +4822,48 @@ public class DBDictionary
default: default:
storeEx = new StoreException(msg); storeEx = new StoreException(msg);
} }
if (recoverable != null) { storeEx.setFatal(isFatalException(errorType, ex));
storeEx.setFatal(!recoverable);
}
return storeEx; return storeEx;
} }
/** /**
* Determine if the SQLException argument matches any element in the * Determine the more appropriate type of store exception by matching the SQL Error State of the
* errorStates. Dictionary subclass can override this method and extract * the given SQLException to the given Error States categorized by error types.
* Dictionary subclass can override this method and extract
* SQLException data to figure out if the exception is recoverable. * SQLException data to figure out if the exception is recoverable.
* *
* @return null if no match is found or a Boolean value indicates the * @param errorStates classification of SQL error states by their specific nature. The keys of the
* exception is recoverable. * map represent one of the constants defined in {@link StoreException}. The value corresponding to
* a key represent the set of SQL Error States representing specific category of database error.
* This supplied map is sourced from <code>sql-error-state-codes.xml</xml> and filtered the
* error states for the current database.
*
* @param ex original SQL Exception as raised by the database driver.
*
* @return A constant indicating the category of error as defined in {@link StoreException}.
*/ */
protected Boolean matchErrorState(int subtype, Set<String> errorStates, protected int matchErrorState(Map<Integer,Set<String>> errorStates, SQLException ex) {
SQLException ex) {
String errorState = ex.getSQLState(); String errorState = ex.getSQLState();
return errorStates.contains(errorState) ? Boolean.FALSE : null; for (Map.Entry<Integer,Set<String>> states : errorStates.entrySet()) {
if (states.getValue().contains(errorState))
return states.getKey();
}
return StoreException.GENERAL;
}
/**
* Determine if the given SQL Exception is fatal or recoverable (such as a timeout).
* This implementation always returns true (i.e. all exceptions are fatal).
* The current dictionary implementation can overwrite this method to mark certain
* exception conditions as recoverable error.
* @param subtype A constant indicating the category of error as defined in {@link StoreException}.
* @param ex original SQL Exception as raised by the database driver.
*
* @return false if the error is fatal.
*/
protected boolean isFatalException(int subtype, SQLException ex) {
return true;
} }
/** /**

View File

@ -377,23 +377,17 @@ public class InformixDictionary
} }
@Override @Override
protected Boolean matchErrorState(int subtype, Set<String> errorStates, protected boolean isFatalException(int subtype, SQLException ex) {
SQLException ex) {
Boolean recoverable = null; // SQL State of IX000 is a general purpose Informix error code
String errorState = ex.getSQLState(); // category, so only return Boolean.TRUE if we match SQL Codes
if (errorStates.contains(errorState)) { // recoverable = Boolean.FALSE;
// SQL State of IX000 is a general purpose Informix error code if ((subtype == StoreException.LOCK && ex.getErrorCode() == -154)
// category, so only return Boolean.TRUE if we match SQL Codes ||(subtype == StoreException.QUERY && ex.getErrorCode() == -213)) {
// recoverable = Boolean.FALSE; return false;
if (subtype == StoreException.LOCK &&
ex.getErrorCode() == -154) {
recoverable = Boolean.TRUE;
} else if (subtype == StoreException.QUERY &&
ex.getErrorCode() == -213) {
recoverable = Boolean.TRUE;
}
} }
return recoverable;
return super.isFatalException(subtype, ex);
} }
} }

View File

@ -1259,25 +1259,21 @@ public class OracleDictionary
} }
@Override @Override
protected Boolean matchErrorState(int subtype, Set<String> errorStates, protected boolean isFatalException(int subtype, SQLException ex) {
SQLException ex) {
Boolean recoverable = null;
String errorState = ex.getSQLState(); String errorState = ex.getSQLState();
int errorCode = ex.getErrorCode(); int errorCode = ex.getErrorCode();
if (errorStates.contains(errorState)) { if ((subtype == StoreException.LOCK)
recoverable = Boolean.FALSE; && (("61000".equals(errorState) && (errorCode == 54 ||
if ((subtype == StoreException.LOCK) errorCode == 60 || errorCode == 4020 ||
&& ((errorState.equals("61000") && (errorCode == 54 || errorCode == 4021 || errorCode == 4022))
errorCode == 60 || errorCode == 4020 || || ("42000".equals(errorState) && errorCode == 2049))) {
errorCode == 4021 || errorCode == 4022)) return false;
|| (errorState.equals("42000") && errorCode == 2049))) {
recoverable = Boolean.TRUE;
} else if (subtype == StoreException.QUERY &&
errorState.equals("72000") && errorCode == 1013) {
recoverable = Boolean.TRUE;
}
} }
return recoverable; if (subtype == StoreException.QUERY &&
"72000".equals(errorState) && errorCode == 1013) {
return false;
}
return super.isFatalException(subtype, ex);
} }
@Override @Override

View File

@ -29,7 +29,7 @@
<sql-state-codes> <sql-state-codes>
<dictionary class="org.apache.openjpa.jdbc.sql.DB2Dictionary"> <dictionary class="org.apache.openjpa.jdbc.sql.DB2Dictionary">
<lock>40001,57033,57011</lock> <lock>40001,57033,57011,57014</lock>
<referential-integrity>23502,42912,23001,23504,23511,23512,23513,23515,23520</referential-integrity> <referential-integrity>23502,42912,23001,23504,23511,23512,23513,23515,23520</referential-integrity>
<object-exists>23505</object-exists> <object-exists>23505</object-exists>
<object-not-found></object-not-found> <object-not-found></object-not-found>
@ -155,12 +155,12 @@
</dictionary> </dictionary>
<dictionary class="org.apache.openjpa.jdbc.sql.MySQLDictionary"> <dictionary class="org.apache.openjpa.jdbc.sql.MySQLDictionary">
<lock>1205,1213</lock> <lock>41000</lock>
<referential-integrity>630,839,840,893,1062,1169,1215,1216,1217,1451,1452,1557</referential-integrity> <referential-integrity>630,839,840,893,1062,1169,1215,1216,1217,1451,1452,1557</referential-integrity>
<object-exists>23000</object-exists> <object-exists>23000</object-exists>
<object-not-found></object-not-found> <object-not-found></object-not-found>
<optimistic>41000</optimistic> <optimistic></optimistic>
<query></query> <query>70100</query>
</dictionary> </dictionary>
<dictionary class="org.apache.openjpa.jdbc.sql.OracleDictionary"> <dictionary class="org.apache.openjpa.jdbc.sql.OracleDictionary">

View File

@ -246,10 +246,29 @@ public class Filters {
return false; return false;
} }
/**
* Convert the given value to match the given (presumably a setter) method argument type.
*
* @param o given value
* @param method a presumably setter method
*
* @return the same value if the method does not have one and only one input argument.
*/
public static Object convertToMatchMethodArgument(Object o, Method method) {
if (method == null || method.getParameterTypes().length != 1) {
return o;
}
return convert(o, method.getParameterTypes()[0], true);
}
public static Object convert(Object o, Class<?> type) {
return convert(o, type, false);
}
/** /**
* Convert the given value to the given type. * Convert the given value to the given type.
*/ */
public static Object convert(Object o, Class<?> type) { public static Object convert(Object o, Class<?> type, boolean strictNumericConversion) {
if (o == null) if (o == null)
return null; return null;
if (o.getClass() == type) if (o.getClass() == type)
@ -315,13 +334,13 @@ public class Filters {
throw new ClassCastException(_loc.get("cant-convert", o, throw new ClassCastException(_loc.get("cant-convert", o,
o.getClass(), type).getMessage()); o.getClass(), type).getMessage());
if (type == Integer.class) { if (type == Integer.class && allowNumericConversion(o.getClass(), type, strictNumericConversion)) {
return ((Number) o).intValue(); return ((Number) o).intValue();
} else if (type == Float.class) { } else if (type == Float.class && allowNumericConversion(o.getClass(), type, strictNumericConversion)) {
return new Float(((Number) o).floatValue()); return new Float(((Number) o).floatValue());
} else if (type == Double.class) { } else if (type == Double.class) {
return new Double(((Number) o).doubleValue()); return new Double(((Number) o).doubleValue());
} else if (type == Long.class) { } else if (type == Long.class && allowNumericConversion(o.getClass(), type, strictNumericConversion)) {
return ((Number) o).longValue(); return ((Number) o).longValue();
} else if (type == BigDecimal.class) { } else if (type == BigDecimal.class) {
// the BigDecimal constructor doesn't handle the // the BigDecimal constructor doesn't handle the
@ -339,15 +358,29 @@ public class Filters {
return new BigDecimal(o.toString()); return new BigDecimal(o.toString());
} else if (type == BigInteger.class) { } else if (type == BigInteger.class) {
return new BigInteger(o.toString()); return new BigInteger(o.toString());
} else if (type == Short.class) { } else if (type == Short.class && allowNumericConversion(o.getClass(), type, strictNumericConversion)) {
return new Short(((Number) o).shortValue()); return new Short(((Number) o).shortValue());
} else if (type == Byte.class) { } else if (type == Byte.class && allowNumericConversion(o.getClass(), type, strictNumericConversion)) {
return new Byte(((Number) o).byteValue()); return new Byte(((Number) o).byteValue());
} else { } else if (!strictNumericConversion) {
return ((Number) o).intValue(); return ((Number) o).intValue();
} else {
throw new ClassCastException(_loc.get("cant-convert", o, o.getClass(), type).getMessage());
} }
} }
private static boolean allowNumericConversion(Class<?> actual, Class<?> target, boolean strict) {
if (!strict || actual == target)
return true;
if (actual == Byte.class) return false;
if (actual == Double.class) return target == Float.class;
if (actual == Float.class) return target == Double.class;
if (actual == Integer.class) return target == Long.class || target == Short.class;
if (actual == Long.class) return target == Integer.class || target == Short.class;
if (actual == Short.class) return target == Long.class || target == Integer.class;
return false;
}
/** /**
* Add the given values. * Add the given values.
*/ */

View File

@ -30,13 +30,13 @@ import org.apache.openjpa.lib.util.Localizer;
* @author Marc Prud'hommeaux * @author Marc Prud'hommeaux
* @since 0.3.1 * @since 0.3.1
*/ */
@SuppressWarnings("serial")
public class LockException public class LockException
extends StoreException { extends StoreException {
private static final transient Localizer _loc = Localizer.forPackage private static final transient Localizer _loc = Localizer.forPackage(LockException.class);
(LockException.class);
private int timeout = -1; private int timeout = -1;
private int lockLevel = -1; private int lockLevel = -1;
public LockException(Object failed) { public LockException(Object failed) {
@ -49,8 +49,7 @@ public class LockException
} }
public LockException(Object failed, int timeout, int lockLevel) { public LockException(Object failed, int timeout, int lockLevel) {
super(_loc.get("lock-timeout", Exceptions.toString(failed), super(_loc.get("lock-timeout", Exceptions.toString(failed), String.valueOf(timeout)));
String.valueOf(timeout)));
setFailedObject(failed); setFailedObject(failed);
setTimeout(timeout); setTimeout(timeout);
} }
@ -86,8 +85,7 @@ public class LockException
String str = super.toString(); String str = super.toString();
if (timeout < 0) if (timeout < 0)
return str; return str;
return str + Exceptions.SEP + "Timeout: " + timeout + ", LockLevel" return str + Exceptions.SEP + "Timeout: " + timeout + ", LockLevel" + lockLevel;
+ lockLevel;
} }
private void writeObject(ObjectOutputStream out) private void writeObject(ObjectOutputStream out)

View File

@ -35,6 +35,7 @@ import org.apache.openjpa.lib.util.Localizer.Message;
* @author Abe White * @author Abe White
* @since 0.4.0 * @since 0.4.0
*/ */
@SuppressWarnings("serial")
public abstract class OpenJPAException public abstract class OpenJPAException
extends RuntimeException extends RuntimeException
implements Serializable, ExceptionInfo { implements Serializable, ExceptionInfo {

View File

@ -26,6 +26,7 @@ import org.apache.openjpa.lib.util.Localizer.Message;
* @author Marc Prud'hommeaux * @author Marc Prud'hommeaux
* @since 0.2.5 * @since 0.2.5
*/ */
@SuppressWarnings("serial")
public class StoreException public class StoreException
extends OpenJPAException { extends OpenJPAException {

View File

@ -0,0 +1,146 @@
/*
* 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.query;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.LockTimeoutException;
import javax.persistence.Query;
import javax.persistence.QueryTimeoutException;
import junit.framework.AssertionFailedError;
import org.apache.openjpa.persistence.exception.PObject;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
/**
* Tests that correct timeout exceptions are being thrown depending on whether it is a query or a lock operation.
*
* @author Pinaki Poddar
*
*/
public class TestTimeoutException extends SingleEMFTestCase {
private final Class<?> entityClass = PObject.class;
private final ExecutorService scheduler = Executors.newCachedThreadPool();
public void setUp() {
super.setUp(entityClass);
}
public void testQueryTimeOutExceptionWhileQueryingWithLocksOnAlreadyLockedEntities() {
EntityManager em1 = emf.createEntityManager();
EntityManager em2 = emf.createEntityManager();
assertNotSame(em1, em2);
Object oid = createEntity(em1);
em1.getTransaction().begin();
Object entity = em1.find(entityClass, oid);
assertNotNull(entity);
em1.lock(entity, LockModeType.PESSIMISTIC_WRITE);
em2.getTransaction().begin();
final Query query = em2.createQuery("select p from PObject p");
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
long timeout = 1000;
query.setHint("javax.persistence.query.timeout", timeout);
assertError(new Callable<Throwable>() {
public Throwable call() throws Exception {
try {
query.getResultList();
} catch (Throwable t) {
return t;
}
return null;
}
}, QueryTimeoutException.class, timeout);
assertTrue(em2.getTransaction().isActive());
em2.getTransaction().rollback();
em1.getTransaction().rollback();
}
public void testLockTimeOutExceptionWhileLockingAlreadyLockedEntities() {
EntityManager em1 = emf.createEntityManager();
final EntityManager em2 = emf.createEntityManager();
assertNotSame(em1, em2);
final Object oid = createEntity(em1);
em1.getTransaction().begin();
final Object entity1 = em1.find(entityClass, oid);
assertNotNull(entity1);
em1.lock(entity1, LockModeType.PESSIMISTIC_WRITE);
em2.getTransaction().begin();
final Object entity2 = em2.find(entityClass, oid);
final long timeout = 1000;
assertError(new Callable<Throwable>() {
public Throwable call() throws Exception {
try {
Map<String,Object> hint = new HashMap<String, Object>();
hint.put("javax.persistence.lock.timeout", timeout);
em2.lock(entity2, LockModeType.PESSIMISTIC_WRITE, hint);
} catch (Throwable t) {
return t;
}
return null;
}
}, LockTimeoutException.class, timeout);
assertTrue(em2.getTransaction().isActive());
em2.getTransaction().rollback();
em1.getTransaction().rollback();
}
public Object createEntity(EntityManager em) {
long id = System.nanoTime();
em.getTransaction().begin();
PObject pc = new PObject();
pc.setId(id);
em.persist(pc);
em.getTransaction().commit();
return id;
}
/**
* Assert that an exception of proper type has been thrown by the given task within the given timeout.
* @param t
* @param expeceted
*/
void assertError(Callable<Throwable> task, Class<? extends Throwable> expected, long timeout) {
try {
Future<Throwable> future = scheduler.submit(task);
Throwable error = future.get();
if (error == null) {
throw new AssertionFailedError("No exception was raised but expected " + expected.getName());
} else if (!expected.isAssignableFrom(error.getClass())) {
error.printStackTrace();
throw new AssertionFailedError(error.getClass().getName() + " was raised but expected " +
expected.getName());
}
} catch (Throwable t) {
t.printStackTrace();
throw new AssertionFailedError(t.getClass().getName() + " was raised but expected " +
expected.getName());
}
}
}

View File

@ -30,6 +30,7 @@ import javax.persistence.QueryTimeoutException;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration; import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.sql.DBDictionary; import org.apache.openjpa.jdbc.sql.DBDictionary;
import org.apache.openjpa.persistence.LockTimeoutException;
import org.apache.openjpa.persistence.test.SQLListenerTestCase; import org.apache.openjpa.persistence.test.SQLListenerTestCase;
/** /**
@ -40,6 +41,7 @@ public class TestPessimisticLocks extends SQLListenerTestCase {
private DBDictionary dict = null; private DBDictionary dict = null;
public void setUp() { public void setUp() {
setUp(Employee.class, Department.class, "openjpa.LockManager", "mixed");
setSupportedDatabases( setSupportedDatabases(
org.apache.openjpa.jdbc.sql.DerbyDictionary.class, org.apache.openjpa.jdbc.sql.DerbyDictionary.class,
// org.apache.openjpa.jdbc.sql.OracleDictionary.class, // org.apache.openjpa.jdbc.sql.OracleDictionary.class,
@ -48,7 +50,6 @@ public class TestPessimisticLocks extends SQLListenerTestCase {
return; return;
} }
setUp(Employee.class, Department.class, "openjpa.LockManager", "mixed");
String empTable = getMapping(Employee.class).getTable().getFullName(); String empTable = getMapping(Employee.class).getTable().getFullName();
String deptTable = getMapping(Department.class).getTable().getFullName(); String deptTable = getMapping(Department.class).getTable().getFullName();
@ -125,10 +126,11 @@ public class TestPessimisticLocks extends SQLListenerTestCase {
// find Employee(2) with a lock, should block and expected a PessimisticLockException // find Employee(2) with a lock, should block and expected a PessimisticLockException
em2.find(Employee.class, 2, LockModeType.PESSIMISTIC_READ, map); em2.find(Employee.class, 2, LockModeType.PESSIMISTIC_READ, map);
fail("Unexcpected find succeeded. Should throw a PessimisticLockException."); fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
} catch (QueryTimeoutException e) { } catch (LockTimeoutException e) {
// TODO: DB2: This is the current unexpected exception due to OPENJPA-991. // TODO: DB2: This is the current unexpected exception due to OPENJPA-991.
// Remove this when the problem is fixed // Remove this when the problem is fixed
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage()); System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage() + " Failed "
+ e.getFailedObject());
} catch (PessimisticLockException e) { } catch (PessimisticLockException e) {
// TODO: This is the expected exception but will be fixed under OPENJPA-991 // TODO: This is the expected exception but will be fixed under OPENJPA-991
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage()); // System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());
@ -208,10 +210,11 @@ public class TestPessimisticLocks extends SQLListenerTestCase {
// find Employee(2) with a lock, should block and expected a PessimisticLockException // find Employee(2) with a lock, should block and expected a PessimisticLockException
em2.find(Employee.class, 2, LockModeType.PESSIMISTIC_READ, map); em2.find(Employee.class, 2, LockModeType.PESSIMISTIC_READ, map);
fail("Unexcpected find succeeded. Should throw a PessimisticLockException."); fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
} catch (QueryTimeoutException e) { } catch (LockTimeoutException e) {
// TODO: DB2: This is the current unexpected exception due to OPENJPA-991. // TODO: DB2: This is the current unexpected exception due to OPENJPA-991.
// Remove this when the problem is fixed // Remove this when the problem is fixed
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage()); System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage() + " Failed " +
e.getFailedObject());
} catch (PessimisticLockException e) { } catch (PessimisticLockException e) {
// TODO: This is the expected exception but will be fixed under OPENJPA-991 // TODO: This is the expected exception but will be fixed under OPENJPA-991
// System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage()); // System.out.println("Caught " + e.getClass().getName() + ":" + e.getMessage());

View File

@ -35,6 +35,7 @@ import org.apache.openjpa.util.StoreException;
* @since 2.0.0 * @since 2.0.0
* @nojavadoc * @nojavadoc
*/ */
@SuppressWarnings("serial")
public class LockTimeoutException public class LockTimeoutException
extends javax.persistence.LockTimeoutException extends javax.persistence.LockTimeoutException
implements Serializable, ExceptionInfo { implements Serializable, ExceptionInfo {
@ -43,12 +44,10 @@ public class LockTimeoutException
private transient Object _failed = null; private transient Object _failed = null;
private transient Throwable[] _nested = null; private transient Throwable[] _nested = null;
public LockTimeoutException(String msg, Throwable[] nested, public LockTimeoutException(String msg, Throwable[] nested, Object failed) {
Object failed, boolean fatal) {
super(msg); super(msg);
_nested = nested; _nested = nested;
_failed = failed; _failed = failed;
_fatal = fatal;
} }
public int getType() { public int getType() {

View File

@ -151,63 +151,40 @@ public class PersistenceExceptions
*/ */
private static Throwable translateStoreException(OpenJPAException ke) { private static Throwable translateStoreException(OpenJPAException ke) {
Exception e; Exception e;
int subtype = ke.getSubtype();
String msg = ke.getMessage();
Throwable[] nested = getNestedThrowables(ke);
Object failed = getFailedObject(ke);
boolean fatal = ke.isFatal();
Throwable cause = (ke.getNestedThrowables() != null Throwable cause = (ke.getNestedThrowables() != null
&& ke.getNestedThrowables().length == 1) && ke.getNestedThrowables().length == 1)
? ke.getNestedThrowables()[0] : null; ? ke.getNestedThrowables()[0] : null;
if (ke.getSubtype() == StoreException.OBJECT_NOT_FOUND if (subtype == StoreException.OBJECT_NOT_FOUND || cause instanceof ObjectNotFoundException) {
|| cause instanceof ObjectNotFoundException) { e = new org.apache.openjpa.persistence.EntityNotFoundException(msg, nested, failed, fatal);
e = new org.apache.openjpa.persistence.EntityNotFoundException } else if (subtype == StoreException.OPTIMISTIC || cause instanceof OptimisticException) {
(ke.getMessage(), getNestedThrowables(ke), e = new org.apache.openjpa.persistence.OptimisticLockException(msg, nested, failed, fatal);
getFailedObject(ke), ke.isFatal()); } else if (subtype == StoreException.LOCK || cause instanceof LockException) {
} else if (ke.getSubtype() == StoreException.OPTIMISTIC LockException lockEx = (LockException) (ke instanceof LockException ? ke : cause);
|| cause instanceof OptimisticException) { if (lockEx != null && lockEx.getLockLevel() >= MixedLockLevels.LOCK_PESSIMISTIC_READ) {
e = new org.apache.openjpa.persistence.OptimisticLockException
(ke.getMessage(), getNestedThrowables(ke),
getFailedObject(ke), ke.isFatal());
} else if (ke.getSubtype() == StoreException.LOCK
|| cause instanceof LockException) {
LockException lockEx = (LockException)
(ke instanceof LockException ? ke : cause);
if (lockEx != null && lockEx.getLockLevel() >=
MixedLockLevels.LOCK_PESSIMISTIC_READ) {
if (!lockEx.isFatal()) { if (!lockEx.isFatal()) {
e = new org.apache.openjpa.persistence e = new org.apache.openjpa.persistence.LockTimeoutException(msg, nested, failed);
.LockTimeoutException(
ke.getMessage(), getNestedThrowables(ke),
getFailedObject(ke), ke.isFatal());
} else { } else {
e = new org.apache.openjpa.persistence e = new org.apache.openjpa.persistence.PessimisticLockException(msg, nested, failed);
.PessimisticLockException(
ke.getMessage(), getNestedThrowables(ke),
getFailedObject(ke), ke.isFatal());
} }
} else { } else {
e = new org.apache.openjpa.persistence.OptimisticLockException( e = new org.apache.openjpa.persistence.OptimisticLockException(msg, nested, failed, fatal);
ke.getMessage(), getNestedThrowables(ke),
getFailedObject(ke), ke.isFatal());
} }
} else if (ke.getSubtype() == StoreException.OBJECT_EXISTS } else if (subtype == StoreException.OBJECT_EXISTS || cause instanceof ObjectExistsException) {
|| cause instanceof ObjectExistsException) { e = new org.apache.openjpa.persistence.EntityExistsException(msg, nested, failed, fatal);
e = new org.apache.openjpa.persistence.EntityExistsException } else if (subtype == StoreException.QUERY || cause instanceof QueryException) {
(ke.getMessage(), getNestedThrowables(ke), QueryException queryEx = (QueryException) (ke instanceof QueryException ? ke : cause);
getFailedObject(ke), ke.isFatal());
} else if (ke.getSubtype() == StoreException.QUERY
|| cause instanceof QueryException) {
QueryException queryEx = (QueryException)
(ke instanceof QueryException ? ke : cause);
if (!queryEx.isFatal()) { if (!queryEx.isFatal()) {
e = new org.apache.openjpa.persistence.QueryTimeoutException( e = new org.apache.openjpa.persistence.QueryTimeoutException(msg, nested, failed, false);
ke.getMessage(), getNestedThrowables(ke),
getFailedObject(ke), ke.isFatal());
} else { } else {
e = new org.apache.openjpa.persistence.PersistenceException( e = new org.apache.openjpa.persistence.PersistenceException(msg, nested, failed, true);
ke.getMessage(), getNestedThrowables(ke),
getFailedObject(ke), ke.isFatal());
} }
} else { } else {
e = new org.apache.openjpa.persistence.PersistenceException e = new org.apache.openjpa.persistence.PersistenceException(msg, nested, failed, fatal);
(ke.getMessage(), getNestedThrowables(ke),
getFailedObject(ke), ke.isFatal());
} }
e.setStackTrace(ke.getStackTrace()); e.setStackTrace(ke.getStackTrace());
return e; return e;

View File

@ -31,24 +31,24 @@ import org.apache.openjpa.util.StoreException;
/** /**
* Pessimistic concurrency violation. * Pessimistic concurrency violation.
* This exception is always fatal in contrast to {@linkplain LockTimeoutException}.
* *
* @since 2.0.0 * @since 2.0.0
* @nojavadoc * @nojavadoc
*/ */
@SuppressWarnings("serial")
public class PessimisticLockException public class PessimisticLockException
extends javax.persistence.PessimisticLockException extends javax.persistence.PessimisticLockException
implements Serializable, ExceptionInfo { implements Serializable, ExceptionInfo {
private transient boolean _fatal = false; private transient boolean _fatal = true;
private transient Object _failed = null; private transient Object _failed = null;
private transient Throwable[] _nested = null; private transient Throwable[] _nested = null;
public PessimisticLockException(String msg, Throwable[] nested, public PessimisticLockException(String msg, Throwable[] nested, Object failed) {
Object failed, boolean fatal) {
super(msg); super(msg);
_nested = nested; _nested = nested;
_failed = failed; _failed = failed;
_fatal = fatal;
} }
public int getType() { public int getType() {

View File

@ -293,7 +293,7 @@ public class QueryImpl<X> implements OpenJPAQuerySPI<X>, Serializable {
} }
return result; return result;
} catch (LockTimeoutException e) { } catch (LockTimeoutException e) {
throw new QueryTimeoutException(e.getMessage(), new Throwable[]{e}, getQueryString(), e.isFatal()); throw new QueryTimeoutException(e.getMessage(), new Throwable[]{e}, this);
} finally { } finally {
unlock(); unlock();
} }

View File

@ -35,6 +35,7 @@ import org.apache.openjpa.util.StoreException;
* @since 2.0.0 * @since 2.0.0
* @nojavadoc * @nojavadoc
*/ */
@SuppressWarnings("serial")
public class QueryTimeoutException public class QueryTimeoutException
extends javax.persistence.QueryTimeoutException extends javax.persistence.QueryTimeoutException
implements Serializable, ExceptionInfo { implements Serializable, ExceptionInfo {
@ -43,8 +44,11 @@ public class QueryTimeoutException
private transient Object _failed = null; private transient Object _failed = null;
private transient Throwable[] _nested = null; private transient Throwable[] _nested = null;
public QueryTimeoutException(String msg, Throwable[] nested, public QueryTimeoutException(String msg, Throwable[] nested, Object failed) {
Object failed, boolean fatal) { this(msg, nested, failed, false);
}
public QueryTimeoutException(String msg, Throwable[] nested, Object failed, boolean fatal) {
super(msg); super(msg);
_nested = nested; _nested = nested;
_failed = failed; _failed = failed;