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:
Albert Lee 2009-02-24 18:48:09 +00:00
parent 62c76e42ea
commit 22a0f837e1
32 changed files with 6287 additions and 132 deletions

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
/**

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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">

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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 + ".*");
}

View File

@ -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() + "}";
}
}

View File

@ -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;
}
}
}
}

View File

@ -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() + "]";
}
}

View File

@ -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() + "}";
}
}

View File

@ -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
);
}
}

View File

@ -34,4 +34,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
public @interface AllowFailure {
boolean value() default true;
String msg() default "";
}

View File

@ -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> {

View File

@ -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());
}
}
}

View File

@ -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>

View File

@ -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();
}
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -57,7 +57,7 @@ public class OptimisticLockException
}
public int getSubtype() {
return StoreException.OPTIMISTIC;
return StoreException.LOCK;
}
public boolean isFatal() {

View File

@ -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

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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>