From 22a0f837e110d422b945e80148e2c0f85412fef1 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 24 Feb 2009 18:48:09 +0000 Subject: [PATCH] OPENJPA-891 - Committing basic JPA2 lock mode support for em refresh, find and lock methods. git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@747489 13f79535-47bb-0310-9956-ffa450edef68 --- .../openjpa/jdbc/kernel/JDBCStoreManager.java | 4 +- .../jdbc/kernel/PessimisticLockManager.java | 15 +- .../openjpa/jdbc/sql/DB2Dictionary.java | 21 + .../apache/openjpa/jdbc/sql/DBDictionary.java | 79 +- .../openjpa/jdbc/sql/DerbyDictionary.java | 16 + .../openjpa/jdbc/sql/SQLExceptions.java | 25 +- .../jdbc/sql/sql-error-state-codes.xml | 37 +- .../openjpa/kernel/VersionLockManager.java | 5 + .../apache/openjpa/util/LockException.java | 16 +- .../apache/openjpa/util/StoreException.java | 10 + .../openjpa/jdbc/kernel/TestInsertOrder.java | 4 +- .../persistence/lockmgr/LockEmployee.java | 87 + .../lockmgr/LockManagerTestBase.java | 818 ++++++++ .../persistence/lockmgr/LockStory.java | 63 + .../openjpa/persistence/lockmgr/LockTask.java | 76 + .../lockmgr/TestJPA2LockManager.java | 1697 +++++++++++++++++ .../lockmgr/TestOptimisticLockManager.java | 893 +++++++++ .../lockmgr/TestPessimisticLockManager.java | 1566 +++++++++++++++ .../persistence/test/AllowFailure.java | 1 + .../persistence/test/PersistenceTestCase.java | 15 +- .../persistence/test/SQLListenerTestCase.java | 187 +- openjpa-persistence/pom.xml | 5 + .../persistence/EntityManagerImpl.java | 245 ++- .../openjpa/persistence/JPA2LockLevels.java | 56 + .../openjpa/persistence/JPA2LockManager.java | 69 + .../persistence/LockTimeoutException.java | 112 ++ .../persistence/OptimisticLockException.java | 2 +- .../persistence/PersistenceExceptions.java | 26 +- .../PersistenceProductDerivation.java | 31 +- .../persistence/PessimisticLockException.java | 112 ++ .../persistence/QueryTimeoutException.java | 110 ++ .../src/doc/manual/ref_guide_conf.xml | 16 +- 32 files changed, 6287 insertions(+), 132 deletions(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockEmployee.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockManagerTestBase.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockStory.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockTask.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestJPA2LockManager.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestOptimisticLockManager.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLockManager.java create mode 100644 openjpa-persistence/src/main/java/org/apache/openjpa/persistence/JPA2LockLevels.java create mode 100644 openjpa-persistence/src/main/java/org/apache/openjpa/persistence/JPA2LockManager.java create mode 100644 openjpa-persistence/src/main/java/org/apache/openjpa/persistence/LockTimeoutException.java create mode 100644 openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PessimisticLockException.java create mode 100644 openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryTimeoutException.java diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java index f49c3e9c5..3c7c4b353 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java @@ -52,7 +52,6 @@ import org.apache.openjpa.jdbc.sql.Union; import org.apache.openjpa.kernel.BrokerImpl; import org.apache.openjpa.kernel.FetchConfiguration; import org.apache.openjpa.kernel.FinderCache; -import org.apache.openjpa.kernel.FinderQuery; import org.apache.openjpa.kernel.LockManager; import org.apache.openjpa.kernel.OpenJPAStateManager; import org.apache.openjpa.kernel.PCState; @@ -270,7 +269,8 @@ public class JDBCStoreManager } catch (ClassNotFoundException cnfe) { throw new UserException(cnfe); } catch (SQLException se) { - throw SQLExceptions.getStore(se, _dict); + throw SQLExceptions.getStoreSQLException(sm, se, _dict, + fetch.getReadLockLevel()); } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java index e08fbdea9..616404df7 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java @@ -97,16 +97,16 @@ public class PessimisticLockManager // only need to lock if not loaded from locking result ConnectionInfo info = (ConnectionInfo) sdata; if (info == null || info.result == null || !info.result.isLocking()) - lockRow(sm, timeout); + lockRow(sm, timeout, level); } - super.lockInternal(sm, level, timeout, sdata); + optimisticLockInternal(sm, level, timeout, sdata); } /** * Lock the specified instance row by issuing a "SELECT ... FOR UPDATE" * statement. */ - private void lockRow(OpenJPAStateManager sm, int timeout) { + private void lockRow(OpenJPAStateManager sm, int timeout, int level) { // assert that the dictionary supports the "SELECT ... FOR UPDATE" // construct; if not, and we the assertion does not throw an // exception, then just return without locking @@ -135,9 +135,10 @@ public class PessimisticLockManager stmnt = prepareStatement(conn, sql); setTimeout(stmnt, timeout); rs = executeQuery(conn, stmnt, sql); - checkLock(rs, sm); + checkLock(rs, sm, timeout); } catch (SQLException se) { - throw SQLExceptions.getStore(se, dict); + throw SQLExceptions.getStoreSQLException(sm, se, dict, + level); } finally { if (stmnt != null) try { stmnt.close(); } catch (SQLException se) {} @@ -204,10 +205,10 @@ public class PessimisticLockManager * This method is to provide override for non-JDBC or JDBC-like * implementation of checking lock from the result set. */ - protected void checkLock(ResultSet rs, OpenJPAStateManager sm) + protected void checkLock(ResultSet rs, OpenJPAStateManager sm, int timeout) throws SQLException { if (!rs.next()) - throw new LockException(sm.getManagedInstance()); + throw new LockException(sm.getManagedInstance(), timeout); return; } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java index e7e47fbf3..39e2dd6e9 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java @@ -24,6 +24,7 @@ import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.sql.Types; import java.util.Arrays; +import java.util.Set; import java.util.StringTokenizer; import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration; @@ -40,6 +41,7 @@ import org.apache.openjpa.kernel.Filters; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.meta.JavaTypes; import org.apache.openjpa.util.OpenJPAException; +import org.apache.openjpa.util.StoreException; import org.apache.openjpa.util.UnsupportedException; import serp.util.Strings; @@ -325,6 +327,10 @@ public class DB2Dictionary } } + public boolean supportIsolationForUpdate() { + return true; + } + /** * Get the update clause for the query based on the * updateClause and isolationLevel hints @@ -832,4 +838,19 @@ public class DB2Dictionary String nullSafe(String s) { return s == null ? "" : s; } + + @Override + protected Boolean matchErrorState(int subtype, Set errorStates, + SQLException ex) { + Boolean recoverable = null; + String errorState = ex.getSQLState(); + if (errorStates.contains(errorState)) { + recoverable = Boolean.FALSE; + if (subtype == StoreException.LOCK && errorState.equals("57033") + && ex.getMessage().indexOf("80") != -1) { + recoverable = Boolean.TRUE; + } + } + return recoverable; + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java index 70b4bf408..e6c755c79 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java @@ -2399,6 +2399,14 @@ public class DBDictionary } } + /** + * Return true if the dictionary uses isolation level to compute the + * returned getForUpdateClause() SQL clause. + */ + public boolean supportIsolationForUpdate() { + return false; + } + /** * Return the "SELECT" operation clause, adding any available hints, etc. */ @@ -4220,29 +4228,56 @@ public class DBDictionary * 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 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); + Boolean recoverable = null; + int errorType = StoreException.GENERAL; + for (Integer type : sqlStateCodes.keySet()) { + Set errorStates = sqlStateCodes.get(type); + if (errorStates != null) { + recoverable = matchErrorState(type, errorStates, ex); + if (recoverable != null) { + errorType = type; + break; + } + } } + StoreException storeEx; + switch (errorType) { + case StoreException.LOCK: + storeEx = new LockException(msg); + break; + case StoreException.OBJECT_EXISTS: + storeEx = new ObjectExistsException(msg); + break; + case StoreException.OBJECT_NOT_FOUND: + storeEx = new ObjectNotFoundException(msg); + break; + case StoreException.OPTIMISTIC: + storeEx = new OptimisticException(msg); + break; + case StoreException.REFERENTIAL_INTEGRITY: + storeEx = new ReferentialIntegrityException(msg); + break; + default: + storeEx = new StoreException(msg); + } + if (recoverable != null) { + storeEx.setRecoverable(recoverable); + } + return storeEx; + } + + /* + * Determine if the SQLException argument matches any element in the + * errorStates. Dictionary subclass can override this method and extract + * SQLException data to figure out if the exception is recoverable. + * + * @return null if no match is found or a Boolean value indicates the + * exception is recoverable. + */ + protected Boolean matchErrorState(int subtype, Set errorStates, + SQLException ex) { + String errorState = ex.getSQLState(); + return errorStates.contains(errorState) ? Boolean.FALSE : null; } /** diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java index 7437e1de4..761a58a21 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java @@ -23,6 +23,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; import javax.sql.DataSource; @@ -103,4 +104,19 @@ public class DerbyDictionary } } } + + @Override + protected Boolean matchErrorState(int subtype, Set errorStates, + SQLException ex) { + Boolean recoverable = null; + String errorState = ex.getSQLState(); + int errorCode = ex.getErrorCode(); + if (errorStates.contains(errorState)) { + recoverable = Boolean.FALSE; + if (subtype == StoreException.LOCK && errorCode < 30000) { + recoverable = Boolean.TRUE; + } + } + return recoverable; + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java index cfd6934e8..b5ba00991 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java @@ -22,13 +22,12 @@ import java.sql.SQLException; import java.util.LinkedList; import java.util.List; +import org.apache.openjpa.conf.OpenJPAConfiguration; +import org.apache.openjpa.kernel.LockLevels; +import org.apache.openjpa.kernel.OpenJPAStateManager; import org.apache.openjpa.lib.util.Localizer.Message; 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.StoreException; /** @@ -117,4 +116,22 @@ public class SQLExceptions { } return (SQLException[]) errs.toArray(new SQLException[errs.size()]); } + + public static OpenJPAException getStoreSQLException(OpenJPAStateManager sm, + SQLException se, DBDictionary dict, int level) { + return getStoreSQLException(sm.getContext().getConfiguration(), se, + dict, level); + } + + public static OpenJPAException getStoreSQLException( + OpenJPAConfiguration config, SQLException se, DBDictionary dict, + int level) { + OpenJPAException storeEx = SQLExceptions.getStore(se, dict); + String lm = config.getLockManager(); + if (storeEx.getSubtype() == StoreException.LOCK) { + LockException lockEx = (LockException) storeEx; + lockEx.setLockLevel(level); + } + return storeEx; + } } diff --git a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml index 5a5327723..d9af606d0 100644 --- a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml +++ b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml @@ -29,24 +29,26 @@ - -911,-913 - -407,-530,-531,-532,-543,-544,-545,-603,-667,-803 + 40001,57033,57014 + 23502,42912,23001,23504,23511,23512,23513,23515,23520,23505 - 40001 + 40001,40XL1,40XL2 22001,22005,23502,23503,23513,X0Y32 23505 - 40XL1,40001 + 1205 544,2601,2627,8114,8115 + + 1205 @@ -91,24 +93,51 @@ + 22003,22012,22025,23000,23001 + + + + -9 + + + + -239,-268,-692,-11030 + + + + + + + + + + + + + + + + + + diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionLockManager.java b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionLockManager.java index 1d5d15cde..694c47d8d 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionLockManager.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionLockManager.java @@ -95,6 +95,11 @@ public class VersionLockManager */ protected void lockInternal(OpenJPAStateManager sm, int level, int timeout, Object sdata) { + optimisticLockInternal(sm, level, timeout, sdata); + } + + protected void optimisticLockInternal(OpenJPAStateManager sm, int level, + int timeout, Object sdata) { // Set lock level first to prevent infinite recursion with // transactional(..) call setLockLevel(sm, level); diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/LockException.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/LockException.java index 82fa32a09..cbf7ab226 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/LockException.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/LockException.java @@ -37,7 +37,8 @@ public class LockException (LockException.class); private int timeout = -1; - + private int lockLevel = -1; + public LockException(Object failed) { super(_loc.get("lock-failed", Exceptions.toString(failed))); setFailedObject(failed); @@ -69,20 +70,31 @@ public class LockException return this; } + public void setLockLevel(int lockLevel) { + this.lockLevel = lockLevel; + } + + public int getLockLevel() { + return lockLevel; + } + public String toString() { String str = super.toString(); if (timeout < 0) return str; - return str + Exceptions.SEP + "Timeout: " + timeout; + return str + Exceptions.SEP + "Timeout: " + timeout + ", LockLevel" + + lockLevel; } private void writeObject(ObjectOutputStream out) throws IOException { out.writeInt(timeout); + out.writeInt(lockLevel); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { timeout = in.readInt(); + lockLevel = in.readInt(); } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/util/StoreException.java b/openjpa-kernel/src/main/java/org/apache/openjpa/util/StoreException.java index bb831727e..ef06cffbb 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/util/StoreException.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/util/StoreException.java @@ -35,6 +35,8 @@ public class StoreException public static final int REFERENTIAL_INTEGRITY = 4; public static final int OBJECT_EXISTS = 5; + private boolean recoverable = false; + public StoreException(String msg) { super(msg); } @@ -58,4 +60,12 @@ public class StoreException public int getType() { return STORE; } + + public void setRecoverable(boolean recoverable) { + this.recoverable = recoverable; + } + + public boolean isRecoverable() { + return recoverable; + } } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestInsertOrder.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestInsertOrder.java index 6e86aa208..92c48fbb4 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestInsertOrder.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestInsertOrder.java @@ -59,7 +59,7 @@ public class TestInsertOrder extends SQLListenerTestCase { em.getTransaction().commit(); em.close(); - assertSQLOrder("INSERT INTO " + empTableName + ".*", "INSERT INTO " + assertAllSQLInOrder("INSERT INTO " + empTableName + ".*", "INSERT INTO " + taskTableName + ".*", "INSERT INTO " + storyTableName + ".*"); } @@ -75,7 +75,7 @@ public class TestInsertOrder extends SQLListenerTestCase { em.getTransaction().commit(); em.close(); - assertSQLOrder("INSERT INTO " + empTableName + ".*", "INSERT INTO " + assertAllSQLInOrder("INSERT INTO " + empTableName + ".*", "INSERT INTO " + taskTableName + ".*", "INSERT INTO " + storyTableName + ".*"); } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockEmployee.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockEmployee.java new file mode 100644 index 000000000..b09953f20 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockEmployee.java @@ -0,0 +1,87 @@ +/* + * 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.lockmgr; + +import java.util.Collection; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Version; + +@Entity +public class LockEmployee { + + @Id + private int id; + + @Version + private int version; + + private String firstName; + private String lastName; + + @OneToMany(mappedBy = "employee" + , cascade = { CascadeType.ALL } + ) + private Collection tasks; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public Collection getTasks() { + return tasks; + } + + public void setTasks(Collection tasks) { + this.tasks = tasks; + } + + public int getVersion() { + return version; + } + + public String toString() { + return this.getClass().getName() + "[id=" + getId() + ", ver=" + + getVersion() + "] first=" + getFirstName() + ", last=" + + getLastName() + ", tasks={" + getTasks() + "}"; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockManagerTestBase.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockManagerTestBase.java new file mode 100644 index 000000000..b6910bae5 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockManagerTestBase.java @@ -0,0 +1,818 @@ +/* + * 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.lockmgr; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.EntityManager; +import javax.persistence.LockModeType; +import javax.persistence.TransactionRequiredException; + +import org.apache.openjpa.lib.log.Log; +import org.apache.openjpa.persistence.test.SQLListenerTestCase; + +/** + * Base class to support testing "version", "pessimistic" and "jpa2" + * lock manager behaviors. + */ +public abstract class LockManagerTestBase extends SQLListenerTestCase { + + protected final SQLAssertions commonSelect = new SQLAssertions( + SQLAssertType.AnySQLAnyOrder, + "SELECT .* FROM LockEmployee .*" + ); + protected final SQLAssertions commonForUpdate = new SQLAssertions( + SQLAssertType.NoneSQLAnyOrder, + ".* FOR UPDATE .*", + ".* KEEP UPDATE LOCKS.*" // DB2 SQL Statement Lock Pattern + ); + protected final SQLAssertions commonSelectForUpdate = new SQLAssertions( + SQLAssertType.AnySQLAnyOrder, + "SELECT .* FROM LockEmployee .* FOR UPDATE .*", + "SELECT .* FROM LockEmployee .* KEEP UPDATE LOCKS.*" + ); + + protected static final Class[] ExpectingOptimisticLockExClass = + new Class[] { javax.persistence.OptimisticLockException.class }; + protected static final Class[] ExpectingPessimisticLockExClass = + new Class[] { javax.persistence.PessimisticLockException.class }; + protected static final Class[] ExpectingLockTimeoutExClass = + new Class[] { javax.persistence.LockTimeoutException.class}; + protected static final Class[] ExpectingAnyLockExClass = + new Class[] { javax.persistence.PessimisticLockException.class, + javax.persistence.LockTimeoutException.class + }; + + protected static final String Default_FirstName = "Def FirstName"; + + protected static final String TxtLockRead = "[Lock Read, "; + protected static final String TxtLockWrite = "[Lock Write, "; + protected static final String TxtLockOptimistic = "[Lock Opt, "; + protected static final String TxtLockOptimisticForceInc = "[Lock OptFI, "; + protected static final String TxtLockPessimisticRead = "[Lock PesRd, "; + protected static final String TxtLockPessimisticWrite = "[Lock PesWr, "; + protected static final String TxtLockPessimisticForceInc = "[Lock PesFI, "; + + protected static final String TxtCommit = "Commit] "; + protected static final String TxtRollback = "Rollback] "; + + protected static final String TxtThread1 = "T1: "; + protected static final String TxtThread2 = "T2: "; + + protected enum CommitAction { + Commit, Rollback + }; + + protected enum RollbackAction { + Rolledback, NoRolledback + }; + + protected enum ThreadToRunFirst { + RunThread1, RunThread2 + } + + protected enum ThreadToResumeFirst { + ResumeThread1, ResumeThread2 + } + + protected enum MethodToCall { + NoOp, + Find, FindWaitLock, FindNoUpdate, + Refresh, + Lock, + } + + private String empTableName; + private String taskTableName; + private String storyTableName; + + private long waitInMsec = -1; + + protected void commonSetUp() { + empTableName = getMapping(LockEmployee.class).getTable().getFullName(); + taskTableName = getMapping(LockTask.class).getTable().getFullName(); + storyTableName = getMapping(LockStory.class).getTable().getFullName(); + + cleanupDB(); + + LockEmployee e1, e2, e3; + e1 = newTree(1); + e2 = newTree(2); + e3 = newTree(3); + + resetSQL(); + EntityManager em = null; + try { + em = emf.createEntityManager(); + em.getTransaction().begin(); + em.persist(e1); + em.persist(e2); + em.persist(e3); + em.getTransaction().commit(); + } finally { + if (em != null && em.isOpen()) { + em.close(); + } + } + + assertAllSQLInOrder( + "INSERT INTO " + empTableName + " .*", + "INSERT INTO " + taskTableName + " .*", + "INSERT INTO " + storyTableName + " .*", + "INSERT INTO " + empTableName + " .*", + "INSERT INTO " + taskTableName + " .*", + "INSERT INTO " + storyTableName + " .*", + "INSERT INTO " + empTableName + " .*", + "INSERT INTO " + taskTableName + " .*", + "INSERT INTO " + storyTableName + " .*" + ); + + // dynamic runtime test to determine wait time. + long speedCnt = -1; + if (waitInMsec == -1) { + speedCnt = platformSpeedTest(); + try { + waitInMsec = 500000 / (speedCnt / 1000000); + } catch (Throwable t) { + } + } + if (waitInMsec <= 0) { + waitInMsec = 30 * 1000; // default to 30sec + } + getLog().trace( + "**** Speed Cont=" + speedCnt + ", waitTime(ms)=" + waitInMsec); + } + + private long platformSpeedTest() { + PlatformSpeedTestThread speedThread = new PlatformSpeedTestThread(); + speedThread.start(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + logStack(e); + } + speedThread.interrupt(); + try { + Thread.sleep(50); + } catch (InterruptedException e) { + logStack(e); + } + return speedThread.getLoopCnt(); + } + + private void cleanupDB() { + EntityManager em = null; + try { + em = emf.createEntityManager(); + em.getTransaction().begin(); + + em.createQuery("delete from " + empTableName).executeUpdate(); + em.createQuery("delete from " + taskTableName).executeUpdate(); + em.createQuery("delete from " + storyTableName).executeUpdate(); + + em.getTransaction().commit(); + } finally { + if (em != null && em.isOpen()) { + em.close(); + } + } + } + + /** + * Helper to create a tree of entities + * + * @param id + * ID for the entities. + * @return an unmanaged Employee instance with the appropriate relationships + * set. + */ + private LockEmployee newTree(int id) { + LockEmployee e = new LockEmployee(); + e.setId(id); + + LockTask t = new LockTask(); + t.setId(id); + + LockStory s = new LockStory(); + s.setId(id); + + Collection tasks = new ArrayList(); + tasks.add(t); + + Collection stories = new ArrayList(); + stories.add(s); + + e.setTasks(tasks); + t.setEmployee(e); + + t.setStories(stories); + s.setTask(t); + + return e; + } + + protected Log getLog() { + return emf.getConfiguration().getLog("Tests"); + } + + protected Log getDumpStackLog() { + return emf.getConfiguration().getLog("DumpStack"); + } + + protected void logStack(Throwable t) { + StringWriter str = new StringWriter(); + PrintWriter print = new PrintWriter(str); + t.printStackTrace(print); + getDumpStackLog().trace(str.toString()); + } + + // Compute the timeout value used before the resume step for + // each thread + private long computeSleepFromTimeout( Integer timeout ) { + long value = 500; // default to 500 msec + if( timeout != null) { + value = timeout * 2; + } + return value; + } + + public void testFindNormal() { + getLog().info("---> testFindNormal()"); + resetSQL(); + EntityManager em = null; + try { + em = emf.createEntityManager(); + LockEmployee e = em.find(LockEmployee.class, 1); + getLog().trace("Found: " + e); + assertNotNull(e); + assertEquals("Expected=1, testing=" + e.getId(), 1, e.getId()); + commonSelect.validate(); + commonForUpdate.validate(); + } finally { + if (em != null && em.isOpen()) { + em.close(); + } + } + } + + public void testFindLockNone() { + getLog().info("---> testFindLockNone()"); + commonTestSequence( + LockModeType.NONE, + 1, + 0, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + protected void commonTestSequence(LockModeType mode, + int id, // LockEmployee ids used, id & id+1 + int commitVersionDiff, // version field difference on commit + SQLAssertions commitSimilarSQLOrder, SQLAssertions commitNotAnySQL, + SQLAssertions rollbackSimilarSQLOrder, SQLAssertions rollbackNotAnySQL){ + Log log = getLog(); + log.trace("commonTestSequence(lockmode=" + mode + ",id=" + id + + ",verDiff=" + commitVersionDiff + "...)"); + EntityManager em = null; + try { + em = emf.createEntityManager(); + resetSQL(); + try { + em.find(LockEmployee.class, id, mode); + if (mode != LockModeType.NONE) + fail("Need transaction begin to apply lock mode " + mode); + } catch (TransactionRequiredException trex) { + if (mode == LockModeType.NONE) + fail("Do not need transaction begin to for lock " + mode); + } catch (Exception ex) { + logStack(ex); + fail("Need transaction begin to apply lock mode " + mode + + ". Exception encountered:" + ex.getMessage()); + } + + int lastVersion = -1; + resetSQL(); + em.clear(); + em.getTransaction().begin(); + LockEmployee e1 = em.find(LockEmployee.class, id, mode); + assertNotNull(e1); + assertEquals("Expected=" + id + ", testing=" + e1.getId(), id, e1 + .getId()); + lastVersion = e1.getVersion(); + em.getTransaction().commit(); + if (commitSimilarSQLOrder != null) + commitSimilarSQLOrder.validate(); + if (commitNotAnySQL != null) + commitNotAnySQL.validate(); + + resetSQL(); + em.clear(); + LockEmployee e2 = em.find(LockEmployee.class, id); + assertNotNull(e2); + assertEquals("Expected=" + id + ", testing=" + e1.getId(), id, e1 + .getId()); + assertEquals("Expected=" + (lastVersion + commitVersionDiff) + + ", testing=" + e2.getVersion(), lastVersion + + commitVersionDiff, e2.getVersion()); + + lastVersion = -1; + ++id; + resetSQL(); + em.clear(); + em.getTransaction().begin(); + e1 = em.find(LockEmployee.class, id, mode); + assertNotNull(e1); + assertEquals("Expected=" + id + ", testing=" + e1.getId(), id, e1 + .getId()); + lastVersion = e1.getVersion(); + em.getTransaction().rollback(); + if (rollbackSimilarSQLOrder != null) + rollbackSimilarSQLOrder.validate(); + if (rollbackNotAnySQL != null) + rollbackNotAnySQL.validate(); + + resetSQL(); + em.clear(); + e2 = em.find(LockEmployee.class, id); + assertNotNull(e2); + assertEquals("Expected=" + id + ", testing=" + e1.getId(), id, e1 + .getId()); + assertEquals("Expected=" + lastVersion + ", testing=" + + e2.getVersion(), lastVersion, e2.getVersion()); + } catch (Exception ex) { + logStack(ex); + Throwable rootCause = ex.getCause(); + String failStr = "Unexpected exception:" + ex.getClass().getName() + + ":" + ex; + if (rootCause != null) { + failStr += "\n -- Cause --> " + + rootCause.getClass().getName() + ":" + rootCause; + } + fail(failStr); + } finally { + if (em != null && em.isOpen()) { + if (em.getTransaction().isActive()) { + if (em.getTransaction().getRollbackOnly()) { + log.trace("finally: rolledback"); + em.getTransaction().rollback(); + log.trace("finally: rolledback completed"); + } else { + log.trace("finally: commit"); + em.getTransaction().commit(); + log.trace("finally: commit completed"); + } + } + em.close(); + } + } + } + + public void concurrentLockingTest( + int id, + int expectedVersionIncrement, + String expectedFirstName, + ThreadToRunFirst thread2Run, + ThreadToResumeFirst thread2Resume, + + LockModeType t1LockMode, + String t1ChangeValue, + CommitAction t1Commit, + RollbackAction t1ExpectedSystemRolledback, + Class[] t1ExpectedExceptions, + MethodToCall t1Method, + Integer t1Timeout, // in msec + + LockModeType t2LockMode, + String t2ChangeValue, + CommitAction t2Commit, + RollbackAction t2ExpectedSystemRolledback, + Class[] t2ExpectedExceptions, + MethodToCall t2Method, + Integer t2Timeout // in msec + ) { + + Log log = getLog(); + log.trace("================= Concurrent Lock Test: ================="); + log.trace(" id=" + id + ", versionInc=" + expectedVersionIncrement + + ", expectedFirstName='" + expectedFirstName + "', threadRunFirst=" + + thread2Run + ", thread2Resume=" + thread2Resume); + log.trace(" t1:lock=" + t1LockMode + ", change='" + t1ChangeValue + + "', " + (t1Commit == CommitAction.Commit ? "commit" : "rollback") + + ", expectedSysRollback=" + t1ExpectedSystemRolledback + + ", expectedEx=" + Arrays.toString(t1ExpectedExceptions) + + ", method=" + t1Method + ", timeout=" + t1Timeout); + log.trace(" t2:lock=" + t2LockMode + ", change='" + t2ChangeValue + + "', " + (t2Commit == CommitAction.Commit ? "commit" : "rollback") + + ", expectedSysRollback=" + t2ExpectedSystemRolledback + + ", expectedEx=" + Arrays.toString(t2ExpectedExceptions) + + ", method=" + t2Method + ", timeout=" + t2Timeout); + long endTime = System.currentTimeMillis() + waitInMsec; + + EntityManager em = null; + try { + em = emf.createEntityManager(); + em.getTransaction().begin(); + LockEmployee ei = em.find(LockEmployee.class, id); + assertNotNull(ei); + ei.setFirstName(Default_FirstName); + em.getTransaction().commit(); + } catch (Exception ex) { + logStack(ex); + Throwable rootCause = ex.getCause(); + String failStr = "Unable to pre-initialize FirstName to known " + + "value:" + ex.getClass().getName() + ":" + ex; + if (rootCause != null) { + failStr += "\n -- Cause --> " + + rootCause.getClass().getName() + ":" + rootCause; + } + fail(failStr); + } finally { + if (em != null && em.isOpen()) { + if (em.getTransaction().isActive()) { + if (em.getTransaction().getRollbackOnly()) { + log.trace("finally: rolledback"); + em.getTransaction().rollback(); + log.trace("finally: rolledback completed"); + } else { + log.trace("finally: commit"); + em.getTransaction().commit(); + log.trace("finally: commit completed"); + } + } + em.close(); + } + } + em = null; + try { + em = emf.createEntityManager(); + LockEmployee e0 = em.find(LockEmployee.class, id); + assertNotNull(e0); + int lastVersion = e0.getVersion(); + log.trace("Start version=" + lastVersion); + em.clear(); + + LockTestThread t1 = new LockTestThread(t1LockMode, id, + t1ChangeValue, t1Commit, t1Method, t1Timeout); + LockTestThread t2 = new LockTestThread(t2LockMode, id, + t2ChangeValue, t2Commit, t2Method, t2Timeout); + + if (thread2Run == ThreadToRunFirst.RunThread1) { + t1.start(); + } else { + t2.start(); + } + Thread.sleep(500); + if (thread2Run == ThreadToRunFirst.RunThread2) { + t1.start(); + } else { + t2.start(); + } + // continue the thread after each thread had a chance to fetch the + // row + if (thread2Resume == ThreadToResumeFirst.ResumeThread1) { + Thread.sleep(computeSleepFromTimeout(t1Timeout)); + t1.resumeEmAction(); + } else { + Thread.sleep(computeSleepFromTimeout(t2Timeout)); + t2.resumeEmAction(); + } + if (thread2Resume == ThreadToResumeFirst.ResumeThread2) { + Thread.sleep(computeSleepFromTimeout(t1Timeout)); + t1.resumeEmAction(); + } else { + Thread.sleep(computeSleepFromTimeout(t2Timeout)); + t2.resumeEmAction(); + } + + log.trace("started children threads"); + + // wait for threads to die or timeout + log.trace("checking if thread is alive"); + while ((t1.isAlive() || t2.isAlive()) + && System.currentTimeMillis() < endTime) { + try { + Thread.sleep(500); + } catch (InterruptedException e1) { + } + log.trace("waiting children thread for completion (" + + (endTime - System.currentTimeMillis()) + " ms left)"); + } + + testThread(t1, t1ExpectedExceptions, + t1ExpectedSystemRolledback, log); + testThread(t2, t2ExpectedExceptions, + t2ExpectedSystemRolledback, log); + + log.trace("verify lock and update are correct."); + LockEmployee e = em.find(LockEmployee.class, id); + log.trace("End version=" + e.getVersion()); + assertNotNull(e); + assertEquals("Expected=" + id + ", testing=" + e.getId(), id, e + .getId()); + assertEquals("Expected=" + (lastVersion + expectedVersionIncrement) + + ", testing=" + e.getVersion(), lastVersion + + expectedVersionIncrement, e.getVersion()); + assertEquals("Expected=" + expectedFirstName + ", testing=" + + e.getFirstName(), expectedFirstName, e.getFirstName()); + } catch (Exception ex) { + logStack(ex); + Throwable rootCause = ex.getCause(); + String failStr = "Unexpected exception:" + ex.getClass().getName() + + ":" + ex; + if (rootCause != null) { + failStr += "\n -- Cause --> " + + rootCause.getClass().getName() + ":" + rootCause; + } + fail(failStr); + } finally { + if (em != null && em.isOpen()) { + if (em.getTransaction().isActive()) { + if (em.getTransaction().getRollbackOnly()) { + log.trace("finally: rolledback"); + em.getTransaction().rollback(); + log.trace("finally: rolledback completed"); + } else { + log.trace("finally: commit"); + em.getTransaction().commit(); + log.trace("finally: commit completed"); + } + } + em.close(); + } + } + } + + private boolean matchExpectedException(Class expected, + Exception tested) { + assertNotNull(expected); + Class testExClass = null; + boolean exMatched = true; + if (tested != null) { + testExClass = tested.getClass(); + exMatched = expected.isAssignableFrom(testExClass); + if (!exMatched) { + Throwable testEx = tested.getCause(); + if (testEx != null) { + testExClass = testEx.getClass(); + exMatched = expected.isAssignableFrom(testExClass); + } + } + } else { + exMatched = false; + } + return exMatched; + } + + private void testThread(LockTestThread t, Class[] expectedExceptions, + RollbackAction expectedSystemRolledback, Log log) { + boolean alive = t.isAlive(); + if (alive) { + log.trace(t.getName() + " is still alive"); + try { + t.interrupt(); + t.join(); + } catch (Exception e) { + logStack(e); + } + } + Class testExClass = null; + boolean exMatched = false; + if (expectedExceptions != null) { + for( Class expectedException : expectedExceptions) { + if( matchExpectedException(expectedException, t.exception) ) { + exMatched = true; + break; + } + } + } else { + if (t.exception == null) { + exMatched = true; + } else { + testExClass = t.exception.getClass(); + } + } + assertTrue( + "Exception test: Expecting=" + Arrays.toString(expectedExceptions) + + ", Testing=" + testExClass, exMatched); + assertEquals(expectedSystemRolledback == RollbackAction.Rolledback, + t.systemRolledback); + } + + private class LockTestThread extends Thread { + + private LockModeType lockMode; + private int id; + private String changeValue; + private boolean commit; + private MethodToCall beforeWaitMethod; + private Integer timeout; + + public Exception exception = null; + public boolean systemRolledback = false; + + public LockTestThread(LockModeType lockMode, int id, + String changeValue, CommitAction commit, + MethodToCall beforeWaitMethod, Integer timeout) { + this.lockMode = lockMode; + this.id = id; + this.changeValue = changeValue; + this.commit = commit == CommitAction.Commit; + this.beforeWaitMethod = beforeWaitMethod; + this.timeout = timeout; + } + + public synchronized void resumeEmAction () { + notify(); + } + + public synchronized void run() { + Log log = getLog(); + log.trace("enter run()"); + EntityManager em = null; + long startTimeStamp = System.currentTimeMillis(); + try { + em = emf.createEntityManager(); + em.getTransaction().begin(); + try { + log.trace(beforeWaitMethod + ": id=" + id + ", lock=" + + lockMode + ", timeout=" + timeout); + LockEmployee e1 = null; + switch (beforeWaitMethod) { + case NoOp: + break; + case Find: + case FindNoUpdate: + if (timeout == null) { + e1 = em.find(LockEmployee.class, id, lockMode); + } else { + Map props = + new HashMap(1); + props + .put("javax.persistence.lock.timeout", timeout); + e1 = em.find(LockEmployee.class, id, lockMode, + props); + } + break; + case FindWaitLock: + e1 = em.find(LockEmployee.class, id); + break; + case Refresh: + e1 = em.find(LockEmployee.class, id); + if (timeout == null) { + em.refresh(e1, lockMode); + } else { + Map props = + new HashMap(1); + props + .put("javax.persistence.lock.timeout", timeout); + em.refresh(e1, lockMode, props); + } + break; + case Lock: + e1 = em.find(LockEmployee.class, id); + if (timeout == null) { + em.lock(e1, lockMode); + } else { + Map props = + new HashMap(1); + props + .put("javax.persistence.lock.timeout", timeout); + em.lock(e1, lockMode, props); + } + break; + } + + log.trace(beforeWaitMethod + ": duration 1=" + + (System.currentTimeMillis() - startTimeStamp)); + log.trace(beforeWaitMethod + ": returns=" + e1); + log.trace("wait()"); + wait(); + switch (beforeWaitMethod) { + case NoOp: + log.trace(beforeWaitMethod + + ": No operation performed."); + break; + case FindWaitLock: + log + .trace(beforeWaitMethod + ": Lock(" + lockMode + + ")"); + em.lock(e1, lockMode); + break; + case FindNoUpdate: + log.trace(beforeWaitMethod + + ": No update to firstName field per request."); + break; + default: + log.trace(beforeWaitMethod + + ": update first name from '" + e1.getFirstName() + + "' to '" + changeValue + "'"); + e1.setFirstName(changeValue); + log.trace("update first name completed"); + } + } finally { + log.trace(beforeWaitMethod + ": duration 2=" + + (System.currentTimeMillis() - startTimeStamp)); + if (em != null) { + systemRolledback = em.getTransaction() + .getRollbackOnly(); + if (systemRolledback) { + log.trace("rolledback by system"); + em.getTransaction().rollback(); + log.trace("rolledback by system completed"); + } else { + if (commit) { + log.trace("commit update"); + em.getTransaction().commit(); + log.trace("commit completed"); + } else { + log.trace("rollback update"); + em.getTransaction().rollback(); + log.trace("rollback completed"); + } + } + } + } + } catch (Exception e) { + exception = e; + log.trace("Caught exception:" + e); + Class exClass = e.getClass(); + if (javax.persistence.PersistenceException.class + .isAssignableFrom(exClass) + || javax.persistence.RollbackException.class + .isAssignableFrom(exClass) + || javax.persistence.OptimisticLockException.class + .isAssignableFrom(exClass) + || javax.persistence.PessimisticLockException.class + .isAssignableFrom(exClass) + || javax.persistence.EntityNotFoundException.class + .isAssignableFrom(exClass)) { + systemRolledback = true; + } + Throwable cause = e.getCause(); + if (cause != null) + log.trace(" root cause:" + cause); + logStack(e); + } finally { + log.trace(beforeWaitMethod + ": duration 3=" + + (System.currentTimeMillis() - startTimeStamp)); + if (em != null && em.isOpen()) { + if (em.getTransaction().isActive()) { + systemRolledback = em.getTransaction() + .getRollbackOnly(); + if (systemRolledback) { + log.trace("finally: rolledback"); + em.getTransaction().rollback(); + log.trace("finally: rolledback completed"); + } else { + log.trace("finally: commit"); + em.getTransaction().commit(); + log.trace("finally: commit completed"); + } + } + em.close(); + } + } + } + } + + class PlatformSpeedTestThread extends Thread { + long loopCnt = 0; + + public long getLoopCnt() { + return loopCnt; + } + + public synchronized void run() { + while (true) { + ++loopCnt; + if (this.isInterrupted()) + break; + } + } + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockStory.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockStory.java new file mode 100644 index 000000000..d61f6b965 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockStory.java @@ -0,0 +1,63 @@ +/* + * 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.lockmgr; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.Version; + +@Entity +public class LockStory { + @Id + private int id; + + @Version + private int version; + + @ManyToOne(cascade = { CascadeType.ALL }) + private LockTask task; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public LockTask getTask() { + return task; + } + + public void setTask(LockTask task) { + this.task = task; + } + + public int getVersion() { + return version; + } + + public String toString() { + return this.getClass().getName() + "[id=" + getId() + ", ver=" + + getVersion() + "]"; + } + +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockTask.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockTask.java new file mode 100644 index 000000000..fedd85b59 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockTask.java @@ -0,0 +1,76 @@ +/* + * 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.lockmgr; + +import java.util.Collection; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Version; + +@Entity +public class LockTask { + @Id + private int id; + + @Version + private int version; + + @OneToMany(mappedBy = "task", cascade = { CascadeType.ALL }) + private Collection stories; + + @ManyToOne(cascade = { CascadeType.ALL }) + private LockEmployee employee; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Collection getStories() { + return stories; + } + + public void setStories(Collection stories) { + this.stories = stories; + } + + public LockEmployee getEmployee() { + return employee; + } + + public void setEmployee(LockEmployee employee) { + this.employee = employee; + } + + public int getVersion() { + return version; + } + + public String toString() { + return this.getClass().getName() + "[id=" + getId() + ", ver=" + + getVersion() + "] stories={" + getStories() + "}"; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestJPA2LockManager.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestJPA2LockManager.java new file mode 100644 index 000000000..0846127d1 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestJPA2LockManager.java @@ -0,0 +1,1697 @@ +/* + * 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.lockmgr; + +import javax.persistence.LockModeType; + +import org.apache.openjpa.persistence.test.AllowFailure; + +/** + * Test JPA 2.0 LockMode type behaviors with "jpa2" lock manager. + */ +public class TestJPA2LockManager extends LockManagerTestBase { + public void setUp() { + setUp(LockEmployee.class, LockTask.class, LockStory.class +// , "openjpa.jdbc.FinderCache", "false" + ); + commonSetUp(); + } + + public void testFindLockRead() { + getLog().info("---> testFindLockRead()"); + commonTestSequence( + LockModeType.READ, + 2, + 0, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + public void testFindLockWrite() { + getLog().info("---> testFindLockWrite()"); + commonTestSequence( + LockModeType.WRITE, + 1, + 1, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + public void testFindLockOptimistic() { + getLog().info("---> testFindLockOptimistic()"); + commonTestSequence( + LockModeType.OPTIMISTIC, + 2, + 0, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + public void testFindLockOptimisticForceIncrement() { + getLog().info("---> testFindLockOptimisticForceIncrement()"); + commonTestSequence( + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + 1, + 1, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + public void testFindLockPessimisticRead() { + getLog().info("---> testFindLockPessimisticRead()"); + commonTestSequence( +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + 2, + 0, + commonSelectForUpdate, + null, + commonSelectForUpdate, + null + ); + } + + public void testFindLockPessimisticWrite() { + getLog().info("---> testFindLockPessimisticWrite()"); + commonTestSequence( +// TODO: LockModeType.PESSIMISTIC_WRITE, + LockModeType.PESSIMISTIC, + 1, + 0, + commonSelectForUpdate, + null, + commonSelectForUpdate, + null + ); + } + + public void testFindLockPessimisticForceIncrement() { + getLog().info("---> testFindLockPessimisticForceIncrement()"); + commonTestSequence( + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + 2, + 1, + commonSelectForUpdate, + null, + commonSelectForUpdate, + null + ); + } + + public void testConcurrentThread1ReadTest() { + getLog().info("---> testConcurrentThread1ReadTest()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Read commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockRead + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.READ, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Read rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockRead + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.READ, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + } + + public void testConcurrentThread1WriteTest() { + getLog().info("---> testConcurrentThread1WriteTest()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Write commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockWrite + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.WRITE, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Write rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockWrite + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.WRITE, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + } + + // TODO: + @AllowFailure(msg="OPENJPA-924 is preventing RR behavior: pessimistic lock " + + "blocked read on thread 2, once thread-1 commit, thread-2 returns " + + "with pre-thread 1 committed data. hence causing an " + + "OptimisticLockException. Disable FinderCache to workaround the " + + "problem.") + public void testConcurrentThread1PessimisticReadTest() { + getLog().info("---> testConcurrentThread1PessimisticReadTest()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Read commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Read commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + } + + // TODO: + @AllowFailure(msg="OPENJPA-924 is preventing RR behavior: pessimistic lock " + + "blocked read on thread 2, once thread-1 commit, thread-2 returns " + + "with pre-thread 1 committed data. hence causing an " + + "OptimisticLockException. Disable FinderCache to workaround the " + + "problem.") + public void testConcurrentThread1PessimisticForceIncTest() { + getLog().info("---> testConcurrentThread1PessimisticForceIncTest()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Read commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Read commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + } + + /** + * Test specific version condition per "3.4.4.2 When lock(entity, + * LockModeType.PESSIMISTIC) is invoked on a versioned entity that is + * already in the persistence context, the provider must also perform + * optimistic version checks when obtaining the lock. An + * OptimisticLockException must be thrown if the version checks fail. + * Depending on the implementation strategy used by the provider, it is + * possible that this exception may not be thrown until flush is called or + * commit time, whichever occurs first." + */ + public void testPessimisticLockOnUpdatedVersion() { + getLog().info("---> testPessimisticLockOnUpdatedVersion()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.FindWaitLock, + null + ); + } + + /** + * Test version is updated for PessimisticForceIncrement + * lock. + */ + public void testPessimisticForceVersion() { + getLog().info("---> testPessimisticForceVersion()"); + String baseFirstName; + String t1FirstName; + + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Noop + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + concurrentLockingTest( + 1, 0, Default_FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.FindNoUpdate, + null, + + LockModeType.NONE, + null, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.NoOp, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Noop + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + concurrentLockingTest( + 1, 1, Default_FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.FindNoUpdate, + null, + + LockModeType.NONE, + null, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.NoOp, + null + ); + } + + /** + * Test Pessimistic lock timeout condition. + */ + public void testPessimisticLockTimeOut() { + getLog().info("---> testPessimisticLockTimeOut()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Noop + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 0, Default_FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread2, + + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.FindNoUpdate, + null, + + LockModeType.PESSIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingAnyLockExClass, + MethodToCall.FindNoUpdate, + 5000 + ); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestOptimisticLockManager.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestOptimisticLockManager.java new file mode 100644 index 000000000..619bd3532 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestOptimisticLockManager.java @@ -0,0 +1,893 @@ +/* + * 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.lockmgr; + +import javax.persistence.LockModeType; + +/** + * Test JPA 2.0 LockTypeMode semantics using OpenJPA optimistic + * "version" lock manager. + * + * @author Albert Lee + * @since 2.0.0 + */ +public class TestOptimisticLockManager extends LockManagerTestBase { + + public void setUp() { + setUp(LockEmployee.class, LockTask.class, LockStory.class + , "openjpa.LockManager", "version" + ); + commonSetUp(); + } + + public void testFindLockRead() { + getLog().info("---> testFindLockRead()"); + commonTestSequence( + LockModeType.READ, + 2, + 0, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + public void testFindLockWrite() { + getLog().info("---> testFindLockWRITE()"); + commonTestSequence( + LockModeType.WRITE, + 1, + 1, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + public void testFindLockOptimistic() { + getLog().info("---> testFindLockOptimistic()"); + commonTestSequence( + LockModeType.OPTIMISTIC, + 2, + 0, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + public void testFindLockOptimisticForceIncrement() { + getLog().info("---> testFindLockOptimisticForceIncrement()"); + commonTestSequence( + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + 1, + 1, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + public void testFindLockPessimisticRead() { + getLog().info("---> testFindLockPessimisticShared()"); + commonTestSequence( +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + 2, + 1, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + public void testFindLockPessimisticWrite() { + getLog().info("---> testFindLockPessimisticWrite()"); + commonTestSequence( +// TODO: LockModeType.PESSIMISTIC_WRITE, + LockModeType.PESSIMISTIC, + 1, + 1, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + public void testFindLockPessimisticForceIncrement() { + getLog().info("---> testFindLockPessimisticForceIncrement()"); + commonTestSequence( + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + 2, + 1, + commonSelect, + commonForUpdate, + commonSelect, + commonForUpdate + ); + } + + public void testConcurrentThread1ReadTest() { + getLog().info("---> testConcurrentThread1ReadTest()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Read commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockRead + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.READ, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Read rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockRead + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.READ, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + } + + public void testConcurrentThread1WriteTest() { + getLog().info("---> testConcurrentThread1WriteTest()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Write commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockWrite + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.WRITE, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Write rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockWrite + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.WRITE, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + } + + public void testConcurrentThread1PessimisticTest() { + getLog().info("---> testConcurrentThread1PessimisticTest()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t1FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.Rolledback, + ExpectingOptimisticLockExClass, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLockManager.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLockManager.java new file mode 100644 index 000000000..f78797a7b --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLockManager.java @@ -0,0 +1,1566 @@ +/* + * 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.lockmgr; + +import javax.persistence.LockModeType; + +import org.apache.openjpa.persistence.test.AllowFailure; + +/** + * Test JPA 2.0 LockTypeMode semantics using OpenJPA pessimistic + * "pessimistic" lock manager. + * + * @author Albert Lee + * @since 2.0.0 + */ +public class TestPessimisticLockManager extends LockManagerTestBase { + + public void setUp() { + setUp(LockEmployee.class, LockTask.class, LockStory.class + , "openjpa.LockManager", "pessimistic" +// , "openjpa.jdbc.FinderCache", "false" + ); + commonSetUp(); + } + + public void testFindLockRead() { + getLog().info("---> testFindLockRead()"); + commonTestSequence( + LockModeType.READ, + 2, + 0, + commonSelectForUpdate, + null, + commonSelectForUpdate, + null + ); + } + + public void testFindLockWrite() { + getLog().info("---> testFindLockWrite()"); + commonTestSequence( + LockModeType.WRITE, + 1, + 0, + commonSelectForUpdate, + null, + commonSelectForUpdate, + null + ); + } + + public void testFindLockOptimistic() { + getLog().info("---> testFindLockOptimistic()"); + commonTestSequence( + LockModeType.OPTIMISTIC, + 2, + 0, + commonSelectForUpdate, + null, + commonSelectForUpdate, + null + ); + } + + public void testFindLockOptimisticForceIncrement() { + getLog().info("---> testFindLockOptimisticForceIncrement()"); + commonTestSequence( + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + 1, + 0, + commonSelectForUpdate, + null, + commonSelectForUpdate, + null + ); + } + + public void testFindLockPessimisticRead() { + getLog().info("---> testFindLockPessimisticRead()"); + commonTestSequence( +// TODO: LockModeType.PESSIMISTIC_SHARED, + LockModeType.PESSIMISTIC, + 2, + 0, + commonSelectForUpdate, + null, + commonSelectForUpdate, + null + ); + } + + public void testFindLockPessimisticWrite() { + getLog().info("---> testFindLockPessimisticWrite()"); + commonTestSequence( +// TODO: LockModeType.PESSIMISTIC_WRITE, + LockModeType.PESSIMISTIC, + 1, + 0, + commonSelectForUpdate, + null, + commonSelectForUpdate, + null + ); + } + + public void testFindLockPessimisticForceIncrement() { + getLog().info("---> testFindLockPessimisticForceIncrement()"); + commonTestSequence( + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + 2, + 0, + commonSelectForUpdate, + null, + commonSelectForUpdate, + null + ); + } + + // TODO: + @AllowFailure(msg="OPENJPA-924 is preventing RR behavior: pessimistic lock " + + "blocked read on thread 2, once thread-1 commit, thread-2 returns " + + "with pre-thread 1 committed data. hence causing an " + + "OptimisticLockException. Disable FinderCache to workaround the " + + "problem.") + public void testConcurrentThread1ReadTest() { + getLog().info("---> testConcurrentThread1ReadTest()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Read commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockRead + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.READ, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Read rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockRead + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.READ, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic commit + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtCommit + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic rollback + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimistic + TxtRollback + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + } + + // TODO: + @AllowFailure(msg="OPENJPA-924 is preventing RR behavior: pessimistic lock " + + "blocked read on thread 2, once thread-1 commit, thread-2 returns " + + "with pre-thread 1 committed data. hence causing an " + + "OptimisticLockException. Disable FinderCache to workaround the " + + "problem.") + public void testConcurrentThread1WriteTest() { + getLog().info("---> testConcurrentThread1WriteTest()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Write commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockWrite + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.WRITE, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Write rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockWrite + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.WRITE, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment commit + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Optimistic_Force_Increment rollback + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockOptimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + } + + // TODO: + @AllowFailure(msg="OPENJPA-924 is preventing RR behavior: pessimistic lock " + + "blocked read on thread 2, once thread-1 commit, thread-2 returns " + + "with pre-thread 1 committed data. hence causing an " + + "OptimisticLockException. Disable FinderCache to workaround the " + + "problem.") + public void testConcurrentThread1PessimisticReadTest() { + getLog().info("---> testConcurrentThread1PessimisticReadTest()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Read commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Read commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read commit + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtCommit + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Read rollback + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticRead + TxtRollback + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + +// TODO: LockModeType.PESSIMISTIC_READ, + LockModeType.PESSIMISTIC, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + } + + // TODO: + @AllowFailure(msg="OPENJPA-924 is preventing RR behavior: pessimistic lock " + + "blocked read on thread 2, once thread-1 commit, thread-2 returns " + + "with pre-thread 1 committed data. hence causing an " + + "OptimisticLockException. Disable FinderCache to workaround the " + + "problem.") + public void testConcurrentThread1PessimisticForceIncTest() { + getLog().info("---> testConcurrentThread1PessimisticForceIncTest()"); + String baseFirstName; + String t1FirstName; + String t2FirstName; + + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Optimistic commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockOptimistic + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Read commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Read commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Optimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockOptimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.OPTIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Pessimistic_Read commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticRead + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_READ, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Pessimistic_Write commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticWrite + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC, +// TODO: LockModeType.PESSIMISTIC_WRITE, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment commit + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtCommit + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 2, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + //======================================================= + // Thread 1: Pessimistic_Force_Increment rollback + // Thread 2: Pessimistic_Force_Increment commit + baseFirstName = 1 + TxtLockPessimisticForceInc + TxtRollback + + 2 + TxtLockPessimisticForceInc + TxtCommit; + t1FirstName = TxtThread1 + baseFirstName; + t2FirstName = TxtThread2 + baseFirstName; + concurrentLockingTest( + 1, 1, t2FirstName, + ThreadToRunFirst.RunThread1, + ThreadToResumeFirst.ResumeThread1, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t1FirstName, + CommitAction.Rollback, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null, + + LockModeType.PESSIMISTIC_FORCE_INCREMENT, + t2FirstName, + CommitAction.Commit, + RollbackAction.NoRolledback, + null, + MethodToCall.Find, + null + ); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AllowFailure.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AllowFailure.java index 5abe878ba..b08f4176a 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AllowFailure.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AllowFailure.java @@ -34,4 +34,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; @Retention(RUNTIME) public @interface AllowFailure { boolean value() default true; + String msg() default ""; } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java index 4dcfa6bd1..66d0c24e0 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java @@ -449,8 +449,11 @@ public abstract class PersistenceTestCase try { super.runBare(); } catch (Throwable t) { - if (allowFailure()) { - System.err.println("*** FAILED (but ignored):" + this); + String allowFailureMsg = getAllowFailureMsg(); + if ( allowFailureMsg != null ) { + System.err.println("*** FAILED (but ignored): " + this); + System.err.println("*** Reason : " + + allowFailureMsg); System.err.println("Stacktrace of failure"); t.printStackTrace(); } else { @@ -464,12 +467,12 @@ public abstract class PersistenceTestCase * @AllowFailure. Method level annotation has higher precedence than Class * level annotation. */ - protected boolean allowFailure() { + protected String getAllowFailureMsg() { try { Method runMethod = getClass().getMethod(getName(), (Class[])null); AllowFailure anno = runMethod.getAnnotation(AllowFailure.class); if (anno != null) - return anno.value(); + return anno.value() ? anno.msg() : null; } catch (SecurityException e) { //ignore } catch (NoSuchMethodException e) { @@ -477,8 +480,8 @@ public abstract class PersistenceTestCase } AllowFailure anno = getClass().getAnnotation(AllowFailure.class); if (anno != null) - return anno.value(); - return false; + return anno.value() ? anno.msg() : null; + return null; } private static class FixedMap extends LinkedHashMap { diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java index 95998dad1..4d4045845 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java @@ -18,9 +18,9 @@ */ package org.apache.openjpa.persistence.test; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.ArrayList; import org.apache.openjpa.lib.jdbc.AbstractJDBCListener; import org.apache.openjpa.lib.jdbc.JDBCEvent; @@ -35,8 +35,7 @@ public abstract class SQLListenerTestCase extends SingleEMFTestCase { private static String _nl = System.getProperty("line.separator"); protected List sql = new ArrayList(); - protected int sqlCount; - + @Override public void setUp(Object... props) { Object[] copy = new Object[props.length + 2]; @@ -67,16 +66,15 @@ public abstract class SQLListenerTestCase * @param sqlExp the SQL expression. E.g., "SELECT BADCOLUMN .*" */ public void assertNotSQL(String sqlExp) { - boolean failed = false; - for (String statement : sql) { - if (statement.matches(sqlExp)) - failed = true; + if (statement.matches(sqlExp)) { + fail("Regular expression\r\n <" + + sqlExp + + ">\r\n should not have been executed in SQL statements:" + + "\r\n" + toString(sql)); + break; + } } - - if (failed) - fail("Regular expression\r\n <" + sqlExp + ">\r\n should not have" - + " been executed in SQL statements: \r\n" + toString(sql)); } /** @@ -91,24 +89,119 @@ public abstract class SQLListenerTestCase } fail("Expected regular expression\r\n <" + sqlExp + ">\r\n to be" - + " contained in SQL statements: \r\n" + toString(sql)); + + " contained in SQL statements: \r\n" + toString(sql)); + } + + /** + * Confirm the list of expected SQL expressions have been executed in the + * order specified. I.e. additional SQL statements can be executed in + * between expected SQLs. + * + * @param expected + * SQL expressions. E.g., ("SELECT FOO .*", "UPDATE .*") + */ + public void assertAllSQLInOrder(String... expected) { + assertSQLInOrder(false, expected); + } + + /** + * Confirm the list of expected SQL expressions have been executed in the + * exact number and order specified. + * + * @param expected + * SQL expressions. E.g., ("SELECT FOO .*", "UPDATE .*") + */ + public void assertAllExactSQLInOrder(String... expected) { + assertSQLInOrder(true, expected); + } + + private void assertSQLInOrder(boolean exact, String... expected) { + boolean match = false; + int sqlSize = sql.size(); + if (expected.length <= sqlSize) { + int hits = 0; + for (String executedSQL : sql) { + if (executedSQL.matches(expected[hits])) { + if (++hits == expected.length) + break; + } + } + match = hits == (exact ? sqlSize : expected.length); + } + + if (!match) { + StringBuilder sb = new StringBuilder(); + sb.append("Did not find SQL in expected order : ").append(_nl); + for (String s : expected) { + sb.append(s).append(_nl); + } + + sb.append("SQL Statements issued : "); + for (String s : sql) { + sb.append(s).append(_nl); + } + fail(sb.toString()); + } + } + + /** + * Confirm the list of expected SQL expressions have executed in any order. + * + * @param expected + * SQL expressions. E.g., ("SELECT FOO .*", "UPDATE .*") + */ + public void assertAllSQLAnyOrder(String... expected) { + for (String statement : expected) { + assertSQL(statement); + } + } + + /** + * Confirm the list of expected SQL expressions have not executed in any + * order. + * + * @param expected + * SQL expressions. E.g., ("SELECT FOO .*", "UPDATE .*") + */ + public void assertNoneSQLAnyOrder(String... expected) { + for (String statement : expected) { + assertNotSQL(statement); + } + } + + /** + * Confirm the any of expected SQL expressions have executed in any order. + * + * @param expected + * SQL expressions. E.g., ("SELECT FOO .*", "UPDATE .*") + */ + public void assertAnySQLAnyOrder(String... expected) { + for (String sqlExp : expected) { + for (String statement : sql) { + if (statement.matches(sqlExp)) + return; + } + } + fail("Expected regular expression\r\n <" + + toString(Arrays.asList(expected)) + ">\r\n to be" + + " contained in SQL statements: \r\n" + toString(sql)); } /** * Gets the number of SQL issued since last reset. */ public int getSQLCount() { - return sqlCount; + return sql.size(); } /** * Resets SQL count. * @return number of SQL counted since last reset. */ - public int resetSQLCount() { - int tmp = sqlCount; - sqlCount = 0; - return tmp; + public int resetSQL() { + int tmp = sql.size(); + sql.clear(); + return tmp; } public String toString(List list) { @@ -125,32 +218,50 @@ public abstract class SQLListenerTestCase public void beforeExecuteStatement(JDBCEvent event) { if (event.getSQL() != null && sql != null) { sql.add(event.getSQL()); - sqlCount++; - } - } - } - - public void assertSQLOrder(String... expected) { - int hits = 0; - - for (String executedSQL : sql) { - if (executedSQL.matches(expected[hits])) { - hits++; } } + } - if (hits != expected.length) { - StringBuilder sb = new StringBuilder(); - sb.append("Did not find SQL in expected order : ").append(_nl); - for (String s : expected) { - sb.append(s).append(_nl); - } + public enum SQLAssertType { + SQL, NotSQL, ContainsSQL, AllSQLInOrder, AllExactSQLInOrder, + AllSQLAnyOrder, NoneSQLAnyOrder, AnySQLAnyOrder + }; - sb.append("SQL Statements issued : "); - for (String s : sql) { - sb.append(s).append(_nl); + public class SQLAssertions { + SQLAssertType type; + String[] template; + + public SQLAssertions(SQLAssertType type, String... template) { + this.type = type; + this.template = template; + } + + public void validate() { + switch (type) { + case SQL: + assertSQL(template[0]); + break; + case NotSQL: + assertNotSQL(template[0]); + break; + case ContainsSQL: + assertContainsSQL(template[0]); + break; + case AllSQLInOrder: + assertAllSQLInOrder(template); + break; + case AllExactSQLInOrder: + assertAllExactSQLInOrder(template); + break; + case AllSQLAnyOrder: + assertAllSQLAnyOrder(template); + break; + case AnySQLAnyOrder: + assertAnySQLAnyOrder(template); + break; + case NoneSQLAnyOrder: + assertNoneSQLAnyOrder(template); } - fail(sb.toString()); } } } diff --git a/openjpa-persistence/pom.xml b/openjpa-persistence/pom.xml index a90f40c7d..8d1560eb6 100644 --- a/openjpa-persistence/pom.xml +++ b/openjpa-persistence/pom.xml @@ -39,6 +39,11 @@ openjpa-kernel ${pom.version} + + org.apache.openjpa + openjpa-jdbc + ${pom.version} + org.apache.geronimo.specs geronimo-jpa_2.0_spec diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java index dfd329727..4182325fd 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java @@ -28,6 +28,7 @@ import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.lang.reflect.Array; +import java.sql.Connection; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; @@ -47,6 +48,8 @@ import org.apache.openjpa.conf.OpenJPAConfiguration; import org.apache.openjpa.ee.ManagedRuntime; import org.apache.openjpa.enhance.PCEnhancer; import org.apache.openjpa.enhance.PCRegistry; +import org.apache.openjpa.jdbc.conf.JDBCConfiguration; +import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration; import org.apache.openjpa.kernel.AbstractBrokerFactory; import org.apache.openjpa.kernel.Broker; import org.apache.openjpa.kernel.DelegatingBroker; @@ -61,6 +64,8 @@ import org.apache.openjpa.kernel.QueryFlushModes; import org.apache.openjpa.kernel.QueryLanguages; import org.apache.openjpa.kernel.Seq; import org.apache.openjpa.kernel.jpql.JPQLParser; +import org.apache.openjpa.lib.conf.Configuration; +import org.apache.openjpa.lib.conf.IntValue; import org.apache.openjpa.lib.util.Closeable; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.meta.ClassMetaData; @@ -459,6 +464,26 @@ public class EntityManagerImpl return (T) _broker.find(oid, true, this); } + public T find(Class cls, Object oid, LockModeType mode) { + return find(cls, oid, mode, null); + } + + @SuppressWarnings("unchecked") + public T find(Class cls, Object oid, LockModeType mode, + Map properties) { + assertNotCloseInvoked(); + if (mode != LockModeType.NONE) + _broker.assertActiveTransaction(); + + boolean fcPushed = pushLockProperties(mode, properties); + try { + oid = _broker.newObjectId(cls, oid); + return (T) _broker.find(oid, true, this); + } finally { + popLockProperties(fcPushed); + } + } + @SuppressWarnings("unchecked") public T[] findAll(Class cls, Object... oids) { if (oids.length == 0) @@ -695,6 +720,23 @@ public class EntityManagerImpl _broker.refresh(entity, this); } + public void refresh(Object entity, LockModeType mode) { + refresh(entity, mode, null); + } + + public void refresh(Object entity, LockModeType mode, + Map properties) { + assertNotCloseInvoked(); + _broker.assertWriteOperation(); + + boolean fcPushed = pushLockProperties(mode, properties); + try { + _broker.refresh(entity, this); + } finally { + popLockProperties(fcPushed); + } + } + public void refreshAll() { assertNotCloseInvoked(); _broker.assertWriteOperation(); @@ -1049,6 +1091,20 @@ public class EntityManagerImpl _broker.lock(entity, toLockLevel(mode), timeout, this); } + public void lock(Object entity, LockModeType mode, + Map properties) { + assertNotCloseInvoked(); + _broker.assertActiveTransaction(); + + boolean fcPushed = pushLockProperties(mode, properties); + try { + _broker.lock(entity, toLockLevel(mode), _broker + .getFetchConfiguration().getLockTimeout(), this); + } finally { + popLockProperties(fcPushed); + } + } + public void lockAll(Collection entities) { assertNotCloseInvoked(); _broker.lockAll(entities, this); @@ -1071,23 +1127,38 @@ public class EntityManagerImpl * Translate our internal lock level to a javax.persistence enum value. */ static LockModeType fromLockLevel(int level) { - if (level < LockLevels.LOCK_READ) + if (level < JPA2LockLevels.LOCK_OPTIMISTIC) return null; - if (level < LockLevels.LOCK_WRITE) + if (level < JPA2LockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT) return LockModeType.READ; - return LockModeType.WRITE; + if (level < JPA2LockLevels.LOCK_PESSIMISTIC_READ) + return LockModeType.WRITE; + if (level < JPA2LockLevels.LOCK_PESSIMISTIC_WRITE) + return LockModeType.PESSIMISTIC; +// TODO: return LockModeType.PESSIMISTIC_READ; + if (level < JPA2LockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT) + return LockModeType.PESSIMISTIC; +// TODO: return LockModeType.PESSIMISTIC_WRITE; + return LockModeType.PESSIMISTIC_FORCE_INCREMENT; } /** * Translate the javax.persistence enum value to our internal lock level. */ static int toLockLevel(LockModeType mode) { - if (mode == null) + if (mode == null || mode == LockModeType.NONE) return LockLevels.LOCK_NONE; - if (mode == LockModeType.READ) + if (mode == LockModeType.READ || mode == LockModeType.OPTIMISTIC) return LockLevels.LOCK_READ; - if (mode == LockModeType.WRITE) + if (mode == LockModeType.WRITE + || mode == LockModeType.OPTIMISTIC_FORCE_INCREMENT) return LockLevels.LOCK_WRITE; +// TODO: if (mode == LockModeType.PESSIMISTIC_READ) +// TODO: return LockLevels.LOCK_PESSIMISTIC_READ; + if (mode == LockModeType.PESSIMISTIC) + return JPA2LockLevels.LOCK_PESSIMISTIC_WRITE; + if (mode == LockModeType.PESSIMISTIC_FORCE_INCREMENT) + return JPA2LockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT; throw new ArgumentException(mode.toString(), null, null, true); } @@ -1430,19 +1501,7 @@ public class EntityManagerImpl return createQuery(jpql); } - public T find(Class entityClass, Object primaryKey, - LockModeType lockMode) { - throw new UnsupportedOperationException( - "JPA 2.0 - Method not yet implemented"); - } - - public T find(Class entityClass, Object primaryKey, - LockModeType lockMode, Map properties) { - throw new UnsupportedOperationException( - "JPA 2.0 - Method not yet implemented"); - } - - /* + /* * @see javax.persistence.EntityManager#getProperties() * * This does not return the password property. @@ -1469,23 +1528,6 @@ public class EntityManagerImpl return _broker.getSupportedProperties(); } - public void lock(Object entity, LockModeType lockMode, Map properties) { - throw new UnsupportedOperationException( - "JPA 2.0 - Method not yet implemented"); - } - - public void refresh(Object entity, LockModeType lockMode) { - throw new UnsupportedOperationException( - "JPA 2.0 - Method not yet implemented"); - } - - public void refresh(Object entity, LockModeType lockMode, Map properties) { - throw new UnsupportedOperationException( - "JPA 2.0 - Method not yet implemented"); - } - public T unwrap(Class cls) { throw new UnsupportedOperationException( "JPA 2.0 - Method not yet implemented"); @@ -1504,4 +1546,135 @@ public class EntityManagerImpl return _ret; } + private enum FetchConfigProperty { + LockTimeout, ReadLockLevel, WriteLockLevel + }; + + private boolean setFetchConfigProperty(FetchConfigProperty[] validProps, + Map properties) { + boolean fcPushed = false; + if (properties != null && properties.size() > 0) { + Configuration conf = _broker.getConfiguration(); + Set inKeys = properties.keySet(); + for (String inKey : inKeys) { + for (FetchConfigProperty validProp : validProps) { + String validPropStr = validProp.toString(); + Set validPropKeys = conf + .getPropertyKeys(validPropStr); + + if (validPropKeys.contains(inKey)) { + FetchConfiguration fCfg = _broker + .getFetchConfiguration(); + IntValue intVal = new IntValue(inKey); + try { + Object setValue = properties.get(inKey); + if (setValue instanceof String) { + intVal.setString((String) setValue); + } else if (Number.class.isAssignableFrom(setValue + .getClass())) { + intVal.setObject(setValue); + } else { + intVal.setString(setValue.toString()); + } + int value = intVal.get(); + switch (validProp) { + case LockTimeout: + if (!fcPushed) { + fCfg = _broker.pushFetchConfiguration(); + fcPushed = true; + } + fCfg.setLockTimeout(value); + break; + case ReadLockLevel: + if (value != LockLevels.LOCK_NONE + && value != fCfg.getReadLockLevel()) { + if (!fcPushed) { + fCfg = _broker.pushFetchConfiguration(); + fcPushed = true; + } + fCfg.setReadLockLevel(value); + } + break; + case WriteLockLevel: + if (value != LockLevels.LOCK_NONE + && value != fCfg.getWriteLockLevel()) { + if (!fcPushed) { + fCfg = _broker.pushFetchConfiguration(); + fcPushed = true; + } + fCfg.setWriteLockLevel(value); + } + break; + } + } catch (Exception e) { + // silently ignore the property + } + break; // for(String inKey : inKeys) + } + } + } + } + return fcPushed; + } + + private boolean pushLockProperties(LockModeType mode, + Map properties) { + boolean fcPushed = false; + // handle properties in map first + if (properties != null) { + fcPushed = setFetchConfigProperty(new FetchConfigProperty[] { + FetchConfigProperty.LockTimeout, + FetchConfigProperty.ReadLockLevel, + FetchConfigProperty.WriteLockLevel }, properties); + } + // override with the specific lockMode, if needed. + int setReadLevel = toLockLevel(mode); + if (setReadLevel != JPA2LockLevels.LOCK_NONE) { + // Set overriden read lock level + FetchConfiguration fCfg = _broker.getFetchConfiguration(); + int curReadLevel = fCfg.getReadLockLevel(); + if (setReadLevel != curReadLevel) { + if (!fcPushed) { + fCfg = _broker.pushFetchConfiguration(); + fcPushed = true; + } + fCfg.setReadLockLevel(setReadLevel); + } + // Set overriden isolation level for pessimistic-read/write + if (((JDBCConfiguration) _broker.getConfiguration()) + .getDBDictionaryInstance().supportIsolationForUpdate()) { + switch (setReadLevel) { + case JPA2LockLevels.LOCK_PESSIMISTIC_READ: + fcPushed = setIsolationForPessimisticLock(fCfg, fcPushed, + Connection.TRANSACTION_REPEATABLE_READ); + break; + + case JPA2LockLevels.LOCK_PESSIMISTIC_WRITE: + case JPA2LockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT: + fcPushed = setIsolationForPessimisticLock(fCfg, fcPushed, + Connection.TRANSACTION_SERIALIZABLE); + break; + + default: + } + } + } + return fcPushed; + } + + private boolean setIsolationForPessimisticLock(FetchConfiguration fCfg, + boolean fcPushed, int level) { + if (!fcPushed) { + fCfg = _broker.pushFetchConfiguration(); + fcPushed = true; + } + ((JDBCFetchConfiguration) fCfg).setIsolation(level); + return fcPushed; + } + + private void popLockProperties(boolean fcPushed) { + if (fcPushed) { + _broker.popFetchConfiguration(); + } + } } diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/JPA2LockLevels.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/JPA2LockLevels.java new file mode 100644 index 000000000..79cb550cc --- /dev/null +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/JPA2LockLevels.java @@ -0,0 +1,56 @@ +/* + * 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; + +import org.apache.openjpa.kernel.LockLevels; + +/** + * Standard object lock levels. + * + * @author Albert Lee + * @since 2.0.0 + */ +public interface JPA2LockLevels extends LockLevels{ + + /** + * Generic optimistic read lock level. Value of 10. + */ + public static final int LOCK_OPTIMISTIC = LOCK_READ; + + /** + * Generic optimistic write lock level. Value of 20. + */ + public static final int LOCK_OPTIMISTIC_FORCE_INCREMENT = LOCK_WRITE; + + /** + * Generic pessimistic read lock level. Value of 30. + */ + public static final int LOCK_PESSIMISTIC_READ = 30; + + /** + * Generic pessimistic write lock level. Value of 40. + */ + public static final int LOCK_PESSIMISTIC_WRITE = 40; + + /** + * Generic pessimistic force increment level. Value of 50. + */ + public static final int LOCK_PESSIMISTIC_FORCE_INCREMENT = 50; + +} diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/JPA2LockManager.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/JPA2LockManager.java new file mode 100644 index 000000000..679d1b66b --- /dev/null +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/JPA2LockManager.java @@ -0,0 +1,69 @@ +/* + * 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; + +import org.apache.openjpa.conf.OpenJPAConfiguration; +import org.apache.openjpa.jdbc.kernel.PessimisticLockManager; +import org.apache.openjpa.jdbc.sql.Select; +import org.apache.openjpa.kernel.LockLevels; +import org.apache.openjpa.kernel.OpenJPAStateManager; +import org.apache.openjpa.lib.log.Log; + +/** + * Test JPA 2.0 LockTypeMode semantics using JPA 2.0 "jpa2" + * lock manager. + * + * @author Albert Lee + * @since 2.0.0 + */ +public class JPA2LockManager extends PessimisticLockManager { + + /* + * (non-Javadoc) + * @see org.apache.openjpa.jdbc.kernel.PessimisticLockManager + * #selectForUpdate(org.apache.openjpa.jdbc.sql.Select,int) + */ + public boolean selectForUpdate(Select sel, int lockLevel) { + return (lockLevel >= JPA2LockLevels.LOCK_PESSIMISTIC_READ) + ? super.selectForUpdate(sel, lockLevel) : false; + } + + /* + * (non-Javadoc) + * @see org.apache.openjpa.jdbc.kernel.PessimisticLockManager# + * lockInternal(org.apache.openjpa.kernel.OpenJPAStateManager, int, int, + * java.lang.Object) + */ + protected void lockInternal(OpenJPAStateManager sm, int level, int timeout, + Object sdata) { + if (level >= JPA2LockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT) { + setVersionCheckOnReadLock(true); + setVersionUpdateOnWriteLock(true); + super.lockInternal(sm, level, timeout, sdata); + } else if (level >= JPA2LockLevels.LOCK_PESSIMISTIC_READ) { + setVersionCheckOnReadLock(true); + setVersionUpdateOnWriteLock(false); + super.lockInternal(sm, level, timeout, sdata); + } else if (level >= JPA2LockLevels.LOCK_OPTIMISTIC) { + setVersionCheckOnReadLock(true); + setVersionUpdateOnWriteLock(true); + optimisticLockInternal(sm, level, timeout, sdata); + } + } +} diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/LockTimeoutException.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/LockTimeoutException.java new file mode 100644 index 000000000..ebca97d85 --- /dev/null +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/LockTimeoutException.java @@ -0,0 +1,112 @@ +/* + * 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; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; + +import org.apache.openjpa.util.ExceptionInfo; +import org.apache.openjpa.util.Exceptions; +import org.apache.openjpa.util.StoreException; + +/** + * Pessimistic lock timeout violation. + * + * @since 2.0.0 + * @nojavadoc + */ +public class LockTimeoutException + extends javax.persistence.LockTimeoutException + implements Serializable, ExceptionInfo { + + private static final long serialVersionUID = -3221030392419625394L; + + private transient boolean _fatal = false; + private transient Object _failed = null; + private transient Throwable[] _nested = null; + + public LockTimeoutException(String msg, Throwable[] nested, + Object failed, boolean fatal) { + super(msg); + _nested = nested; + _failed = failed; + _fatal = fatal; + } + + public int getType() { + return STORE; + } + + public int getSubtype() { + return StoreException.LOCK; + } + + public boolean isFatal() { + return _fatal; + } + + public Throwable getCause() { + return PersistenceExceptions.getCause(_nested); + } + + public Throwable[] getNestedThrowables() { + return (_nested == null) ? Exceptions.EMPTY_THROWABLES : _nested; + } + + public Object getFailedObject() { + return _failed; + } + + public String toString() { + return Exceptions.toString(this); + } + + public void printStackTrace() { + printStackTrace(System.err); + } + + public void printStackTrace(PrintStream out) { + super.printStackTrace(out); + Exceptions.printNestedThrowables(this, out); + } + + public void printStackTrace(PrintWriter out) { + super.printStackTrace(out); + Exceptions.printNestedThrowables(this, out); + } + + private void writeObject(ObjectOutputStream out) + throws IOException { + out.writeBoolean(_fatal); + out.writeObject(Exceptions.replaceFailedObject(_failed)); + out.writeObject(Exceptions.replaceNestedThrowables(_nested)); + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException { + _fatal = in.readBoolean(); + _failed = in.readObject(); + _nested = (Throwable[]) in.readObject(); + } +} + diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OptimisticLockException.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OptimisticLockException.java index 3653512b2..a8944e512 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OptimisticLockException.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OptimisticLockException.java @@ -57,7 +57,7 @@ public class OptimisticLockException } public int getSubtype() { - return StoreException.OPTIMISTIC; + return StoreException.LOCK; } public boolean isFatal() { diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java index ecd046c8a..c14396035 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java @@ -22,6 +22,7 @@ import java.lang.reflect.InvocationTargetException; import org.apache.openjpa.kernel.Broker; import org.apache.openjpa.util.Exceptions; +import org.apache.openjpa.util.LockException; import org.apache.openjpa.util.NoTransactionException; import org.apache.openjpa.util.ObjectExistsException; import org.apache.openjpa.util.ObjectNotFoundException; @@ -64,6 +65,8 @@ public class PersistenceExceptions RuntimeException ex = toPersistenceException(re); if (!(ex instanceof NonUniqueResultException) && !(ex instanceof NoResultException) + && !(ex instanceof LockTimeoutException) + && !(ex instanceof QueryTimeoutException) && !throwing) { try { throwing = true; @@ -152,11 +155,32 @@ public class PersistenceExceptions (ke.getMessage(), getNestedThrowables(ke), getFailedObject(ke), ke.isFatal()); } 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()); + } else if (ke.getSubtype() == StoreException.LOCK + || cause instanceof LockException) { + LockException lockEx = (LockException) + (ke instanceof LockException ? ke : cause); + if (lockEx != null && lockEx.getLockLevel() >= + JPA2LockLevels.LOCK_PESSIMISTIC_READ) { + if (lockEx.isRecoverable()) { + e = new org.apache.openjpa.persistence + .LockTimeoutException( + ke.getMessage(), getNestedThrowables(ke), + getFailedObject(ke), ke.isFatal()); + } else { + e = new org.apache.openjpa.persistence + .PessimisticLockException( + ke.getMessage(), getNestedThrowables(ke), + getFailedObject(ke), ke.isFatal()); + } + } else { + e = new org.apache.openjpa.persistence.OptimisticLockException( + ke.getMessage(), getNestedThrowables(ke), + getFailedObject(ke), ke.isFatal()); + } } else if (ke.getSubtype() == StoreException.OBJECT_EXISTS || cause instanceof ObjectExistsException) { e = new org.apache.openjpa.persistence.EntityExistsException diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java index 5bd7d9c2d..a0215a242 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java @@ -30,6 +30,7 @@ import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.MissingResourceException; + import javax.persistence.spi.PersistenceUnitInfo; import javax.persistence.spi.PersistenceUnitTransactionType; @@ -106,6 +107,32 @@ public class PersistenceProductDerivation PersistenceMetaDataFactory.class.getName()); conf.addValue(new EntityManagerFactoryValue()); + + conf.readLockLevel.setAlias("optimistic", String + .valueOf(JPA2LockLevels.LOCK_OPTIMISTIC)); + conf.readLockLevel.setAlias("optimistic-force-increment", String + .valueOf(JPA2LockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT)); + conf.readLockLevel.setAlias("pessimistic-read", String + .valueOf(JPA2LockLevels.LOCK_PESSIMISTIC_READ)); + conf.readLockLevel.setAlias("pessimistic-write", String + .valueOf(JPA2LockLevels.LOCK_PESSIMISTIC_WRITE)); + conf.readLockLevel.setAlias("pessimistic-force-increment", String + .valueOf(JPA2LockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT)); + + conf.writeLockLevel.setAlias("optimistic", String + .valueOf(JPA2LockLevels.LOCK_OPTIMISTIC)); + conf.writeLockLevel.setAlias("optimistic-force-increment", String + .valueOf(JPA2LockLevels.LOCK_OPTIMISTIC_FORCE_INCREMENT)); + conf.writeLockLevel.setAlias("pessimistic-read", String + .valueOf(JPA2LockLevels.LOCK_PESSIMISTIC_READ)); + conf.writeLockLevel.setAlias("pessimistic-write", String + .valueOf(JPA2LockLevels.LOCK_PESSIMISTIC_WRITE)); + conf.writeLockLevel.setAlias("pessimistic-force-increment", String + .valueOf(JPA2LockLevels.LOCK_PESSIMISTIC_FORCE_INCREMENT)); + + conf.lockManagerPlugin.setAlias("jpa2", + JPA2LockManager.class.getName()); + return true; } @@ -118,8 +145,8 @@ public class PersistenceProductDerivation OpenJPAConfigurationImpl conf = (OpenJPAConfigurationImpl) c; conf.metaFactoryPlugin.setDefault(SPEC_JPA.getName()); conf.metaFactoryPlugin.setString(SPEC_JPA.getName()); - conf.lockManagerPlugin.setDefault("version"); - conf.lockManagerPlugin.setString("version"); + conf.lockManagerPlugin.setDefault("jpa2"); + conf.lockManagerPlugin.setString("jpa2"); conf.nontransactionalWrite.setDefault("true"); conf.nontransactionalWrite.set(true); return true; diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PessimisticLockException.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PessimisticLockException.java new file mode 100644 index 000000000..6e93b00db --- /dev/null +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PessimisticLockException.java @@ -0,0 +1,112 @@ +/* + * 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; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; + +import org.apache.openjpa.util.ExceptionInfo; +import org.apache.openjpa.util.Exceptions; +import org.apache.openjpa.util.StoreException; + +/** + * Pessimistic concurrency violation. + * + * @since 2.0.0 + * @nojavadoc + */ +public class PessimisticLockException + extends javax.persistence.PessimisticLockException + implements Serializable, ExceptionInfo { + + private static final long serialVersionUID = -7241629816016553676L; + + private transient boolean _fatal = false; + private transient Object _failed = null; + private transient Throwable[] _nested = null; + + public PessimisticLockException(String msg, Throwable[] nested, + Object failed, boolean fatal) { + super(msg); + _nested = nested; + _failed = failed; + _fatal = fatal; + } + + public int getType() { + return STORE; + } + + public int getSubtype() { + return StoreException.LOCK; + } + + public boolean isFatal() { + return _fatal; + } + + public Throwable getCause() { + return PersistenceExceptions.getCause(_nested); + } + + public Throwable[] getNestedThrowables() { + return (_nested == null) ? Exceptions.EMPTY_THROWABLES : _nested; + } + + public Object getFailedObject() { + return _failed; + } + + public String toString() { + return Exceptions.toString(this); + } + + public void printStackTrace() { + printStackTrace(System.err); + } + + public void printStackTrace(PrintStream out) { + super.printStackTrace(out); + Exceptions.printNestedThrowables(this, out); + } + + public void printStackTrace(PrintWriter out) { + super.printStackTrace(out); + Exceptions.printNestedThrowables(this, out); + } + + private void writeObject(ObjectOutputStream out) + throws IOException { + out.writeBoolean(_fatal); + out.writeObject(Exceptions.replaceFailedObject(_failed)); + out.writeObject(Exceptions.replaceNestedThrowables(_nested)); + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException { + _fatal = in.readBoolean(); + _failed = in.readObject(); + _nested = (Throwable[]) in.readObject(); + } +} + diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryTimeoutException.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryTimeoutException.java new file mode 100644 index 000000000..65418882c --- /dev/null +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryTimeoutException.java @@ -0,0 +1,110 @@ +/* + * 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; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; + +import org.apache.openjpa.util.ExceptionInfo; +import org.apache.openjpa.util.Exceptions; +import org.apache.openjpa.util.StoreException; + +/** + * Query lock timeout violation. + * + * @since 2.0.0 + * @nojavadoc + */ +public class QueryTimeoutException + extends javax.persistence.QueryTimeoutException + implements Serializable, ExceptionInfo { + + private transient boolean _fatal = false; + private transient Object _failed = null; + private transient Throwable[] _nested = null; + + public QueryTimeoutException(String msg, Throwable[] nested, + Object failed, boolean fatal) { + super(msg); + _nested = nested; + _failed = failed; + _fatal = fatal; + } + + public int getType() { + return STORE; + } + + public int getSubtype() { + return StoreException.LOCK; + } + + public boolean isFatal() { + return _fatal; + } + + public Throwable getCause() { + return PersistenceExceptions.getCause(_nested); + } + + public Throwable[] getNestedThrowables() { + return (_nested == null) ? Exceptions.EMPTY_THROWABLES : _nested; + } + + public Object getFailedObject() { + return _failed; + } + + public String toString() { + return Exceptions.toString(this); + } + + public void printStackTrace() { + printStackTrace(System.err); + } + + public void printStackTrace(PrintStream out) { + super.printStackTrace(out); + Exceptions.printNestedThrowables(this, out); + } + + public void printStackTrace(PrintWriter out) { + super.printStackTrace(out); + Exceptions.printNestedThrowables(this, out); + } + + private void writeObject(ObjectOutputStream out) + throws IOException { + out.writeBoolean(_fatal); + out.writeObject(Exceptions.replaceFailedObject(_failed)); + out.writeObject(Exceptions.replaceNestedThrowables(_nested)); + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException { + _fatal = in.readBoolean(); + _failed = in.readObject(); + _nested = (Throwable[]) in.readObject(); + } +} + diff --git a/openjpa-project/src/doc/manual/ref_guide_conf.xml b/openjpa-project/src/doc/manual/ref_guide_conf.xml index ecd519a85..024c0c9a6 100644 --- a/openjpa-project/src/doc/manual/ref_guide_conf.xml +++ b/openjpa-project/src/doc/manual/ref_guide_conf.xml @@ -2116,12 +2116,12 @@ for managing bidirectional relations upon a flush. See LockManager -Default: version +Default: jpa2 Possible values: none, -sjvm, pessimistic, -version +sjvm, version, +pessimistic, jpa2 Description: A plugin string (see @@ -2727,7 +2727,10 @@ ReadLockLevel Possible values: none, -read, write, numeric values for +read, write, +optimistic, optimistic-force-increment, +pessimistic-read, pessimistic-write, +pessimistic-force-increment, numeric values for lock-manager specific lock levels @@ -3090,7 +3093,10 @@ WriteLockLevel Possible values: none, -read, write, numeric values for +read, write, +optimistic, optimistic-force-increment, +pessimistic-read, pessimistic-write, +pessimistic-force-increment, numeric values for lock-manager specific lock levels