mirror of https://github.com/apache/openjpa.git
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
This commit is contained in:
parent
62c76e42ea
commit
22a0f837e1
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> erroStates = sqlStateCodes.get(type);
|
||||
if (erroStates != null && erroStates.contains(errorState)) {
|
||||
errorType = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (errorType) {
|
||||
case StoreException.LOCK:
|
||||
return new LockException(msg);
|
||||
case StoreException.OBJECT_EXISTS:
|
||||
return new ObjectExistsException(msg);
|
||||
case StoreException.OBJECT_NOT_FOUND:
|
||||
return new ObjectNotFoundException(msg);
|
||||
case StoreException.OPTIMISTIC:
|
||||
return new OptimisticException(msg);
|
||||
case StoreException.REFERENTIAL_INTEGRITY:
|
||||
return new ReferentialIntegrityException(msg);
|
||||
default:
|
||||
return new StoreException(msg);
|
||||
Boolean recoverable = null;
|
||||
int errorType = StoreException.GENERAL;
|
||||
for (Integer type : sqlStateCodes.keySet()) {
|
||||
Set<String> errorStates = sqlStateCodes.get(type);
|
||||
if (errorStates != null) {
|
||||
recoverable = matchErrorState(type, errorStates, ex);
|
||||
if (recoverable != null) {
|
||||
errorType = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
StoreException storeEx;
|
||||
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<String> errorStates,
|
||||
SQLException ex) {
|
||||
String errorState = ex.getSQLState();
|
||||
return errorStates.contains(errorState) ? Boolean.FALSE : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<String> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,24 +29,26 @@
|
|||
<sql-state-codes>
|
||||
|
||||
<dictionary class="org.apache.openjpa.jdbc.sql.DB2Dictionary">
|
||||
<lock>-911,-913</lock>
|
||||
<referential-integrity>-407,-530,-531,-532,-543,-544,-545,-603,-667,-803</referential-integrity>
|
||||
<lock>40001,57033,57014</lock>
|
||||
<referential-integrity>23502,42912,23001,23504,23511,23512,23513,23515,23520,23505</referential-integrity>
|
||||
<object-exists></object-exists>
|
||||
<object-not-found></object-not-found>
|
||||
<optimistic></optimistic>
|
||||
</dictionary>
|
||||
|
||||
<dictionary class="org.apache.openjpa.jdbc.sql.DerbyDictionary">
|
||||
<lock>40001</lock>
|
||||
<lock>40001,40XL1,40XL2</lock>
|
||||
<referential-integrity>22001,22005,23502,23503,23513,X0Y32</referential-integrity>
|
||||
<object-exists>23505</object-exists>
|
||||
<object-not-found></object-not-found>
|
||||
<optimistic>40XL1,40001</optimistic>
|
||||
<optimistic></optimistic>
|
||||
</dictionary>
|
||||
|
||||
<dictionary class="org.apache.openjpa.jdbc.sql.SQLServerDictionary">
|
||||
<lock>1205</lock>
|
||||
<referential-integrity>544,2601,2627,8114,8115</referential-integrity>
|
||||
<object-exists></object-exists>
|
||||
<object-not-found></object-not-found>
|
||||
<optimistic>1205</optimistic>
|
||||
</dictionary>
|
||||
|
||||
|
@ -91,24 +93,51 @@
|
|||
</dictionary>
|
||||
|
||||
<dictionary class="org.apache.openjpa.jdbc.sql.H2Dictionary">
|
||||
<lock></lock>
|
||||
<referential-integrity>22003,22012,22025,23000,23001</referential-integrity>
|
||||
<object-exists></object-exists>
|
||||
<object-not-found></object-not-found>
|
||||
<optimistic></optimistic>
|
||||
</dictionary>
|
||||
|
||||
<dictionary class="org.apache.openjpa.jdbc.sql.HSQLDictionary">
|
||||
<lock></lock>
|
||||
<referential-integrity>-9</referential-integrity>
|
||||
<object-exists></object-exists>
|
||||
<object-not-found></object-not-found>
|
||||
<optimistic></optimistic>
|
||||
</dictionary>
|
||||
|
||||
<dictionary class="org.apache.openjpa.jdbc.sql.InformixDictionary">
|
||||
<lock></lock>
|
||||
<referential-integrity>-239,-268,-692,-11030</referential-integrity>
|
||||
<object-exists></object-exists>
|
||||
<object-not-found></object-not-found>
|
||||
<optimistic></optimistic>
|
||||
</dictionary>
|
||||
|
||||
<dictionary class="org.apache.openjpa.jdbc.sql.InterbaseDictionary">
|
||||
<lock></lock>
|
||||
<referential-integrity></referential-integrity>
|
||||
<object-exists></object-exists>
|
||||
<object-not-found></object-not-found>
|
||||
<optimistic></optimistic>
|
||||
</dictionary>
|
||||
|
||||
<dictionary class="org.apache.openjpa.jdbc.sql.FirebirdDictionary">
|
||||
<lock></lock>
|
||||
<referential-integrity></referential-integrity>
|
||||
<object-exists></object-exists>
|
||||
<object-not-found></object-not-found>
|
||||
<optimistic></optimistic>
|
||||
</dictionary>
|
||||
|
||||
<dictionary class="org.apache.openjpa.jdbc.sql.JDataStoreDictionary">
|
||||
<lock></lock>
|
||||
<referential-integrity></referential-integrity>
|
||||
<object-exists></object-exists>
|
||||
<object-not-found></object-not-found>
|
||||
<optimistic></optimistic>
|
||||
</dictionary>
|
||||
|
||||
<dictionary class="org.apache.openjpa.jdbc.sql.MySQLDictionary">
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 + ".*");
|
||||
}
|
||||
|
||||
|
|
|
@ -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<LockTask> 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<LockTask> getTasks() {
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public void setTasks(Collection<LockTask> 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() + "}";
|
||||
}
|
||||
}
|
|
@ -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<LockTask> tasks = new ArrayList<LockTask>();
|
||||
tasks.add(t);
|
||||
|
||||
Collection<LockStory> stories = new ArrayList<LockStory>();
|
||||
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<String, Object> props =
|
||||
new HashMap<String, Object>(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<String, Object> props =
|
||||
new HashMap<String, Object>(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<String, Object> props =
|
||||
new HashMap<String, Object>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() + "]";
|
||||
}
|
||||
|
||||
}
|
|
@ -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<LockStory> stories;
|
||||
|
||||
@ManyToOne(cascade = { CascadeType.ALL })
|
||||
private LockEmployee employee;
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Collection<LockStory> getStories() {
|
||||
return stories;
|
||||
}
|
||||
|
||||
public void setStories(Collection<LockStory> 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() + "}";
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -34,4 +34,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|||
@Retention(RUNTIME)
|
||||
public @interface AllowFailure {
|
||||
boolean value() default true;
|
||||
String msg() default "";
|
||||
}
|
||||
|
|
|
@ -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<EMFKey, OpenJPAEntityManagerFactorySPI> {
|
||||
|
|
|
@ -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<String> sql = new ArrayList<String>();
|
||||
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<String> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,11 @@
|
|||
<artifactId>openjpa-kernel</artifactId>
|
||||
<version>${pom.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.openjpa</groupId>
|
||||
<artifactId>openjpa-jdbc</artifactId>
|
||||
<version>${pom.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.geronimo.specs</groupId>
|
||||
<artifactId>geronimo-jpa_2.0_spec</artifactId>
|
||||
|
|
|
@ -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> T find(Class<T> cls, Object oid, LockModeType mode) {
|
||||
return find(cls, oid, mode, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T find(Class<T> cls, Object oid, LockModeType mode,
|
||||
Map<String, Object> 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> T[] findAll(Class<T> 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<String, Object> 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<String, Object> 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> T find(Class<T> entityClass, Object primaryKey,
|
||||
LockModeType lockMode) {
|
||||
throw new UnsupportedOperationException(
|
||||
"JPA 2.0 - Method not yet implemented");
|
||||
}
|
||||
|
||||
public <T> T find(Class<T> entityClass, Object primaryKey,
|
||||
LockModeType lockMode, Map<String, Object> 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<String,
|
||||
Object> 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<String,
|
||||
Object> properties) {
|
||||
throw new UnsupportedOperationException(
|
||||
"JPA 2.0 - Method not yet implemented");
|
||||
}
|
||||
|
||||
public <T> T unwrap(Class<T> 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<String, Object> properties) {
|
||||
boolean fcPushed = false;
|
||||
if (properties != null && properties.size() > 0) {
|
||||
Configuration conf = _broker.getConfiguration();
|
||||
Set<String> inKeys = properties.keySet();
|
||||
for (String inKey : inKeys) {
|
||||
for (FetchConfigProperty validProp : validProps) {
|
||||
String validPropStr = validProp.toString();
|
||||
Set<String> 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<String, Object> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ public class OptimisticLockException
|
|||
}
|
||||
|
||||
public int getSubtype() {
|
||||
return StoreException.OPTIMISTIC;
|
||||
return StoreException.LOCK;
|
||||
}
|
||||
|
||||
public boolean isFatal() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -2116,12 +2116,12 @@ for managing bidirectional relations upon a flush. See
|
|||
LockManager</literal>
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Default: </emphasis><literal>version</literal>
|
||||
<emphasis role="bold">Default: </emphasis><literal>jpa2</literal>
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Possible values: </emphasis><literal>none</literal>,
|
||||
<literal>sjvm</literal>, <literal>pessimistic</literal>,
|
||||
<literal>version</literal>
|
||||
<literal>sjvm</literal>, <literal>version</literal>,
|
||||
<literal>pessimistic</literal>, <literal>jpa2</literal>
|
||||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Description:</emphasis> A plugin string (see
|
||||
|
@ -2727,7 +2727,10 @@ ReadLockLevel</literal>
|
|||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Possible values: </emphasis><literal>none</literal>,
|
||||
<literal>read</literal>, <literal>write</literal>, numeric values for
|
||||
<literal>read</literal>, <literal>write</literal>,
|
||||
<literal>optimistic</literal>, <literal>optimistic-force-increment</literal>,
|
||||
<literal>pessimistic-read</literal>, <literal>pessimistic-write</literal>,
|
||||
<literal>pessimistic-force-increment</literal>, numeric values for
|
||||
lock-manager specific lock levels
|
||||
</para>
|
||||
<para>
|
||||
|
@ -3090,7 +3093,10 @@ WriteLockLevel</literal>
|
|||
</para>
|
||||
<para>
|
||||
<emphasis role="bold">Possible values: </emphasis><literal>none</literal>,
|
||||
<literal>read</literal>, <literal>write</literal>, numeric values for
|
||||
<literal>read</literal>, <literal>write</literal>,
|
||||
<literal>optimistic</literal>, <literal>optimistic-force-increment</literal>,
|
||||
<literal>pessimistic-read</literal>, <literal>pessimistic-write</literal>,
|
||||
<literal>pessimistic-force-increment</literal>, numeric values for
|
||||
lock-manager specific lock levels
|
||||
</para>
|
||||
<para>
|
||||
|
|
Loading…
Reference in New Issue