You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by al...@apache.org on 2011/02/18 17:59:26 UTC

svn commit: r1072062 - in /openjpa/branches/2.1.x: openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/ openjpa-...

Author: allee8285
Date: Fri Feb 18 16:59:25 2011
New Revision: 1072062

URL: http://svn.apache.org/viewvc?rev=1072062&view=rev
Log:
OPENJPA-1943 - Apply query timeout value to pessimistic row lock statement execution.

Modified:
    openjpa/branches/2.1.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml
    openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
    openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java
    openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java
    openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java
    openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/SequencedActionsTest.java
    openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestMixedLockManagerDeadlock.java
    openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java
    openjpa/branches/2.1.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
    openjpa/branches/2.1.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java

Modified: openjpa/branches/2.1.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml
URL: http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml?rev=1072062&r1=1072061&r2=1072062&view=diff
==============================================================================
--- openjpa/branches/2.1.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml (original)
+++ openjpa/branches/2.1.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/sql-error-state-codes.xml Fri Feb 18 16:59:25 2011
@@ -7,15 +7,15 @@
  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.   
+ under the License.
 -->
 <!-- ======================================================================= -->
 <!-- Lists SQL Error State codes for specific type of faults per database    -->
@@ -47,7 +47,7 @@
 	</dictionary>
 	
 	<dictionary class="org.apache.openjpa.jdbc.sql.SQLServerDictionary">
-		<lock>1204,1205,1222,HY008</lock>
+		<lock>1204,1205,1222,HY008,40001</lock>
 		<referential-integrity>544,2601,2627,8114,8115</referential-integrity>
 		<object-exists>23000</object-exists>
 		<object-not-found></object-not-found>

Modified: openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java?rev=1072062&r1=1072061&r2=1072062&view=diff
==============================================================================
--- openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java (original)
+++ openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java Fri Feb 18 16:59:25 2011
@@ -455,10 +455,14 @@ public class BrokerImpl
     }
 
     public FetchConfiguration pushFetchConfiguration() {
+		return pushFetchConfiguration(null);
+    }
+
+    public FetchConfiguration pushFetchConfiguration(FetchConfiguration fc) {
         if (_fcs == null)
             _fcs = new LinkedList<FetchConfiguration>();
         _fcs.add(_fc);
-        _fc = (FetchConfiguration) _fc.clone();
+        _fc = (FetchConfiguration) (fc != null ? fc : _fc).clone();
         return _fc;
     }
 

Modified: openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java?rev=1072062&r1=1072061&r2=1072062&view=diff
==============================================================================
--- openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java (original)
+++ openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/DelegatingBroker.java Fri Feb 18 16:59:25 2011
@@ -154,6 +154,14 @@ public class DelegatingBroker
         }
     }
 
