You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by jr...@apache.org on 2010/09/21 21:27:09 UTC
svn commit: r999559 - in /openjpa/trunk:
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/
openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/
Author: jrbauer
Date: Tue Sep 21 19:27:08 2010
New Revision: 999559
URL: http://svn.apache.org/viewvc?rev=999559&view=rev
Log:
OPENJPA-1809 Modified syncVersion exception path to pass the lock level into the exception producer. This eventually allows the exception translator to correctly decide whether the exception was the result of a lock timeout.
Added:
openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/VersionEntity.java
Modified:
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java
Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java?rev=999559&r1=999558&r2=999559&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java Tue Sep 21 19:27:08 2010
@@ -305,8 +305,16 @@ public class JDBCStoreManager
try {
return mapping.getVersion().checkVersion(sm, this, true);
} catch (SQLException se) {
- throw SQLExceptions.getStore(se, _dict);
+ throw SQLExceptions.getStore(se, _dict, getReadLockLevel());
+ }
+ }
+
+ private int getReadLockLevel() {
+ JDBCFetchConfiguration fetch = getFetchConfiguration();
+ if (fetch != null) {
+ return fetch.getReadLockLevel();
}
+ return -1;
}
public int compareVersion(OpenJPAStateManager state, Object v1, Object v2) {
Modified: openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java?rev=999559&r1=999558&r2=999559&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java (original)
+++ openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java Tue Sep 21 19:27:08 2010
@@ -21,6 +21,11 @@ package org.apache.openjpa.persistence.l
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
@@ -32,7 +37,10 @@ import javax.persistence.TypedQuery;
import junit.framework.AssertionFailedError;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
+import org.apache.openjpa.jdbc.sql.DB2Dictionary;
import org.apache.openjpa.jdbc.sql.DBDictionary;
+import org.apache.openjpa.jdbc.sql.DerbyDictionary;
+import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.persistence.LockTimeoutException;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
@@ -65,7 +73,7 @@ public class TestPessimisticLocks extend
if (isTestsDisabled())
return;
- setUp(CLEAR_TABLES, Employee.class, Department.class, "openjpa.LockManager", "mixed");
+ setUp(CLEAR_TABLES, Employee.class, Department.class, VersionEntity.class, "openjpa.LockManager", "mixed");
EntityManager em = null;
em = emf.createEntityManager();
@@ -399,6 +407,82 @@ public class TestPessimisticLocks extend
em.getTransaction().commit();
}
+ protected Log getLog() {
+ return emf.getConfiguration().getLog("Tests");
+ }
+
+ /**
+ * This variation introduces a row level write lock in a secondary thread,
+ * issues a refresh in the main thread with a lock timeout, and expects a
+ * LockTimeoutException.
+ */
+ public void testRefreshLockTimeout() {
+
+ // Only run this test on DB2 and Derby for now. It could cause
+ // the test to hang on other platforms.
+ if (!(dict instanceof DerbyDictionary ||
+ dict instanceof DB2Dictionary)) {
+ return;
+ }
+
+ EntityManager em = emf.createEntityManager();
+
+ resetSQL();
+ VersionEntity ve = new VersionEntity();
+ int veid = new Random().nextInt();
+ ve.setId(veid);
+ ve.setName("Versioned Entity");
+
+ em.getTransaction().begin();
+ em.persist(ve);
+ em.getTransaction().commit();
+
+ em.getTransaction().begin();
+ // Assert that the department can be found and no lock mode is set
+ ve = em.find(VersionEntity.class, veid);
+ assertTrue(em.contains(ve));
+ assertTrue(em.getLockMode(ve) == LockModeType.NONE);
+ em.getTransaction().commit();
+
+ // Kick of a thread to lock the DB for update
+ ExecutorService executor = Executors.newFixedThreadPool(1);
+ Future<Boolean> result = executor.submit(new RefreshWithLock(veid, this));
+ try {
+ // Wait for the thread to lock the row
+ getLog().trace("Main: waiting");
+ synchronized (this) {
+ // The derby lock timeout is configured for 60 seconds, by default.
+ wait(70000);
+ }
+ getLog().trace("Main: done waiting");
+ Map<String,Object> props = new HashMap<String,Object>();
+ // This property does not have any effect on Derby for the locking
+ // condition produced by this test. Instead, Derby uses the
+ // lock timeout value specified in the config (pom.xml)
+ props.put("javax.persistence.lock.timeout", 5000);
+ em.getTransaction().begin();
+ getLog().trace("Main: refresh with force increment");
+ em.refresh(ve, LockModeType.PESSIMISTIC_FORCE_INCREMENT, props);
+ getLog().trace("Main: commit");
+ em.getTransaction().commit();
+ getLog().trace("Main: done commit");
+ fail("Expected LockTimeoutException");
+ } catch (Throwable t) {
+ getLog().trace("Main: exception - " + t.getMessage(), t);
+ assertTrue( t instanceof LockTimeoutException);
+ } finally {
+ try {
+ // Wake the thread and wait for the thread to finish
+ synchronized(this) {
+ this.notify();
+ }
+ result.get();
+ } catch (Throwable t) {
+ fail("Caught throwable waiting for thread finish: " + t);
+ }
+ }
+ }
+
/**
* Assert that an exception of proper type has been thrown. Also checks that
* that the exception has populated the failed object.
@@ -435,4 +519,51 @@ public class TestPessimisticLocks extend
return null;
}
+ /**
+ * Separate execution thread used to forcing a lock condition on
+ * a row in the VersionEntity table.
+ */
+ public class RefreshWithLock implements Callable<Boolean> {
+
+ private int _id;
+ private Object _monitor;
+
+ public RefreshWithLock(int id, Object monitor) {
+ _id = id;
+ _monitor = monitor;
+ }
+
+ public Boolean call() throws Exception {
+ try {
+ EntityManager em = emf.createEntityManager();
+
+ em.getTransaction().begin();
+ // Find with pessimistic force increment. Will lock row for duration of TX.
+ VersionEntity ve = em.find(VersionEntity.class, _id, LockModeType.PESSIMISTIC_FORCE_INCREMENT);
+ assertTrue(em.getLockMode(ve) == LockModeType.PESSIMISTIC_FORCE_INCREMENT);
+ // Wake up the main thread
+ getLog().trace("Thread: wake up main thread");
+ synchronized(_monitor) {
+ _monitor.notify();
+ }
+ // Wait up to 120 seconds for main thread to complete. The default derby timeout is 60 seconds.
+ try {
+ getLog().trace("Thread: waiting up to 120 secs for notify");
+ synchronized(_monitor) {
+ _monitor.wait(120000);
+ }
+ getLog().trace("Thread: done waiting");
+ } catch (Throwable t) {
+ getLog().trace("Unexpected thread interrupt",t);
+ }
+
+ em.getTransaction().commit();
+ em.close();
+ getLog().trace("Thread: done");
+ } catch (Throwable t) {
+ getLog().trace("Thread: caught - " + t.getMessage(), t);
+ }
+ return Boolean.TRUE;
+ }
+ }
}
Added: openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/VersionEntity.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/VersionEntity.java?rev=999559&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/VersionEntity.java (added)
+++ openjpa/trunk/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/VersionEntity.java Tue Sep 21 19:27:08 2010
@@ -0,0 +1,61 @@
+/*
+ * 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.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Version;
+
+@Entity
+@Table(name="LK_VERSENT")
+public class VersionEntity {
+
+ @Id
+ private int id;
+
+ private String name;
+
+ @Version
+ private int version;
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+}