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