Handle exceptions from transaction listeners appropriately. Allow user to

override default CallbackMode for both lifecycle and transaction listeners.



git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@452981 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
A. Abram White 2006-10-04 19:12:34 +00:00
parent 88acae75c1
commit 8785be46e7
8 changed files with 236 additions and 82 deletions

View File

@ -52,6 +52,21 @@ public class LifecycleEventManager
private List _exceps = new LinkedList();
private boolean _firing = false;
private boolean _fail = false;
private boolean _failFast = false;
/**
* Whether to fail after first exception when firing events to listeners.
*/
public boolean isFailFast() {
return _failFast;
}
/**
* Whether to fail after first exception when firing events to listeners.
*/
public void setFailFast(boolean failFast) {
_failFast = failFast;
}
/**
* Register a lifecycle listener for the given classes. If the classes
@ -207,22 +222,19 @@ public class LifecycleEventManager
ClassMetaData meta, int type) {
if (meta.getLifecycleMetaData().getIgnoreSystemListeners())
return false;
boolean failFast = (meta.getRepository().getMetaDataFactory().
getDefaults().getCallbackMode() & CALLBACK_FAIL_FAST) != 0;
if (fireEvent(null, source, null, type, _listeners, true, failFast,
null) == Boolean.TRUE)
if (fireEvent(null, source, null, type, _listeners, true, null)
== Boolean.TRUE)
return true;
ListenerList system = meta.getRepository().getSystemListeners();
if (!system.isEmpty() && fireEvent(null, source, null, type, system,
true, failFast, null) == Boolean.TRUE)
true, null) == Boolean.TRUE)
return true;
if (_classListeners != null) {
Class c = source == null ? meta.getDescribedType() :
source.getClass();
do {
if (fireEvent(null, source, null, type, (ListenerList)
_classListeners.get(c), true, failFast, null)
== Boolean.TRUE)
_classListeners.get(c), true, null) == Boolean.TRUE)
return true;
c = c.getSuperclass();
} while (c != null && c != Object.class);
@ -250,21 +262,18 @@ public class LifecycleEventManager
getDefaults();
boolean callbacks = def.getCallbacksBeforeListeners(type);
boolean failFast = (def.getCallbackMode() & CALLBACK_FAIL_FAST) != 0;
if (callbacks)
makeCallbacks(source, related, meta, type, failFast, exceptions);
makeCallbacks(source, related, meta, type, exceptions);
LifecycleEvent ev = (LifecycleEvent) fireEvent(null, source, related,
type, _listeners, false, failFast, exceptions);
type, _listeners, false, exceptions);
if (_classListeners != null) {
Class c = source == null ? meta.getDescribedType() :
source.getClass();
do {
ev = (LifecycleEvent) fireEvent(ev, source, related, type,
(ListenerList) _classListeners.get(c), false, failFast,
exceptions);
(ListenerList) _classListeners.get(c), false, exceptions);
c = c.getSuperclass();
} while (c != null && c != Object.class);
}
@ -272,12 +281,11 @@ public class LifecycleEventManager
// make system listeners
if (!meta.getLifecycleMetaData().getIgnoreSystemListeners()) {
ListenerList system = meta.getRepository().getSystemListeners();
fireEvent(ev, source, related, type, system, false, failFast,
exceptions);
fireEvent(ev, source, related, type, system, false, exceptions);
}
if (!callbacks)
makeCallbacks(source, related, meta, type, failFast, exceptions);
makeCallbacks(source, related, meta, type, exceptions);
// create return array before clearing exceptions
Exception[] ret;
@ -309,7 +317,7 @@ public class LifecycleEventManager
* Make callbacks, recording any exceptions in the given collection.
*/
private void makeCallbacks(Object source, Object related,
ClassMetaData meta, int type, boolean failFast, Collection exceptions) {
ClassMetaData meta, int type, Collection exceptions) {
// make lifecycle callbacks
LifecycleCallbacks[] callbacks = meta.getLifecycleMetaData().
getCallbacks(type);
@ -318,7 +326,7 @@ public class LifecycleEventManager
callbacks[i].makeCallback(source, related, type);
} catch (Exception e) {
exceptions.add(e);
if (failFast)
if (_failFast)
_fail = true;
}
}
@ -329,8 +337,7 @@ public class LifecycleEventManager
* listeners. The event may have already been constructed.
*/
private Object fireEvent(LifecycleEvent ev, Object source, Object rel,
int type, ListenerList listeners, boolean mock, boolean failFast,
List exceptions) {
int type, ListenerList listeners, boolean mock, List exceptions) {
if (listeners == null || !listeners.hasListeners(type))
return null;
@ -471,7 +478,7 @@ public class LifecycleEventManager
}
catch (Exception e) {
exceptions.add(e);
if (failFast)
if (_failFast)
_fail = true;
}
}

