openjpa-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From allee8...@apache.org
Subject svn commit: r747489 [1/5] - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/ openjpa-kernel/src/main/java/...
Date Tue, 24 Feb 2009 18:48:11 GMT
Author: allee8285
Date: Tue Feb 24 18:48:09 2009
New Revision: 747489

URL: http://svn.apache.org/viewvc?rev=747489&view=rev
Log:
OPENJPA-891 - Committing basic JPA2 lock mode support for em refresh, find and lock methods.

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockEmployee.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockManagerTestBase.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockStory.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockTask.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestJPA2LockManager.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestOptimisticLockManager.java   (with props)
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLockManager.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/JPA2LockLevels.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/JPA2LockManager.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/LockTimeoutException.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PessimisticLockException.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryTimeoutException.java   (with props)
Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreManager.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java
    openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionLockManager.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/LockException.java
    openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/StoreException.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestInsertOrder.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AllowFailure.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/SQLListenerTestCase.java
    openjpa/trunk/openjpa-persistence/pom.xml
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/OptimisticLockException.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceExceptions.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
    openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml

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=747489&r1=747488&r2=747489&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 Feb 24 18:48:09 2009
@@ -52,7 +52,6 @@
 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 @@
         } catch (ClassNotFoundException cnfe) {
             throw new UserException(cnfe);
         } catch (SQLException se) {
-            throw SQLExceptions.getStore(se, _dict);
+            throw SQLExceptions.getStoreSQLException(sm, se, _dict,
+                fetch.getReadLockLevel());
         }
     }
 

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java?rev=747489&r1=747488&r2=747489&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/PessimisticLockManager.java Tue Feb 24 18:48:09 2009
@@ -97,16 +97,16 @@
             // 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 @@
             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 @@
      * 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;
     }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java?rev=747489&r1=747488&r2=747489&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java Tue Feb 24 18:48:09 2009
@@ -24,6 +24,7 @@
 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.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 boolean supportIsolationForUpdate() {
+        return true;
+    }
+
     /**
      * Get the update clause for the query based on the
      * updateClause and isolationLevel hints
@@ -832,4 +838,19 @@
     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;
+    }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?rev=747489&r1=747488&r2=747489&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java Tue Feb 24 18:48:09 2009
@@ -2400,6 +2400,14 @@
     }
 
     /**
+     * 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.
      */
     public String getSelectOperation(JDBCFetchConfiguration fetch) {
@@ -4220,29 +4228,56 @@
      * 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;
     }
     
     /**

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java?rev=747489&r1=747488&r2=747489&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java Tue Feb 24 18:48:09 2009
@@ -23,6 +23,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
 
 import javax.sql.DataSource;
 
@@ -103,4 +104,19 @@
             }
         }
     }
+    
+    @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;
+    }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java?rev=747489&r1=747488&r2=747489&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/SQLExceptions.java Tue Feb 24 18:48:09 2009
@@ -22,13 +22,12 @@
 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 @@
         }
         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;
+    }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml?rev=747489&r1=747488&r2=747489&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml Tue Feb 24 18:48:09 2009
@@ -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">

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionLockManager.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionLockManager.java?rev=747489&r1=747488&r2=747489&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionLockManager.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/VersionLockManager.java Tue Feb 24 18:48:09 2009
@@ -95,6 +95,11 @@
      */
     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);

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/LockException.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/LockException.java?rev=747489&r1=747488&r2=747489&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/LockException.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/LockException.java Tue Feb 24 18:48:09 2009
@@ -37,7 +37,8 @@
         (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 @@
         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();
 	}
 }

Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/StoreException.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/StoreException.java?rev=747489&r1=747488&r2=747489&view=diff
==============================================================================
--- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/StoreException.java (original)
+++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/util/StoreException.java Tue Feb 24 18:48:09 2009
@@ -35,6 +35,8 @@
     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 int getType() {
         return STORE;
     }
+    
+    public void setRecoverable(boolean recoverable) {
+        this.recoverable = recoverable;
+    }
+
+    public boolean isRecoverable() {
+        return recoverable;
+    }
 }

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestInsertOrder.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestInsertOrder.java?rev=747489&r1=747488&r2=747489&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestInsertOrder.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/jdbc/kernel/TestInsertOrder.java Tue Feb 24 18:48:09 2009
@@ -59,7 +59,7 @@
         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 @@
         em.getTransaction().commit();
         em.close();
 
-        assertSQLOrder("INSERT INTO " + empTableName + ".*", "INSERT INTO "
+        assertAllSQLInOrder("INSERT INTO " + empTableName + ".*", "INSERT INTO "
             + taskTableName + ".*", "INSERT INTO " + storyTableName + ".*");
     }
 

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockEmployee.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockEmployee.java?rev=747489&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockEmployee.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockEmployee.java Tue Feb 24 18:48:09 2009
@@ -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() + "}";
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockEmployee.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockManagerTestBase.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockManagerTestBase.java?rev=747489&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockManagerTestBase.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockManagerTestBase.java Tue Feb 24 18:48:09 2009
@@ -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;
+            }
+        }
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockManagerTestBase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockStory.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockStory.java?rev=747489&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockStory.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockStory.java Tue Feb 24 18:48:09 2009
@@ -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() + "]";
+    }
+
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockStory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockTask.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockTask.java?rev=747489&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockTask.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockTask.java Tue Feb 24 18:48:09 2009
@@ -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() + "}";
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/lockmgr/LockTask.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message