OPENJPA-506

git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/1.0.x@617334 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Patrick Linskey 2008-02-01 02:41:00 +00:00
parent dc1088934b
commit f12c0926de
12 changed files with 614 additions and 39 deletions

View File

@ -41,6 +41,12 @@ public class LifecycleEvent
*/
public static final int AFTER_PERSIST = 1;
/**
* Event type when an instance is made persistent, after the record has
* been written to the store
*/
public static final int AFTER_PERSIST_PERFORMED = 18;
/**
* Event type when an instance is loaded.
*/
@ -76,6 +82,12 @@ public class LifecycleEvent
*/
public static final int AFTER_DELETE = 8;
/**
* Event type when an instance is deleted, after the record has been
* deleted from the store.
*/
public static final int AFTER_DELETE_PERFORMED = 19;
/**
* Event type when an instance is dirtied for the first time.
*/
@ -121,12 +133,26 @@ public class LifecycleEvent
*/
public static final int AFTER_REFRESH = 17;
/**
* Event type when an instance is modified. This is not invoked for
* PNEW records, but is invoked for PNEWFLUSHED.
*/
public static final int BEFORE_UPDATE = 20;
/**
* Event type when an instance is modified, after the change has been
* sent to the store. This is not invoked for PNEW records, but is
* invoked for PNEWFLUSHED records.
*/
public static final int AFTER_UPDATE_PERFORMED = 21;
/**
* Convenience array of all event types.
*/
public static final int[] ALL_EVENTS = new int[]{
BEFORE_PERSIST,
AFTER_PERSIST,
AFTER_PERSIST_PERFORMED,
AFTER_LOAD,
BEFORE_STORE,
AFTER_STORE,
@ -134,6 +160,7 @@ public class LifecycleEvent
AFTER_CLEAR,
BEFORE_DELETE,
AFTER_DELETE,
AFTER_DELETE_PERFORMED,
BEFORE_DIRTY,
AFTER_DIRTY,
BEFORE_DIRTY_FLUSHED,
@ -143,6 +170,8 @@ public class LifecycleEvent
BEFORE_ATTACH,
AFTER_ATTACH,
AFTER_REFRESH,
BEFORE_UPDATE,
AFTER_UPDATE_PERFORMED,
};
private final int _type;

View File