View File

@ -21,6 +21,7 @@ import javax.resource.cci.LocalTransaction;
import javax.transaction.Synchronization;
import org.apache.openjpa.ee.ManagedRuntime;
import org.apache.openjpa.event.CallbackModes;
import org.apache.openjpa.event.LifecycleEventManager;
import org.apache.openjpa.lib.util.Closeable;
import org.apache.openjpa.meta.ClassMetaData;
@ -40,7 +41,7 @@ public interface Broker
extends Synchronization, Connection, LocalTransaction,
javax.resource.spi.LocalTransaction, Closeable, StoreContext,
ConnectionRetainModes, DetachState, LockLevels,
RestoreState, AutoClear, AutoDetach {
RestoreState, AutoClear, AutoDetach, CallbackModes {
/**
* Set the broker's behavior for implicit actions such as flushing,
@ -240,6 +241,18 @@ public interface Broker
*/
public void removeTransactionListener(Object listener);
/**
* The callback mode for handling exceptions from transaction event
* listeners.
*/
public int getTransactionListenerCallbackMode();
/**
* The callback mode for handling exceptions from transaction event
* listeners.
*/
public void setTransactionListenerCallbackMode(int mode);
/**
* Register a listener for lifecycle-related events on the specified
* classes. If the classes are null, all events will be propagated to
@ -261,6 +274,16 @@ public interface Broker
*/
public LifecycleEventManager getLifecycleEventManager();
/**
* The callback mode for handling exceptions from lifecycle event listeners.
*/
public int getLifecycleListenerCallbackMode();
/**
* The callback mode for handling exceptions from lifecycle event listeners.
*/
public void setLifecycleListenerCallbackMode(int mode);
/**
* Begin a transaction.
*/

View File

@ -212,7 +212,6 @@ public class BrokerImpl
private int _autoDetach = 0;
private int _detachState = DETACH_LOADED;
private boolean _detachedNew = true;
private int _callbackMode = CallbackModes.CALLBACK_IGNORE;
private boolean _orderDirty = false;
// status
@ -221,7 +220,9 @@ public class BrokerImpl
// event managers
private TransactionEventManager _transEventManager = null;
private int _transCallbackMode = 0;
private LifecycleEventManager _lifeEventManager = null;
private int _lifeCallbackMode = 0;
/**
* Set the persistence manager's authentication. This is the first
@ -251,13 +252,10 @@ public class BrokerImpl
DelegatingStoreManager sm, boolean managed, int connMode) {
_conf = factory.getConfiguration();
_compat = _conf.getCompatibilityInstance();
_factory = factory;
_log = _conf.getLog(OpenJPAConfiguration.LOG_RUNTIME);
_cache = new ManagedCache(newManagedObjectCache());
_lifeEventManager = new LifecycleEventManager();
_callbackMode = _conf.getMetaDataRepositoryInstance().
getMetaDataFactory(). getDefaults().getCallbackMode();
_operating = MapBackedSet.decorate(new IdentityMap());
_connRetainMode = connMode;
_managed = managed;
if (managed)
@ -265,9 +263,15 @@ public class BrokerImpl
else
_runtime = new LocalManagedRuntime(this);
_lifeEventManager = new LifecycleEventManager();
_transEventManager = new TransactionEventManager();
int cmode = _conf.getMetaDataRepositoryInstance().
getMetaDataFactory().getDefaults().getCallbackMode();
setLifecycleListenerCallbackMode(cmode);
setTransactionListenerCallbackMode(cmode);
// setup default options
_factory.configureBroker(this);
_operating = MapBackedSet.decorate(new IdentityMap());
// make sure to do this after configuring broker so that store manager
// can look to broker configuration; we set both store and lock managers
@ -624,6 +628,20 @@ public class BrokerImpl
}
}
public int getLifecycleListenerCallbackMode() {
return _lifeCallbackMode;
}
public void setLifecycleListenerCallbackMode(int mode) {
beginOperation(false);
try {
_lifeCallbackMode = mode;
_lifeEventManager.setFailFast((mode & CALLBACK_FAIL_FAST) != 0);
} finally {
endOperation();
}
}
/**
* Give state managers access to the lifecycle event manager.
*/
@ -632,41 +650,39 @@ public class BrokerImpl
}
/**
* Fire lifecycle events, handling any exceptions appropriately.
* Fire given lifecycle event, handling any exceptions appropriately.
*
* @return whether events are being processed at this time.
* @return whether events are being processed at this time
*/
boolean fireLifecycleEvent(Object src, Object related, ClassMetaData meta,
int eventType) {
if (_lifeEventManager == null) // uninitialized
if (_lifeEventManager == null)
return false;
Exception[] exceps = _lifeEventManager.fireEvent(src, related, meta,
eventType);
if (exceps.length == 0
|| (_callbackMode & CallbackModes.CALLBACK_IGNORE) != 0)
return true;
OpenJPAException ke = new CallbackException
(_loc.get("callback-err", meta)).
setNestedThrowables(exceps).setFatal(true);
if ((_callbackMode & CallbackModes.CALLBACK_ROLLBACK) != 0
&& (_flags & FLAG_ACTIVE) != 0)
setRollbackOnlyInternal();
if ((_callbackMode & CallbackModes.CALLBACK_LOG) != 0
&& _log.isWarnEnabled())
_log.warn(ke);
if ((_callbackMode & CallbackModes.CALLBACK_RETHROW) != 0)
throw ke;
handleCallbackExceptions(_lifeEventManager.fireEvent(src, related,
meta, eventType), _lifeCallbackMode);
return true;
}
/**
* Take actions on callback exceptions depending on callback mode.
*/
private void handleCallbackExceptions(Exception[] exceps, int mode) {
if (exceps.length == 0 || (mode & CALLBACK_IGNORE) != 0)
return;
OpenJPAException ke = new CallbackException(_loc.get("callback-err")).
setNestedThrowables(exceps).setFatal(true);
if ((mode & CALLBACK_ROLLBACK) != 0 && (_flags & FLAG_ACTIVE) != 0)
setRollbackOnlyInternal();
if ((mode & CALLBACK_LOG) != 0 && _log.isWarnEnabled())
_log.warn(ke);
if ((mode & CALLBACK_RETHROW) != 0)
throw ke;
}
public void addTransactionListener(Object tl) {
beginOperation(false);
try {
if (_transEventManager == null)
_transEventManager = new TransactionEventManager();
_transEventManager.addListener(tl);
if (tl instanceof RemoteCommitEventManager)
_flags |= FLAG_REMOTE_LISTENER;
@ -678,8 +694,7 @@ public class BrokerImpl
public void removeTransactionListener(Object tl) {
beginOperation(false);
try {
if (_transEventManager != null
&& _transEventManager.removeListener(tl)
if (_transEventManager.removeListener(tl)
&& (tl instanceof RemoteCommitEventManager))
_flags &= ~FLAG_REMOTE_LISTENER;
} finally {
@ -687,6 +702,31 @@ public class BrokerImpl
}
}
public int getTransactionListenerCallbackMode() {
return _transCallbackMode;
}
public void setTransactionListenerCallbackMode(int mode) {
beginOperation(false);
try {
_transCallbackMode = mode;
_transEventManager.setFailFast((mode & CALLBACK_FAIL_FAST) != 0);
} finally {
endOperation();
}
}
/**
* Fire given transaction event, handling any exceptions appropriately.
*
* @return whether events are being processed at this time
*/
private void fireTransactionEvent(TransactionEvent trans) {
if (_transEventManager != null)
handleCallbackExceptions(_transEventManager.fireEvent(trans),
_transCallbackMode);
}
///////////
// Lookups
///////////
@ -1151,9 +1191,8 @@ public class BrokerImpl
}
_lm.beginTransaction();
if (_transEventManager != null
&& _transEventManager.hasBeginListeners())
_transEventManager.fireEvent(new TransactionEvent(this,
if (_transEventManager.hasBeginListeners())
fireTransactionEvent(new TransactionEvent(this,
TransactionEvent.AFTER_BEGIN, null, null, null, null));
} catch (OpenJPAException ke) {
// if we already started the transaction, don't let it commit
@ -1755,9 +1794,8 @@ public class BrokerImpl
_flags &= ~FLAG_FLUSHED;
_flags &= ~FLAG_TRANS_ENDING;
if (_transEventManager != null
&& _transEventManager.hasEndListeners()) {
_transEventManager.fireEvent(new TransactionEvent(this,
if (_transEventManager.hasEndListeners()) {
fireTransactionEvent(new TransactionEvent(this,
status == Status.STATUS_COMMITTED
? TransactionEvent.AFTER_COMMIT_COMPLETE
: TransactionEvent.AFTER_ROLLBACK_COMPLETE,
@ -1804,8 +1842,7 @@ public class BrokerImpl
// special case the remote commit listener used by the datacache cause
// we know it doesn't require the commit event when nothing changes
boolean flush = (_flags & FLAG_FLUSH_REQUIRED) != 0;
boolean listeners = _transEventManager != null
&& (_transEventManager.hasFlushListeners()
boolean listeners = (_transEventManager.hasFlushListeners()
|| _transEventManager.hasEndListeners())
&& ((_flags & FLAG_REMOTE_LISTENER) == 0
|| _transEventManager.getListeners().size() > 1);
@ -1840,26 +1877,25 @@ public class BrokerImpl
if ((_flags & FLAG_STORE_ACTIVE) == 0)
beginStoreManagerTransaction(false);
if (_transEventManager != null
&& (_transEventManager.hasFlushListeners()
if ((_transEventManager.hasFlushListeners()
|| _transEventManager.hasEndListeners())
&& (flush || reason == FLUSH_COMMIT)) {
// fire events
mobjs = new ManagedObjectCollection(transactional);
if (reason == FLUSH_COMMIT
&& _transEventManager.hasEndListeners()) {
_transEventManager.fireEvent(new TransactionEvent
(this, TransactionEvent.BEFORE_COMMIT, mobjs,
_persistedClss, _updatedClss, _deletedClss));
fireTransactionEvent(new TransactionEvent(this,
TransactionEvent.BEFORE_COMMIT, mobjs,
_persistedClss, _updatedClss, _deletedClss));
flushAdditions(transactional, reason);
flush = (_flags & FLAG_FLUSH_REQUIRED) != 0;
}
if (flush && _transEventManager.hasFlushListeners()) {
_transEventManager.fireEvent(new TransactionEvent
(this, TransactionEvent.BEFORE_FLUSH, mobjs,
_persistedClss, _updatedClss, _deletedClss));
fireTransactionEvent(new TransactionEvent(this,
TransactionEvent.BEFORE_FLUSH, mobjs,
_persistedClss, _updatedClss, _deletedClss));
flushAdditions(transactional, reason);
}
}
@ -1922,9 +1958,8 @@ public class BrokerImpl
throwNestedExceptions(exceps, true);
if (flush && reason != FLUSH_ROLLBACK && reason != FLUSH_LOGICAL
&& _transEventManager != null
&& _transEventManager.hasFlushListeners()) {
_transEventManager.fireEvent(new TransactionEvent(this,
fireTransactionEvent(new TransactionEvent(this,
TransactionEvent.AFTER_FLUSH, mobjs, _persistedClss,
_updatedClss, _deletedClss));
}
@ -2064,13 +2099,12 @@ public class BrokerImpl
// fire after rollback/commit event
Collection mobjs = null;
if (_transEventManager != null && _transEventManager.hasEndListeners())
{
if (_transEventManager.hasEndListeners()) {
mobjs = new ManagedObjectCollection(transStates);
int eventType = (rollback) ? TransactionEvent.AFTER_ROLLBACK
: TransactionEvent.AFTER_COMMIT;
_transEventManager.fireEvent(new TransactionEvent(this, eventType,
mobjs, _persistedClss, _updatedClss, _deletedClss));
fireTransactionEvent(new TransactionEvent(this, eventType, mobjs,
_persistedClss, _updatedClss, _deletedClss));
}
// null transactional caches now so that all the removeFromTransaction
@ -2130,10 +2164,9 @@ public class BrokerImpl
_savepointCache = null;
// fire after state change event
if (_transEventManager != null && _transEventManager.hasEndListeners())
_transEventManager.fireEvent(new TransactionEvent(this,
TransactionEvent.AFTER_STATE_TRANSITIONS, mobjs, null, null,
null));
if (_transEventManager.hasEndListeners())
fireTransactionEvent(new TransactionEvent(this, TransactionEvent.
AFTER_STATE_TRANSITIONS, mobjs, null, null, null));
// now clear trans cache; keep cleared version rather than
// null to avoid having to re-create the set later; more efficient

View File

@ -794,6 +794,22 @@ public class DelegatingBroker
}
}
public int getTransactionListenerCallbackMode() {
try {
return _broker.getTransactionListenerCallbackMode();
} catch (RuntimeException re) {
throw translate(re);
}
}
public void setTransactionListenerCallbackMode(int mode) {
try {
_broker.setTransactionListenerCallbackMode(mode);
} catch (RuntimeException re) {
throw translate(re);
}
}
public void addLifecycleListener(Object listener, Class[] classes) {
try {
_broker.addLifecycleListener(listener, classes);
@ -810,6 +826,22 @@ public class DelegatingBroker
}
}
public int getLifecycleListenerCallbackMode() {
try {
return _broker.getLifecycleListenerCallbackMode();
} catch (RuntimeException re) {
throw translate(re);
}
}
public void setLifecycleListenerCallbackMode(int mode) {
try {
_broker.setLifecycleListenerCallbackMode(mode);
} catch (RuntimeException re) {
throw translate(re);
}
}
public LifecycleEventManager getLifecycleEventManager() {
try {
return _broker.getLifecycleEventManager();

View File

@ -320,7 +320,8 @@ savepoint-init: This savepoint has already been initialized.
savepoint-flush-not-supported: The configured SavepointManager does not \
support incremental flushing when a savepoint has been set. You must \
release your savepoints before flushing.
callback-err: An error occured processing callbacks for instance of type "{0}".
callback-err: Errors occured processing listener callbacks. See the nested \
exceptions for details.
bad-agg-listener-hint: Query hint value "{0}" ({1}) cannot be converted into \
an aggregate listener.
bad-filter-listener-hint: Query hint value "{0}" ({1}) cannot be converted \

View File

@ -37,6 +37,7 @@ public abstract class AbstractConcurrentEventManager implements EventManager {
private static Exception[] EMPTY_EXCEPTIONS = new Exception[0];
private final Collection _listeners;
private boolean _failFast = false;
/**
* Default constructor.
@ -45,6 +46,20 @@ public abstract class AbstractConcurrentEventManager implements EventManager {
_listeners = newListenerCollection();
}
/**
* Whether to fail after the first exception thrown by any listener.
*/
public boolean isFailFast() {
return _failFast;
}
/**
* Whether to fail after the first exception thrown by any listener.
*/
public void setFailFast(boolean failFast) {
_failFast = failFast;
}
/**
* Register an event listener.
*/
@ -93,6 +108,8 @@ public abstract class AbstractConcurrentEventManager implements EventManager {
try {
fireEvent(event, itr.next());
} catch (Exception e) {
if (_failFast)
return new Exception[] { e };
if (exceptions == null)
exceptions = new LinkedList();
exceptions.add(e);

View File

@ -283,6 +283,14 @@ public class EntityManagerImpl
_broker.removeTransactionListener(listener);
}
public int getTransactionListenerCallbackMode() {
return _broker.getTransactionListenerCallbackMode();
}
public void setTransactionListenerCallbackMode(int mode) {
_broker.setTransactionListenerCallbackMode(mode);
}
public void addLifecycleListener(Object listener, Class... classes) {
_broker.addLifecycleListener(listener, classes);
}
@ -291,6 +299,14 @@ public class EntityManagerImpl
_broker.removeLifecycleListener(listener);
}
public int getLifecycleListenerCallbackMode() {
return _broker.getLifecycleListenerCallbackMode();
}
public void setLifecycleListenerCallbackMode(int mode) {
_broker.setLifecycleListenerCallbackMode(mode);
}
@SuppressWarnings("unchecked")
public <T> T getReference(Class<T> cls, Object oid) {
oid = _broker.newObjectId(cls, oid);

View File

@ -23,6 +23,7 @@ import javax.persistence.Query;
import org.apache.openjpa.conf.OpenJPAConfiguration;
import org.apache.openjpa.ee.ManagedRuntime;
import org.apache.openjpa.event.CallbackModes;
import org.apache.openjpa.kernel.AutoClear;
import org.apache.openjpa.kernel.AutoDetach;
import org.apache.openjpa.kernel.ConnectionRetainModes;
@ -41,7 +42,7 @@ public interface OpenJPAEntityManager
extends EntityManager, EntityTransaction, javax.resource.cci.Connection,
javax.resource.cci.LocalTransaction, javax.resource.spi.LocalTransaction,
Closeable, ConnectionRetainModes, DetachState, RestoreState, AutoDetach,
AutoClear {
AutoClear, CallbackModes {
/**
* Return the factory that produced this entity manager.
@ -300,6 +301,18 @@ public interface OpenJPAEntityManager
*/
public void removeTransactionListener(Object listener);
/**
* The {@link CallbackModes} flags for handling transaction listener
* exceptions.
*/
public int getTransactionListenerCallbackMode();
/**
* The {@link CallbackModes} flags for handling transaction listener
* exceptions.
*/
public void setTransactionListenerCallbackMode(int callbackMode);
/**
* Register a listener for lifecycle-related events on the specified
* classes. If the classes are null, all events will be propagated to
@ -312,6 +325,18 @@ public interface OpenJPAEntityManager
*/
public void removeLifecycleListener(Object listener);
/**
* The {@link CallbackModes} flags for handling lifecycle listener
* exceptions.
*/
public int getLifecycleListenerCallbackMode();
/**
* The {@link CallbackModes} flags for handling lifecycle listener
* exceptions.
*/
public void setLifecycleListenerCallbackMode(int callbackMode);
///////////
// Lookups
///////////