+    public FetchConfiguration pushFetchConfiguration(FetchConfiguration fc) {
+        try {
+            return _broker.pushFetchConfiguration(fc);
+        } catch (RuntimeException re) {
+            throw translate(re);
+        }
+    }
+
     public void popFetchConfiguration() {
         try {
             _broker.popFetchConfiguration();

Modified: openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java?rev=1072062&r1=1072061&r2=1072062&view=diff
==============================================================================
--- openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java (original)
+++ openjpa/branches/2.1.x/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreContext.java Fri Feb 18 16:59:25 2011
@@ -74,6 +74,15 @@ public interface StoreContext {
     public FetchConfiguration pushFetchConfiguration();
 
     /**
+     * Pushes the fetch configuration argument onto a stack, and makes the new configuration
+     * the active one.
+     *
+     * @since 2.1.1
+     * @return the new fetch configuration
+     */
+    public FetchConfiguration pushFetchConfiguration(FetchConfiguration fc);
+
+    /**
      * Pops the fetch configuration from the top of the stack, making the
      * next one down the active one. This returns void to avoid confusion,
      * since fetch configurations tend to be used in method-chaining

Modified: openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java?rev=1072062&r1=1072061&r2=1072062&view=diff
==============================================================================
--- openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java (original)
+++ openjpa/branches/2.1.x/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/AbstractPersistenceTestCase.java Fri Feb 18 16:59:25 2011
@@ -271,6 +271,12 @@ public abstract class AbstractPersistenc
         for (Broker b : ((AbstractBrokerFactory) JPAFacadeHelper.toBrokerFactory(emf)).getOpenBrokers()) {
             if (b != null && !b.isClosed()) {
                 EntityManager em = JPAFacadeHelper.toEntityManager(b);
+                if( em.getTransaction().isActive() ) {
+                	try {
+						em.getTransaction().rollback();
+					} catch (Exception e) {
+					}
+                }
                 closeEM(em);
             }
         }

Modified: openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/SequencedActionsTest.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/SequencedActionsTest.java?rev=1072062&r1=1072061&r2=1072062&view=diff
==============================================================================
--- openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/SequencedActionsTest.java (original)
+++ openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/SequencedActionsTest.java Fri Feb 18 16:59:25 2011
@@ -684,41 +684,93 @@ public abstract class SequencedActionsTe
                             }
                         }
                     }
-                    String testExClass = null;
-                    Throwable curThrowable = null;
                     int threadId = threadToRun;
                     if (args.length > 1) {
                         threadId = (Integer) args[1];
                     }
-                    TestThread exThread = threads.get(threadId);
-                    curThrowable = exThread.throwable;
-                    testExClass = processException(curAction, curThrowable);
-
-                    boolean exMatched = false;
-                    if (expectedExceptions != null
-                        && expectedExceptions.size() > 0) {
-                        for (Class<?> expectedException :
-                            expectedExceptions) {
-                            if (matchExpectedException(curAct, expectedException,
-                                curThrowable)) {
+                    if( threadId != -1 ) {
+                    	// test exception on a specific thread
+                        String testExClass = null;
+                        Throwable curThrowable = null;
+                        boolean exMatched = false;
+                        TestThread exThread = threads.get(threadId);
+                        curThrowable = exThread.throwable;
+                        testExClass = processException(exThread, curAction, curThrowable);
+
+                        if (expectedExceptions != null
+                            && expectedExceptions.size() > 0) {
+                            for (Class<?> expectedException :
+                                expectedExceptions) {
+                                if (matchExpectedException(curAct, expectedException,
+                                    curThrowable)) {
+                                    exMatched = true;
+                                    break;
+                                }
+                            }
+                        } else {
+                            if (curThrowable == null) {
                                 exMatched = true;
-                                break;
                             }
                         }
-                    } else {
-                        if (curThrowable == null) {
-                            exMatched = true;
-                        }
-                    }
-                    if (!exMatched) {
-                        log.trace(testExClass);
-                        if (curThrowable != null) {
-                            logStack(curThrowable);
+                        if (!exMatched) {
+                            log.trace(testExClass);
+                            if (curThrowable != null) {
+                                logStack(curThrowable);
+                            }
                         }
+                        assertTrue(curAct + ":Expecting=" + expectedExceptions
+                            + ", Testing=" + testExClass, exMatched);
+                        exThread.throwable = null;
+                    } else {
+                    	// test exception in any thread; used for deadlock exception testing since db server
+                    	// decides on which thread to terminate if deadlock is detected.
+                        if (expectedExceptions == null || expectedExceptions.size() == 0) {
+                        	// Expecting no exception in all threads.
+                        	boolean noExMatched = true;
+							String aTestExClass = "[";
+							for (TestThread aThread : threads) {
+								Throwable aThrowable = aThread.throwable;
+								aTestExClass += processException(aThread, curAction, aThrowable) + ", ";
+							    if (aThrowable != null) {
+							    	noExMatched = false;
+		                            log.trace(aTestExClass);
+	                                logStack(aThrowable);
+		                            aThread.throwable = null;
+							    }
+							}
+	                        assertTrue(curAct + ":Expecting=[no exception]"
+	                                + ", Testing=" + aTestExClass + ']', noExMatched);
+						} else {
+                        	// Expecting any exception in any threads.
+							boolean aMatched = false;
+							String aTestExClass = "[";
+							for (TestThread aThread : threads) {
+								Throwable aThrowable = aThread.throwable;
+								aTestExClass += processException(aThread, curAction, aThrowable) + ", ";
+
+								for (Class<?> anExpectedException : expectedExceptions) {
+									if (matchExpectedException(curAct,
+											anExpectedException, aThrowable)) {
+										aMatched = true;
+										break;
+									}
+								}
+								if (aMatched) {
+									break;
+								} else {
+		                            if (aThrowable != null) {
+		                                logStack(aThrowable);
+			                            aThread.throwable = null;
+		                            }
+								}
+							}
+	                        if (!aMatched) {
+	                            log.trace(aTestExClass);
+	                        }
+	                        assertTrue(curAct + ":Expecting=" + expectedExceptions
+	                            + ", Testing=" + aTestExClass + "]", aMatched);
+						}
                     }
-                    assertTrue(curAct + ":Expecting=" + expectedExceptions
-                        + ", Testing=" + testExClass, exMatched);
-                    exThread.throwable = null;
                     break;
 
                 case WaitAllChildren:
@@ -855,7 +907,7 @@ public abstract class SequencedActionsTe
                     log.trace("finally: commit completed");
                 }
                 } catch(Exception finalEx) {
-                    String failStr = processException(curAction, finalEx);
+                    String failStr = processException(thisThread, curAction, finalEx);
                     log.trace("Fincally:" + failStr);
                 }
             }
@@ -881,11 +933,11 @@ public abstract class SequencedActionsTe
         return lockMode;
     }
 
-    private String processException(Act curAction, Throwable t) {
-        String failStr = "Caught exception: none";
+    private String processException(TestThread thread, Act curAction, Throwable t) {
+        String failStr = "[" + thread.threadToRun + "] Caught exception: none";
         if (t != null) {
             getLog().trace(
-                "Caught exception: " + t.getClass().getName() + ":" + t);
+            		"[" + thread.threadToRun + "] Caught exception: " + t.getClass().getName() + ":" + t);
             logStack(t);
             Throwable rootCause = t.getCause();
             failStr = "Failed on action '" + curAction + "' with exception "

Modified: openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestMixedLockManagerDeadlock.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestMixedLockManagerDeadlock.java?rev=1072062&r1=1072061&r2=1072062&view=diff
==============================================================================
--- openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestMixedLockManagerDeadlock.java (original)
+++ openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestMixedLockManagerDeadlock.java Fri Feb 18 16:59:25 2011
@@ -19,6 +19,7 @@
 package org.apache.openjpa.persistence.lockmgr;
 
 import java.util.Arrays;
+import java.util.HashMap;
 
 import javax.persistence.EntityManager;
 import javax.persistence.LockModeType;
@@ -28,12 +29,14 @@ import javax.persistence.LockModeType;
  */
 public class TestMixedLockManagerDeadlock extends SequencedActionsTest {
     private DBType dbType;
+    private HashMap<DBType,Class<?>[]> expWriteLockExClasses;
     
     public void setUp() {
         setSupportedDatabases(
+                org.apache.openjpa.jdbc.sql.DB2Dictionary.class,
                 org.apache.openjpa.jdbc.sql.DerbyDictionary.class,
                 org.apache.openjpa.jdbc.sql.OracleDictionary.class,
-                org.apache.openjpa.jdbc.sql.DB2Dictionary.class);
+                org.apache.openjpa.jdbc.sql.SQLServerDictionary.class);
         if (isTestsDisabled()) {
             return;
         }
@@ -41,6 +44,12 @@ public class TestMixedLockManagerDeadloc
         setUp(LockEmployee.class
             , "openjpa.LockManager", "mixed"
         );
+        expWriteLockExClasses = new HashMap<DBType,Class<?>[]>();
+        expWriteLockExClasses.put(DBType.db2, null);
+        expWriteLockExClasses.put(DBType.derby, ExpectingOptimisticLockExClass);
+        expWriteLockExClasses.put(DBType.oracle, null);
+        expWriteLockExClasses.put(DBType.sqlserver, ExpectingOptimisticLockExClass);
+
         commonSetUp();
         EntityManager em = emf.createEntityManager();
         dbType = getDBType(em);
@@ -49,8 +58,7 @@ public class TestMixedLockManagerDeadloc
     /* ======== Find dead lock exception test ============*/
     public void testFindDeadLockException() {
         commonFindTest("testFindDeadLockException", LockModeType.READ, null); 
-        commonFindTest("testFindDeadLockException", LockModeType.WRITE, dbType == DBType.oracle ? null
-                : ExpectingOptimisticLockExClass);
+        commonFindTest("testFindDeadLockException", LockModeType.WRITE, expWriteLockExClasses.get(dbType));
         commonFindTest("testFindDeadLockException", LockModeType.PESSIMISTIC_WRITE, ExpectingAnyLockExClass);
     }
 
@@ -73,7 +81,7 @@ public class TestMixedLockManagerDeadloc
             {Act.FindWithLock, 2, t1Lock},                        
             
             {Act.WaitAllChildren},
-            {Act.TestException, 1, t1Exceptions},
+            {Act.TestException, -1, t1Exceptions}, // test t1Exceptions in any thread
             {Act.RollbackTx}
         };
         Object[][] thread1 = {
@@ -94,10 +102,8 @@ public class TestMixedLockManagerDeadloc
     /* ======== named query dead lock exception test ============*/
     public void testNamedQueryDeadLockException() {
         commonNamedQueryTest("testNamedQueryDeadLockException", LockModeType.READ, null);
-        commonNamedQueryTest("testNamedQueryDeadLockException", LockModeType.WRITE, dbType == DBType.oracle ? null
-                : ExpectingOptimisticLockExClass);
-//      commonNamedQueryTest("testNamedQueryDeadLockException", LockModeType.PESSIMISTIC_FORCE_INCREMENT,
-//      ExpectingAnyLockExClass);
+        commonNamedQueryTest("testNamedQueryDeadLockException", LockModeType.WRITE, expWriteLockExClasses.get(dbType));
+        commonNamedQueryTest("testNamedQueryDeadLockException", LockModeType.PESSIMISTIC_FORCE_INCREMENT, ExpectingAnyLockExClass);
     }
 
     private void commonNamedQueryTest( String testName, 
@@ -119,7 +125,7 @@ public class TestMixedLockManagerDeadloc
             {Act.NamedQueryWithLock, "findEmployeeById", 2, t1Lock, "openjpa.hint.IgnorePreparedQuery", true},                        
             
             {Act.WaitAllChildren},
-            {Act.TestException, 1, t1Exceptions},
+            {Act.TestException, -1, t1Exceptions},
 
             {Act.RollbackTx},
             {Act.CloseEm}

Modified: openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java?rev=1072062&r1=1072061&r2=1072062&view=diff
==============================================================================
--- openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java (original)
+++ openjpa/branches/2.1.x/openjpa-persistence-locking/src/test/java/org/apache/openjpa/persistence/lockmgr/TestPessimisticLocks.java Fri Feb 18 16:59:25 2011
@@ -137,10 +137,7 @@ public class TestPessimisticLocks extend
             em2.find(Employee.class, 2, LockModeType.PESSIMISTIC_READ, hints);
             fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
         } catch (Throwable e) {            
-            if (!dict.supportsLockingWithMultipleTables)
-                assertError(e, PessimisticLockException.class);
-            else 
-                assertError(e, LockTimeoutException.class);
+            assertError(e, PessimisticLockException.class, LockTimeoutException.class);
         } finally {
             if (em1.getTransaction().isActive())
                 em1.getTransaction().rollback();
@@ -206,10 +203,7 @@ public class TestPessimisticLocks extend
             em2.find(Employee.class, 2, LockModeType.PESSIMISTIC_READ, map);
             fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
         } catch (Exception e) {
-            if (!dict.supportsLockingWithMultipleTables)
-                assertError(e, PessimisticLockException.class);
-            else
-                assertError(e, LockTimeoutException.class);
+            assertError(e, PessimisticLockException.class, LockTimeoutException.class);
         } finally {
             if (em1.getTransaction().isActive())
                 em1.getTransaction().rollback();
@@ -276,10 +270,7 @@ public class TestPessimisticLocks extend
             assertTrue("Test department name = 'D20'", q.get(0).getName().equals("D10")
                     || q.get(0).getName().equals("D20"));
         } catch (Exception ex) {
-            if (!dict.supportsLockingWithMultipleTables)
-                fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
-            else
-                assertError(ex, QueryTimeoutException.class);
+            assertError(ex, QueryTimeoutException.class);
         } finally {
             if (em1.getTransaction().isActive())
                 em1.getTransaction().rollback();
@@ -304,10 +295,7 @@ public class TestPessimisticLocks extend
             List<Employee> q = query.getResultList();
             fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
         } catch (Exception e) {
-            if (!dict.supportsLockingWithMultipleTables)
-                assertError(e, PessimisticLockException.class);
-            else
-                assertError(e, QueryTimeoutException.class);
+            assertError(e, PessimisticLockException.class, QueryTimeoutException.class);
         } finally {
             if (em1.getTransaction().isActive())
                 em1.getTransaction().rollback();
@@ -342,10 +330,7 @@ public class TestPessimisticLocks extend
             assertEquals("Expected 1 element with department id=20", q.size(), 1);
             assertEquals("Test department name = 'D20'", q.get(0).getName(), "D20");
         } catch (Exception ex) {
-            if (!dict.supportsLockingWithMultipleTables)
-                fail("Caught unexpected " + ex.getClass().getName() + ":" + ex.getMessage());
-            else 
-                assertError(ex, QueryTimeoutException.class);
+            assertError(ex, QueryTimeoutException.class);
         } finally {
             if (em1.getTransaction().isActive())
                 em1.getTransaction().rollback();
@@ -370,10 +355,7 @@ public class TestPessimisticLocks extend
             List<?> q = query.getResultList();
             fail("Unexcpected find succeeded. Should throw a PessimisticLockException.");
         } catch (Exception e) {
-            if (!dict.supportsLockingWithMultipleTables)
-                assertError(e, PessimisticLockException.class);
-            else 
-                assertError(e, QueryTimeoutException.class);
+            assertError(e, PessimisticLockException.class, QueryTimeoutException.class);
         } finally {
             if (em1.getTransaction().isActive())
                 em1.getTransaction().rollback();
@@ -505,12 +487,22 @@ public class TestPessimisticLocks extend
      * @param expeceted
      *            type of the exception
      */
-    void assertError(Throwable actual, Class<? extends Throwable> expected) {
-        if (!expected.isAssignableFrom(actual.getClass())) {
-            actual.printStackTrace();
-            throw new AssertionFailedError(actual.getClass().getName() + " was raised but expected "
-                    + expected.getName());
-        }
+    void assertError(Throwable actual, Class<? extends Throwable> ... expected) {
+		boolean matched = false;
+		String expectedNames = "";
+		for (Class<? extends Throwable> aExpected : expected) {
+			expectedNames += aExpected.getName() + ", ";
+			if (aExpected.isAssignableFrom(actual.getClass())) {
+				matched = true;
+			}
+		}
+		if (!matched) {
+			actual.printStackTrace();
+			throw new AssertionFailedError(actual.getClass().getName()
+					+ " was raised but expecting one of the following: ["
+					+ expectedNames.substring(0, expectedNames.length() - 2) + "]");
+		}
+
         Object failed = getFailedObject(actual);
         assertNotNull("Failed object is null", failed);
         assertNotEquals("null", failed);

Modified: openjpa/branches/2.1.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java?rev=1072062&r1=1072061&r2=1072062&view=diff
==============================================================================
--- openjpa/branches/2.1.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java (original)
+++ openjpa/branches/2.1.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java Fri Feb 18 16:59:25 2011
@@ -163,10 +163,14 @@ public class EntityManagerImpl
     }
 
     public FetchPlan pushFetchPlan() {
+		return pushFetchPlan(null);
+    }
+
+    public FetchPlan pushFetchPlan(FetchConfiguration fc) {
         assertNotCloseInvoked();
         _broker.lock();
         try {
-            _broker.pushFetchConfiguration();
+            _broker.pushFetchConfiguration(fc);
             return getFetchPlan();
         } finally {
             _broker.unlock();

Modified: openjpa/branches/2.1.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.1.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java?rev=1072062&r1=1072061&r2=1072062&view=diff
==============================================================================
--- openjpa/branches/2.1.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java (original)
+++ openjpa/branches/2.1.x/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java Fri Feb 18 16:59:25 2011
@@ -316,22 +316,26 @@ public class QueryImpl<X> implements Ope
 	
 	public List getResultList() {
 		_em.assertNotCloseInvoked();
-		Object ob = execute();
-		if (ob instanceof List) {
-			List ret = (List) ob;
-			if (ret instanceof ResultList) {
-			    RuntimeExceptionTranslator trans = PersistenceExceptions.getRollbackTranslator(_em);
-			    if (_query.isDistinct()) {
-			        return new DistinctResultList((ResultList) ret, trans);
+		boolean queryFetchPlanUsed = pushQueryFetchPlan();
+		try {
+		    Object ob = execute();
+		    if (ob instanceof List) {
+			    List ret = (List) ob;
+			    if (ret instanceof ResultList) {
+			        RuntimeExceptionTranslator trans = PersistenceExceptions.getRollbackTranslator(_em);
+			        if (_query.isDistinct()) {
+			            return new DistinctResultList((ResultList) ret, trans);
+			        } else {
+			            return new DelegatingResultList((ResultList) ret, trans);
+			        }
 			    } else {
-			        return new DelegatingResultList((ResultList) ret, trans);
+				    return ret;
 			    }
-			} else {
-				return ret;
-			}
+		    }
+		    return Collections.singletonList(ob);
+		} finally {
+			popQueryFetchPlan(queryFetchPlanUsed);
 		}
-
-		return Collections.singletonList(ob);
 	}
 
 	/**
@@ -340,18 +344,45 @@ public class QueryImpl<X> implements Ope
 	public X getSingleResult() {
 		_em.assertNotCloseInvoked();
         setHint(QueryHints.HINT_RESULT_COUNT, 1); // for DB2 optimization
-		List result = getResultList();
-		if (result == null || result.isEmpty())
-            throw new NoResultException(_loc.get("no-result", getQueryString())
-                    .getMessage());
-		if (result.size() > 1)
-            throw new NonUniqueResultException(_loc.get("non-unique-result",
-                    getQueryString(), result.size()).getMessage());
+		boolean queryFetchPlanUsed = pushQueryFetchPlan();
 		try {
-		    return (X)result.get(0);
-		} catch (Exception e) {
-            throw new NoResultException(_loc.get("no-result", getQueryString())
-                .getMessage());
+		    List result = getResultList();
+		    if (result == null || result.isEmpty())
+                throw new NoResultException(_loc.get("no-result", getQueryString())
+                        .getMessage());
+		    if (result.size() > 1)
+                throw new NonUniqueResultException(_loc.get("non-unique-result",
+                        getQueryString(), result.size()).getMessage());
+		    try {
+		        return (X)result.get(0);
+		    } catch (Exception e) {
+                throw new NoResultException(_loc.get("no-result", getQueryString())
+                    .getMessage());
+		    }
+		} finally {
+			popQueryFetchPlan(queryFetchPlanUsed);
+		}
+	}
+
+	private boolean pushQueryFetchPlan() {
+		boolean fcPushed = false;
+		if (_fetch != null && _hintHandler != null) {
+			switch (_fetch.getReadLockMode()) {
+			case PESSIMISTIC_READ:
+			case PESSIMISTIC_WRITE:
+			case PESSIMISTIC_FORCE_INCREMENT:
+				// push query fetch plan to em if pessisimistic lock and any
+				// hints are set
+				_em.pushFetchPlan(((FetchPlanImpl)_fetch).getDelegate());
+				fcPushed = true;
+			}
+		}
+		return fcPushed;
+	}
+
+	private void popQueryFetchPlan(boolean queryFetchPlanUsed) {
+		if (queryFetchPlanUsed) {
+			_em.popFetchPlan();
 		}
 	}