@ -28,6 +28,8 @@ import java.util.Map;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.MetaDataDefaults;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.lib.util.Localizer;
/**
* Manager that can be used to track and notify listeners on lifecycle events.
@ -48,6 +50,9 @@ public class LifecycleEventManager
private static final Exception[] EMPTY_EXCEPTIONS = new Exception[0];
private static final Localizer _loc = Localizer.forPackage(
LifecycleEventManager.class);
private Map _classListeners = null; // class -> listener list
private ListenerList _listeners = null;
private List _addListeners = new LinkedList();
@ -56,6 +61,11 @@ public class LifecycleEventManager
private boolean _firing = false;
private boolean _fail = false;
private boolean _failFast = false;
private final Log _log;
public LifecycleEventManager(Log log) {
_log = log;
}
/**
* Whether to fail after first exception when firing events to listeners.
@ -132,7 +142,9 @@ public class LifecycleEventManager
*/
public boolean hasPersistListeners(Object source, ClassMetaData meta) {
return hasHandlers(source, meta, LifecycleEvent.BEFORE_PERSIST)
|| hasHandlers(source, meta, LifecycleEvent.AFTER_PERSIST);
|| hasHandlers(source, meta, LifecycleEvent.AFTER_PERSIST)
|| hasHandlers(source, meta,
LifecycleEvent.AFTER_PERSIST_PERFORMED);
}
/**
@ -140,7 +152,8 @@ public class LifecycleEventManager
*/
public boolean hasDeleteListeners(Object source, ClassMetaData meta) {
return hasHandlers(source, meta, LifecycleEvent.BEFORE_DELETE)
|| hasHandlers(source, meta, LifecycleEvent.AFTER_DELETE);
|| hasHandlers(source, meta, LifecycleEvent.AFTER_DELETE)
|| hasHandlers(source, meta, LifecycleEvent.AFTER_DELETE_PERFORMED);
}
/**
@ -166,6 +179,14 @@ public class LifecycleEventManager
|| hasHandlers(source, meta, LifecycleEvent.AFTER_STORE);
}
/**
* Return whether there are listeners or callbacks for the given source.
*/
public boolean hasUpdateListeners(Object source, ClassMetaData meta) {
return hasHandlers(source, meta, LifecycleEvent.BEFORE_UPDATE)
|| hasHandlers(source, meta, LifecycleEvent.AFTER_UPDATE_PERFORMED);
}
/**
* Return whether there are listeners or callbacks for the given source.
*/
@ -470,6 +491,11 @@ public class LifecycleEventManager
((AttachListener) listener).afterAttach(ev);
}
break;
default:
if (_log.isWarnEnabled())
_log.warn(_loc.get("unknown-lifecycle-event",
Integer.toString(type)));
break;
}
}
catch (Exception e) {

View File

@ -264,7 +264,8 @@ public class BrokerImpl
else
_runtime = new LocalManagedRuntime(this);
_lifeEventManager = new LifecycleEventManager();
_lifeEventManager = new LifecycleEventManager(
_conf.getLog(OpenJPAConfiguration.LOG_RUNTIME));
_transEventManager = new TransactionEventManager();
int cmode = _conf.getMetaDataRepositoryInstance().
getMetaDataFactory().getDefaults().getCallbackMode();

View File

@ -952,6 +952,11 @@ public class StateManagerImpl
if (reason != BrokerImpl.FLUSH_ROLLBACK
&& reason != BrokerImpl.FLUSH_LOGICAL) {
// analyze previous state for later
boolean wasNew = isNew();
boolean wasFlushed = isFlushed();
boolean wasDeleted = isDeleted();
// all dirty fields were flushed
_flush.or(_dirty);
@ -972,6 +977,15 @@ public class StateManagerImpl
// if this object was stored with preFlush, do post-store callback
if ((_flags & FLAG_PRE_FLUSHED) > 0)
fireLifecycleEvent(LifecycleEvent.AFTER_STORE);
// do post-update as needed
if (wasNew && !wasFlushed)
fireLifecycleEvent(LifecycleEvent.AFTER_PERSIST_PERFORMED);
else if (wasDeleted)
fireLifecycleEvent(LifecycleEvent.AFTER_DELETE_PERFORMED);
else
// updates and new-flushed with changes
fireLifecycleEvent(LifecycleEvent.AFTER_UPDATE_PERFORMED);
} else if (reason == BrokerImpl.FLUSH_ROLLBACK) {
// revert to last loaded version and original oid
assignVersionField(_loadVersion);
@ -2761,6 +2775,11 @@ public class StateManagerImpl
if (isPersistent()) {
fireLifecycleEvent(LifecycleEvent.BEFORE_STORE);
// BEFORE_PERSIST is handled during Broker.persist and Broker.attach
if (isDeleted())
fireLifecycleEvent(LifecycleEvent.BEFORE_DELETE);
else
fireLifecycleEvent(LifecycleEvent.BEFORE_UPDATE);
_flags |= FLAG_PRE_FLUSHED;
}

View File

@ -33,6 +33,7 @@ import org.apache.openjpa.util.ApplicationIds;
import org.apache.openjpa.util.ObjectNotFoundException;
import org.apache.openjpa.util.OptimisticException;
import org.apache.openjpa.util.ImplHelper;
import org.apache.openjpa.event.LifecycleEvent;
/**
* Handles attaching instances using version and primary key fields.
@ -133,8 +134,13 @@ class VersionAttachStrategy
return into;
}
// invoke any preAttach on the detached instance
manager.fireBeforeAttach(toAttach, meta);
if (isNew) {
broker.fireLifecycleEvent(toAttach, null, meta,
LifecycleEvent.BEFORE_PERSIST);
} else {
// invoke any preAttach on the detached instance
manager.fireBeforeAttach(toAttach, meta);
}
// assign the detached pc the same state manager as the object we're
// copying into during the attach process

View File

@ -168,9 +168,9 @@ public class ImplHelper {
}
}
/**
* Returns the fields of the state that require an update.
*
/**
* Returns the fields of the state that require an update.
*
* @param sm the state to check
* @return the BitSet of fields that need update, or null if none
*/

View File

@ -96,4 +96,6 @@ bean-constructor: Could not instantiate class {0}. Make sure it has an \
method-notfound: Method "{1}" with arguments of type: {2} \
not found in class "{0}".
broker-factory-listener-exception: Exception thrown while calling a \
BrokerFactoryListener. This exception will be ignored.
BrokerFactoryListener. This exception will be ignored.
unknown-lifecycle-event: An unknown lifecycle event was encountered. Please \
report this to dev@openjpa.apache.org. Event type: {0}.

View File

@ -24,27 +24,52 @@ import javax.persistence.PostLoad;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Version;
import javax.persistence.PreRemove;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PostPersist;
import javax.persistence.GeneratedValue;
import javax.persistence.Transient;
@Entity
public class ExceptionsFromCallbacksEntity {
@Id private long id;
@Id @GeneratedValue private long id;
@Version private int version;
private boolean throwOnPrePersist;
private boolean throwOnPreUpdate;
@Transient private boolean throwOnPrePersist;
@Transient private boolean throwOnPostPersist;
@Transient private boolean throwOnPreUpdate;
@Transient private boolean throwOnPostUpdate;
private boolean throwOnPostLoad;
@Transient private boolean throwOnPreRemove;
@Transient private boolean throwOnPostRemove;
private String stringField;
public void setThrowOnPrePersist(boolean b) {
throwOnPrePersist = b;
}
public void setThrowOnPostPersist(boolean b) {
throwOnPostPersist = b;
}
public void setThrowOnPreUpdate(boolean b) {
throwOnPreUpdate = b;
}
public void setThrowOnPostUpdate(boolean b) {
throwOnPostUpdate = b;
}
public void setThrowOnPostLoad(boolean b) {
throwOnPostLoad = b;
}
public void setThrowOnPreUpdate(boolean b) {
throwOnPreUpdate = b;
public void setThrowOnPreRemove(boolean b) {
throwOnPreRemove = b;
}
public void setThrowOnPostRemove(boolean b) {
throwOnPostRemove = b;
}
public void setStringField(String s) {
@ -57,18 +82,50 @@ public class ExceptionsFromCallbacksEntity {
throw new CallbackTestException();
}
@PostPersist
public void postPersist() {
if (throwOnPostPersist)
throw new CallbackTestException();
}
@PostLoad
public void postLoad() {
if (throwOnPostLoad && isInvokedFromTestMethod())
throw new CallbackTestException();
}
private boolean isInvokedFromTestMethod() {
return TestExceptionsFromCallbacks.testRunning;
}
@PreUpdate
public void preUpdate() {
if (throwOnPreUpdate)
throw new CallbackTestException();
}
@PostLoad
public void postLoad() {
if (throwOnPostLoad)
@PostUpdate
public void postUpdate() {
if (throwOnPostUpdate)
throw new CallbackTestException();
}
@PreRemove
public void preRemove() {
if (throwOnPreRemove && isInvokedFromTestMethod())
throw new CallbackTestException();
}
@PostRemove
public void postRemove() {
if (throwOnPostRemove && isInvokedFromTestMethod())
throw new CallbackTestException();
}
public Object getId() {
return id;
}
public class CallbackTestException
extends RuntimeException {
}

View File

@ -18,18 +18,15 @@
*/
package org.apache.openjpa.persistence.callbacks;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import javax.persistence.EntityManager;
import javax.persistence.Persistence;
import javax.persistence.RollbackException;
import junit.framework.TestCase;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory;
import org.apache.openjpa.persistence.OpenJPAPersistence;
import org.apache.openjpa.persistence.callbacks.ExceptionsFromCallbacksEntity.CallbackTestException;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
import org.apache.openjpa.enhance.PersistenceCapable;
/**
* Tests against JPA section 3.5's description of callback exception handling.
@ -37,8 +34,35 @@ import org.apache.openjpa.persistence.test.SingleEMFTestCase;
public class TestExceptionsFromCallbacks
extends SingleEMFTestCase {
public static boolean testRunning = false;
@Override
public void setUp() {
setUp(ExceptionsFromCallbacksEntity.class);
Set needEnhancement = new HashSet();
needEnhancement.add(
"testPostUpdateExceptionDuringFlushWithNewInstance");
needEnhancement.add(
"testPreUpdateExceptionDuringFlushWithExistingFlushedInstance");
needEnhancement.add(
"testPreUpdateExceptionDuringCommitWithExistingFlushedInstance");
needEnhancement.add(
"testPostUpdateExceptionDuringFlushWithExistingFlushedInstance");
needEnhancement.add(
"testPostUpdateExceptionDuringCommitWithExistingFlushedInstance");
if (!PersistenceCapable.class.isAssignableFrom(
ExceptionsFromCallbacksEntity.class)
&& needEnhancement.contains(getName()))
// actually, we really only need redef
fail("this test method does not work without enhancement");
setUp(ExceptionsFromCallbacksEntity.class, CLEAR_TABLES, "openjpa.Log", "SQL=TRACE");
testRunning = true;
}
@Override
public void tearDown() throws Exception {
testRunning = false;
super.tearDown();
}
public void testPrePersistException() {
@ -60,13 +84,41 @@ public class TestExceptionsFromCallbacks
}
}
public void testPreUpdateExceptionDuringFlush() {
public void testPrePersistExceptionOnMerge() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
o.setThrowOnPreUpdate(true);
o.setThrowOnPrePersist(true);
try {
em.merge(o);
fail("merge should have failed");
} catch (CallbackTestException cte) {
// transaction should be still active, but marked for rollback
assertTrue(em.getTransaction().isActive());
assertTrue(em.getTransaction().getRollbackOnly());
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
em.close();
}
}
public void testPostPersistExceptionDuringFlush() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
o.setThrowOnPostPersist(true);
em.persist(o);
mutateAndFlush(em, o);
}
private void mutateAndFlush(EntityManager em,
ExceptionsFromCallbacksEntity o) {
o.setStringField("foo");
flush(em);
}
private void flush(EntityManager em) {
try {
em.flush();
fail("flush should have failed");
@ -81,20 +133,29 @@ public class TestExceptionsFromCallbacks
}
}
public void testPreUpdateExceptionDuringCommit() {
public void testPostPersistExceptionDuringCommit() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
o.setThrowOnPreUpdate(true);
o.setThrowOnPostPersist(true);
em.persist(o);
mutateAndCommit(em, o);
}
private void mutateAndCommit(EntityManager em,
ExceptionsFromCallbacksEntity o) {
o.setStringField("foo");
commit(em);
}
private void commit(EntityManager em) {
try {
em.getTransaction().commit();
fail("commit should have failed");
} catch (RollbackException re) {
assertEquals(CallbackTestException.class,
re.getCause().getClass());
// transaction should be rolled back at this point
assertFalse(em.getTransaction().isActive());
} finally {
@ -103,7 +164,253 @@ public class TestExceptionsFromCallbacks
em.close();
}
}
public void testPrePersistExceptionDuringFlushWithNewFlushedInstance() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
o.setThrowOnPrePersist(true);
// should pass; pre-persist should not be triggered
o.setStringField("foo");
em.flush();
em.getTransaction().commit();
em.close();
}
public void testPrePersistExceptionDuringCommitWithNewFlushedInstance() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
o.setThrowOnPrePersist(true);
// should pass; pre-persist should not be triggered
o.setStringField("foo");
em.getTransaction().commit();
em.close();
}
public void testPostPersistExceptionDuringFlushWithNewFlushedInstance() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
o.setThrowOnPostPersist(true);
// should pass; post-persist should not be triggered
o.setStringField("foo");
em.flush();
em.getTransaction().commit();
em.close();
}
public void testPostPersistExceptionDuringCommitWithNewFlushedInstance() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
o.setThrowOnPostPersist(true);
// should pass; post-persist should not be triggered
o.setStringField("foo");
em.getTransaction().commit();
em.close();
}
public void testPreUpdateExceptionWithNewInstance() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
o.setThrowOnPreUpdate(true);
em.persist(o);
o.setStringField("foo");
em.getTransaction().commit();
em.close();
}
public void testPostUpdateExceptionDuringFlushWithNewInstance() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
o.setThrowOnPostUpdate(true);
em.persist(o);
o.setStringField("foo");
em.flush();
em.getTransaction().commit();
em.close();
}
public void testPostUpdateExceptionDuringCommitWithNewInstance() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
o.setThrowOnPostUpdate(true);
em.persist(o);
o.setStringField("foo");
em.getTransaction().commit();
em.close();
}
public void testPreUpdateExceptionDuringFlushWithNewFlushedInstance() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
o.setThrowOnPreUpdate(true);
mutateAndFlush(em, o);
}
public void testPreUpdateExceptionDuringCommitWithNewFlushedInstance() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
o.setThrowOnPreUpdate(true);
mutateAndCommit(em, o);
}
public void testPostUpdateExceptionDuringFlushWithNewFlushedInstance() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
o.setThrowOnPostUpdate(true);
mutateAndFlush(em, o);
}
public void testPostUpdateExceptionDuringCommitWithNewFlushedInstance() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
o.setThrowOnPostUpdate(true);
mutateAndCommit(em, o);
}
public void testPreUpdateExceptionDuringFlushWithExistingInstance() {
Object oid = insert("new instance");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o =
em.find(ExceptionsFromCallbacksEntity.class, oid);
o.setThrowOnPreUpdate(true);
mutateAndFlush(em, o);
}
private Object insert(String s) {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
o.setStringField(s);
em.persist(o);
em.getTransaction().commit();
em.close();
return o.getId();
}
public void testPreUpdateExceptionDuringCommitWithExistingInstance() {
Object oid = insert("new instance");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o =
em.find(ExceptionsFromCallbacksEntity.class, oid);
o.setThrowOnPreUpdate(true);
mutateAndCommit(em, o);
}
public void testPostUpdateExceptionDuringFlushWithExistingInstance() {
Object oid = insert("new instance");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o =
em.find(ExceptionsFromCallbacksEntity.class, oid);
o.setThrowOnPostUpdate(true);
mutateAndFlush(em, o);
}
public void testPostUpdateExceptionDuringCommitWithExistingInstance() {
Object oid = insert("new instance");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o =
em.find(ExceptionsFromCallbacksEntity.class, oid);
o.setThrowOnPostUpdate(true);
mutateAndCommit(em, o);
}
public void testPreUpdateExceptionDuringFlushWithExistingFlushedInstance() {
Object oid = insert("new instance");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o =
em.find(ExceptionsFromCallbacksEntity.class, oid);
o.setStringField("foo");
em.flush();
o.setThrowOnPreUpdate(true);
// there's no additional flush work; should not re-invoke the callback
em.flush();
em.getTransaction().commit();
em.close();
}
public void testPreUpdateExceptionDuringCommitWithExistingFlushedInstance(){
Object oid = insert("new instance");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o =
em.find(ExceptionsFromCallbacksEntity.class, oid);
o.setStringField("foo");
em.flush();
o.setThrowOnPreUpdate(true);
// there's no additional flush work; should not re-invoke the callback
em.getTransaction().commit();
em.close();
}
public void testPostUpdateExceptionDuringFlushWithExistingFlushedInstance(){
Object oid = insert("new instance");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o =
em.find(ExceptionsFromCallbacksEntity.class, oid);
o.setStringField("foo");
em.flush();
o.setThrowOnPostUpdate(true);
// no mutations; should not trigger a PostUpdate
em.flush();
em.getTransaction().commit();
em.close();
}
public void testPostUpdateExceptionDuringCommitWithExistingFlushedInstance()
{
Object oid = insert("new instance");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o =
em.find(ExceptionsFromCallbacksEntity.class, oid);
o.setStringField("foo");
em.flush();
// no mutations; should not trigger a PostUpdate
o.setThrowOnPostUpdate(true);
em.getTransaction().commit();
em.close();
}
public void testPostLoadException() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
@ -117,7 +424,7 @@ public class TestExceptionsFromCallbacks
em = emf.createEntityManager();
em.getTransaction().begin();
try {
o = em.find(ExceptionsFromCallbacksEntity.class, oid);
em.find(ExceptionsFromCallbacksEntity.class, oid);
fail("find should have failed");
} catch (CallbackTestException cte) {
// transaction should be active but marked for rollback
@ -129,4 +436,107 @@ public class TestExceptionsFromCallbacks
em.close();
}
}
public void testPreDeleteException() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
o.setThrowOnPreRemove(true);
try {
em.remove(o);
} catch (CallbackTestException cte) {
// transaction should be active but marked for rollback
assertTrue(em.getTransaction().isActive());
assertTrue(em.getTransaction().getRollbackOnly());
} finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
em.close();
}
}
public void testPostDeleteExceptionDuringFlush() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
o.setThrowOnPostRemove(true);
try {
em.remove(o);
} catch (CallbackTestException e) {
em.getTransaction().rollback();
em.close();
fail("PostRemove is being called too soon (before SQL is issued)");
}
flush(em);
}
public void testPostDeleteExceptionDuringCommit() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
o.setThrowOnPostRemove(true);
try {
em.remove(o);
} catch (CallbackTestException e) {
em.getTransaction().rollback();
em.close();
fail("PostRemove is being called too soon (before SQL is issued)");
}
commit(em);
}
public void testPreDeleteExceptionDoubleDelete() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
// this should pass
em.remove(o);
em.flush();
o.setThrowOnPreRemove(true);
// this shoud also pass; no work to do for delete
em.remove(o);
em.getTransaction().commit();
em.close();
}
public void testPostDeleteExceptionDuringFlushDoubleDelete() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
// this should pass
em.remove(o);
em.flush();
o.setThrowOnPostRemove(true);
// this shoud also pass; no work to do for delete
em.remove(o);
em.flush();
em.getTransaction().commit();
em.close();
}
public void testPostDeleteExceptionDuringCommitDoubleDelete() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
ExceptionsFromCallbacksEntity o = new ExceptionsFromCallbacksEntity();
em.persist(o);
em.flush();
// this should pass
em.remove(o);
em.flush();
o.setThrowOnPostRemove(true);
// this shoud also pass; no work to do for delete
em.remove(o);
em.getTransaction().commit();
em.close();
}
}

View File

@ -29,6 +29,7 @@ import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import junit.framework.TestCase;
import junit.framework.TestResult;
import org.apache.openjpa.kernel.AbstractBrokerFactory;
import org.apache.openjpa.kernel.Broker;
import org.apache.openjpa.meta.ClassMetaData;
@ -47,6 +48,11 @@ public abstract class PersistenceTestCase
*/
protected static final Object CLEAR_TABLES = new Object();
/**
* The {@link TestResult} instance for the current test run.
*/
protected TestResult testResult;
/**
* Create an entity manager factory. Put {@link #CLEAR_TABLES} in
* this list to tell the test framework to delete all table contents
@ -88,8 +94,22 @@ public abstract class PersistenceTestCase
createEntityManagerFactory("test", map);
}
@Override
public void run(TestResult testResult) {
this.testResult = testResult;
super.run(testResult);
}
@Override
public void tearDown() throws Exception {
super.tearDown();
try {
super.tearDown();
} catch (Exception e) {
// if a test failed, swallow any exceptions that happen
// during tear-down, as these just mask the original problem.
if (testResult.wasSuccessful())
throw e;
}
}
/**

View File

@ -58,6 +58,11 @@ public abstract class SingleEMFTestCase
try {
clear(emf);
} catch (Exception e) {
// if a test failed, swallow any exceptions that happen
// during tear-down, as these just mask the original problem.
if (testResult.wasSuccessful())
throw e;
} finally {
closeEMF(emf);
}

View File

@ -47,15 +47,15 @@ class MetaDataParsers {
case PRE_PERSIST:
return new int[]{ LifecycleEvent.BEFORE_PERSIST };
case POST_PERSIST:
return new int[]{ LifecycleEvent.AFTER_PERSIST };
return new int[]{ LifecycleEvent.AFTER_PERSIST_PERFORMED };
case PRE_REMOVE:
return new int[]{ LifecycleEvent.BEFORE_DELETE };
case POST_REMOVE:
return new int[]{ LifecycleEvent.AFTER_DELETE };
return new int[]{ LifecycleEvent.AFTER_DELETE_PERFORMED };
case PRE_UPDATE:
return new int[]{ LifecycleEvent.BEFORE_STORE };
return new int[]{ LifecycleEvent.BEFORE_UPDATE };
case POST_UPDATE:
return new int[]{ LifecycleEvent.AFTER_STORE };
return new int[]{ LifecycleEvent.AFTER_UPDATE_PERFORMED };
case POST_LOAD:
return new int[]{ LifecycleEvent.AFTER_LOAD,
LifecycleEvent.AFTER_REFRESH };