/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.testing.tests.jpa.advanced.concurrency; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import junit.framework.AssertionFailedError; import junit.framework.Test; import junit.framework.TestSuite; import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl; import org.eclipse.persistence.jpa.JpaEntityManager; import org.eclipse.persistence.testing.framework.junit.JUnitTestCase; import org.eclipse.persistence.testing.models.jpa.advanced.Department; import org.eclipse.persistence.testing.models.jpa.advanced.AdvancedTableCreator; /** * This test suite verifies that the state/lifecycle on a unitOfWork does not reset to 0 (Birth) * when a clearForClose() call is attempted in the middle of a *Pending state (1,2,4). * * Note: These tests verify internal API state that JPA functionality depends on. * The tests are tightly coupled to the implementation of the following functions. * Any change to the behavior of these functions may need to be reflected in these tests * * UnitOfWorkImpl.getCloneToOriginals() * UnitOfWorkImpl.setPendingMerge() * UnitOfWorkImpl.clearForClose() * * The server level JTA tests that verify that test this fix are the following * * 02/10/2009-1.1 Michael O'Brien * - 259993: Defer a clear() call to release() if uow lifecycle is 1,2 or 4 (*Pending). * 09/24/2010-2.1 Michael O'Brien * - 326097: assertion failures are ignored by catch block - refactor test * use refactored clear() instead of clearForClose() */ public class LifecycleJUnitTest extends JUnitTestCase { public LifecycleJUnitTest() { super(); } public LifecycleJUnitTest(String name) { super(name); } public static Test suite() { TestSuite suite = new TestSuite("LifecycleJUnitTestSuite"); suite.addTest(new LifecycleJUnitTest("testSetup")); suite.addTest(new LifecycleJUnitTest("testClearWhileEntityManagerInFakeMergePendingState4")); suite.addTest(new LifecycleJUnitTest("testClearWhileEntityManagerInFakeBirthState0")); suite.addTest(new LifecycleJUnitTest("testClearWhileEntityManagerInCommitPendingStateWithClearAfterCommit")); suite.addTest(new LifecycleJUnitTest("testClearWhileEntityManagerInCommitPendingStateWithNoClearAfterCommit")); suite.addTest(new LifecycleJUnitTest("testClearAfterEntityManagerCommitFinished")); return suite; } // RESOURCE_LOCAL non container managed uow private UnitOfWorkImpl getUnitOfWorkFromEntityManager(EntityManager em) { return ((UnitOfWorkImpl)((JpaEntityManager)em).getActiveSession()).acquireUnitOfWork(); } public void testSetup() { clearCache(); new AdvancedTableCreator().replaceTables(JUnitTestCase.getServerSession()); } public void finalize() { } // This test is a pure unit test that directly sets and tries to clear the uow state // There are no actual entities managed in this example public void testClearWhileEntityManagerInFakeMergePendingState4() { EntityManagerFactory emf = getEntityManagerFactory(); EntityManager em = null; UnitOfWorkImpl uow = null; Map cloneToOriginalsMap = null; Department dept = null; try { em = emf.createEntityManager(); // get the underlying uow uow = getUnitOfWorkFromEntityManager(em); // force a get on the map to lazy initialize an empty map cloneToOriginalsMap = uow.getCloneToOriginals(); // verify size 0 // we don't have access to the protected function uow.hasCloneToOriginals(); assertEquals("cloneToOriginalsMap must be size 0", 0, cloneToOriginalsMap.size()); // Verify that cloneToOriginals is null and not lazy initialized dept = new Department(); cloneToOriginalsMap.put(null, dept); // verify size 1 assertEquals("cloneToOriginalsMap must be size 1", 1, cloneToOriginalsMap.size()); // verify we are in birth state int lifecycleBefore = uow.getLifecycle(); assertEquals("Birth state 0 is not set ", 0, lifecycleBefore); // setup the uow in a simulated state uow.setPendingMerge(); // set state to 4 = MergePending // (via backdoor function) verify we are in PendingMerge state int lifecycleInMerge = uow.getLifecycle(); assertEquals("MergePending state 4 is not set ", 4, lifecycleInMerge); // simulate a clear() call in the middle of a merge // 326097: This assertion used to be ignored by a catch block - it is a valid test but we want to clear on the EM not the UOW em.clear(); // verify that the uow ignored the clear call int lifecycleAfter = uow.getLifecycle(); assertEquals("UnModified MergePending state 4 should still be 4 and not Birth state 0 after a clear() ", 4, lifecycleAfter); // verify that a map previously set on the uow was cleared to null by the // verify size 0 assertNotNull("cloneToOriginals Map must not be null after a clear in *Pending state", cloneToOriginalsMap); assertEquals("cloneToOriginalsMap must be size 1", 1, cloneToOriginalsMap.size()); } catch (RuntimeException ex){ if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); throw ex; } finally { closeEntityManager(em); } } public void testClearWhileEntityManagerInFakeBirthState0() { EntityManagerFactory emf = getEntityManagerFactory(); EntityManager em = null; UnitOfWorkImpl uow = null; Map cloneToOriginalsMap = null; Department dept = null; try { em = emf.createEntityManager(); // get the underlying uow uow = getUnitOfWorkFromEntityManager(em); // force a get on the map to lazy initialize an empty map cloneToOriginalsMap = uow.getCloneToOriginals(); // verify size 0 // we don't have access to the protected function uow.hasCloneToOriginals(); assertEquals("cloneToOriginalsMap must be size 0", 0, cloneToOriginalsMap.size()); // Verify that cloneToOriginals is null and not lazy initialized dept = new Department(); cloneToOriginalsMap.put(null, dept); // verify size 1 assertEquals("cloneToOriginalsMap must be size 1", 1, cloneToOriginalsMap.size()); // verify we are in birth state int lifecycleBefore = uow.getLifecycle(); assertEquals("Birth state 0 is not set ", 0, lifecycleBefore); // simulate a clear() call in the middle of a merge em.clear(); // verify that the uow ignored the clear call int lifecycleAfter = uow.getLifecycle(); assertEquals("Unchanged Birth state 0 is not set ", 0, lifecycleAfter); // verify that a map previously set on the em was not cleared to null by the clear // verify size 0 cloneToOriginalsMap = uow.getCloneToOriginals(); assertNotNull("cloneToOriginals Map must not be null after a clear in Birth state", cloneToOriginalsMap); assertEquals("cloneToOriginalsMap must be not be cleared to size 0", 1, cloneToOriginalsMap.size()); } catch (RuntimeException ex){ if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); throw ex; } finally { closeEntityManager(em); } } public void testClearWhileEntityManagerInFakeAfterExternalTransactionRolledBackState6() { } /** * This test simulates EE container callbacks that could occur that affect em lifecycle state. * Specifically it tests whether we handle an attempt to clear an entityManager * that is in the middle of a commit. * We only clear the entityManager if we are in the states * (Birth == 0, WriteChangesFailed==3, Death==5 or AfterExternalTransactionRolledBack==6). * If we are in one of the following *Pending states we defer the clear() to the release() call later */ public void testClearWhileEntityManagerInCommitPendingStateWithClearAfterCommit() { EntityManagerFactory emf = getEntityManagerFactory(); EntityManager em = emf.createEntityManager(); Department dept = null; try { em.getTransaction().begin(); dept = new Department(); // A merge will not populate the @Id field // A persist will populate the @Id field em.persist(dept); // simulate an attempt to call close() while we are in the middle of a commit UnitOfWorkImpl uow = getUnitOfWorkFromEntityManager(em); // get lifecycle state int lifecycleBefore = uow.getLifecycle(); assertEquals("Birth state 0 is not set ", 0, lifecycleBefore); em.clear(); int lifecycleAfter = uow.getLifecycle(); assertEquals("Birth state 0 is not set after a clear on state Birth ", 0, lifecycleAfter); em.getTransaction().commit(); // clear em em.clear(); int lifecycleAfterCommit = uow.getLifecycle(); assertEquals("Birth state 0 is not set after commit ", 0, lifecycleAfterCommit); } catch (RuntimeException ex){ if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); throw ex; } finally { closeEntityManager(em); } } public void testClearWhileEntityManagerInCommitPendingStateWithNoClearAfterCommit() { EntityManagerFactory emf = getEntityManagerFactory(); EntityManager em = emf.createEntityManager(); Department dept = null; try { em.getTransaction().begin(); dept = new Department(); // A merge will not populate the @Id field and will result in a PK null exception in any find later // A persist will populate the @Id field em.persist(dept); // simulate an attempt to call close() while we are in the middle of a commit UnitOfWorkImpl uow = getUnitOfWorkFromEntityManager(em); // get lifecycle state int lifecycleBefore = uow.getLifecycle(); assertEquals("Birth state 0 is not set ", 0, lifecycleBefore); em.clear(); int lifecycleAfter = uow.getLifecycle(); assertEquals("Birth state 0 is not set after a clear on state Birth ", 0, lifecycleAfter); em.getTransaction().commit(); // don't clear em - leave following line commented //em.clear(); int lifecycleAfterCommit = uow.getLifecycle(); assertEquals("Birth state 0 is not set after commit ", 0, lifecycleAfterCommit); } catch (RuntimeException ex){ if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); throw ex; } finally { closeEntityManager(em); } } // This clear should pass because the state is always 0 Begin except for inside the commit() public void testClearAfterEntityManagerCommitFinished() { EntityManagerFactory emf = getEntityManagerFactory(); EntityManager em = emf.createEntityManager(); Department dept = null; try { em.getTransaction().begin(); dept = new Department(); em.persist(dept); // simulate an attempt to call close() while we are in the middle of a commit UnitOfWorkImpl uow = getUnitOfWorkFromEntityManager(em); // get lifecycle state int lifecycleBefore = uow.getLifecycle(); assertEquals("Birth state 0 is not set ", 0, lifecycleBefore); em.clear(); int lifecycleAfter = uow.getLifecycle(); assertEquals("Birth state 0 is not set after a clear on state Birth ", 0, lifecycleAfter); em.getTransaction().commit(); em.clear(); int lifecycleAfterCommit = uow.getLifecycle(); assertEquals("Birth state 0 is not set after commit ", 0, lifecycleAfterCommit); } catch (RuntimeException ex){ if (isTransactionActive(em)){ rollbackTransaction(em); } closeEntityManager(em); throw ex; } finally { closeEntityManager(em); } } }