From 341e3975fefdf5e2fb9a62cdf762af7738657ee6 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Wed, 28 Nov 2012 00:50:54 +0000 Subject: ReplicatedRepository resync no longer creates a for-update transaction on the master. Pass transaction to trigger, allowing it to change modes. --- src/main/java/com/amazon/carbonado/Trigger.java | 120 ++++++++- .../amazon/carbonado/gen/StorableGenerator.java | 19 +- .../repo/indexed/DerivedIndexesTrigger.java | 3 +- .../carbonado/repo/indexed/IndexesTrigger.java | 13 +- .../repo/replicated/ReplicatedStorage.java | 4 +- .../repo/replicated/ReplicationTrigger.java | 19 +- .../com/amazon/carbonado/spi/TriggerManager.java | 275 ++++++++++++++++----- 7 files changed, 369 insertions(+), 84 deletions(-) diff --git a/src/main/java/com/amazon/carbonado/Trigger.java b/src/main/java/com/amazon/carbonado/Trigger.java index 564b86c..c8b12d3 100644 --- a/src/main/java/com/amazon/carbonado/Trigger.java +++ b/src/main/java/com/amazon/carbonado/Trigger.java @@ -62,10 +62,27 @@ public abstract class Trigger { return null; } + /** + * Called before a storable is to be inserted. The default implementation + * simply calls {@link #beforeInsert(Object)}. Only override if access to + * the transaction is required. + * + *

Any exception thrown by this method will cause the insert operation + * to rollback and all remaining triggers to not run. The exception is + * ultimately passed to the caller of the insert method. + * + * @param txn nested transaction which trigger runs in + * @param storable storable before being inserted + * @return arbitrary state object, passed to afterInsert or failedInsert method + */ + public Object beforeInsert(Transaction txn, S storable) throws PersistException { + return beforeInsert(storable); + } + /** * Called before a storable is to be inserted via tryInsert. The default - * implementation simply calls {@link #beforeInsert}. Only override if - * trigger needs to distinguish between different insert variants. + * implementation simply calls {@link #beforeInsert(Object)}. Only override + * if trigger needs to distinguish between different insert variants. * *

Any exception thrown by this method will cause the tryInsert operation * to rollback and all remaining triggers to not run. The exception is @@ -79,6 +96,25 @@ public abstract class Trigger { return beforeInsert(storable); } + /** + * Called before a storable is to be inserted via tryInsert. The default + * implementation simply calls {@link #beforeTryInsert(Object)}. Only + * override if trigger needs access to the transaction and to distinguish + * between different insert variants. + * + *

Any exception thrown by this method will cause the tryInsert operation + * to rollback and all remaining triggers to not run. The exception is + * ultimately passed to the caller of the tryInsert method. + * + * @param txn nested transaction which trigger runs in + * @param storable storable before being inserted + * @return arbitrary state object, passed to afterTryInsert or failedInsert method + * @see #abortTry + */ + public Object beforeTryInsert(Transaction txn, S storable) throws PersistException { + return beforeTryInsert(storable); + } + /** * Called right after a storable has been successfully inserted. The * default implementation does nothing. @@ -140,10 +176,27 @@ public abstract class Trigger { return null; } + /** + * Called before a storable is to be updated. The default implementation + * simply calls {@link #beforeUpdate(Object)}. Only override if access to + * the transaction is required. + * + *

Any exception thrown by this method will cause the update operation + * to rollback and all remaining triggers to not run. The exception is + * ultimately passed to the caller of the update method. + * + * @param txn nested transaction which trigger runs in + * @param storable storable before being updated + * @return arbitrary state object, passed to afterUpdate or failedUpdate method + */ + public Object beforeUpdate(Transaction txn, S storable) throws PersistException { + return beforeUpdate(storable); + } + /** * Called before a storable is to be updated via tryUpdate. The default - * implementation simply calls {@link #beforeUpdate}. Only override if - * trigger needs to distinguish between different update variants. + * implementation simply calls {@link #beforeUpdate(Object)}. Only override + * if trigger needs to distinguish between different update variants. * *

Any exception thrown by this method will cause the tryUpdate operation * to rollback and all remaining triggers to not run. The exception is @@ -157,6 +210,25 @@ public abstract class Trigger { return beforeUpdate(storable); } + /** + * Called before a storable is to be updated via tryUpdate. The default + * implementation simply calls {@link #beforeTryUpdate(Object)}. Only + * override if trigger needs access to the transaction and to distinguish + * between different update variants. + * + *

Any exception thrown by this method will cause the tryUpdate operation + * to rollback and all remaining triggers to not run. The exception is + * ultimately passed to the caller of the tryUpdate method. + * + * @param txn nested transaction which trigger runs in + * @param storable storable before being updated + * @return arbitrary state object, passed to afterTryUpdate or failedUpdate method + * @see #abortTry + */ + public Object beforeTryUpdate(Transaction txn, S storable) throws PersistException { + return beforeTryUpdate(storable); + } + /** * Called right after a storable has been successfully updated. The default * implementation does nothing. @@ -219,10 +291,27 @@ public abstract class Trigger { return null; } + /** + * Called before a storable is to be deleted. The default implementation + * simply calls {@link #beforeDelete(Object)}. Only override if access to + * the transaction is required. + * + *

Any exception thrown by this method will cause the delete operation + * to rollback and all remaining triggers to not run. The exception is + * ultimately passed to the caller of the delete method. + * + * @param txn nested transaction which trigger runs in + * @param storable storable before being deleted + * @return arbitrary state object, passed to afterDelete or failedDelete method + */ + public Object beforeDelete(Transaction txn, S storable) throws PersistException { + return beforeDelete(storable); + } + /** * Called before a storable is to be deleted via tryDelete. The default - * implementation simply calls {@link #beforeDelete}. Only override if - * trigger needs to distinguish between different delete variants. + * implementation simply calls {@link #beforeDelete(Object)}. Only override + * if trigger needs to distinguish between different delete variants. * *

Any exception thrown by this method will cause the tryDelete operation * to rollback and all remaining triggers to not run. The exception is @@ -236,6 +325,25 @@ public abstract class Trigger { return beforeDelete(storable); } + /** + * Called before a storable is to be deleted via tryDelete. The default + * implementation simply calls {@link #beforeTryDelete(Object)}. Only + * override if trigger needs access to the transaction and to distinguish + * between different delete variants. + * + *

Any exception thrown by this method will cause the tryDelete operation + * to rollback and all remaining triggers to not run. The exception is + * ultimately passed to the caller of the tryDelete method. + * + * @param txn nested transaction which trigger runs in + * @param storable storable before being deleted + * @return arbitrary state object, passed to afterTryDelete or failedDelete method + * @see #abortTry + */ + public Object beforeTryDelete(Transaction txn, S storable) throws PersistException { + return beforeTryDelete(storable); + } + /** * Called right after a storable has been successfully deleted. The default * implementation does nothing. diff --git a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java index 4a0d74e..59312c8 100644 --- a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java @@ -3296,9 +3296,9 @@ public final class StorableGenerator { // txn = support.getRootRepository().enterTransaction(); // tryStart: // if (forTry) { - // state = trigger.beforeTryXxx(this); + // state = trigger.beforeTryXxx(txn, this); // } else { - // state = trigger.beforeXxx(this); + // state = trigger.beforeXxx(txn, this); // } // } b.loadLocal(triggerVar); @@ -3325,20 +3325,23 @@ public final class StorableGenerator { Label tryStart = b.createLabel().setLocation(); // if (forTry) { - // state = trigger.beforeTryXxx(this); + // state = trigger.beforeTryXxx(txn, this); // } else { - // state = trigger.beforeXxx(this); + // state = trigger.beforeXxx(txn, this); // } b.loadLocal(triggerVar); + b.loadLocal(txnVar); b.loadThis(); if (forTryVar == null) { if (forTry) { b.invokeVirtual(triggerVar.getType(), "beforeTry" + opType, - TypeDesc.OBJECT, new TypeDesc[] {TypeDesc.OBJECT}); + TypeDesc.OBJECT, + new TypeDesc[] {transactionType, TypeDesc.OBJECT}); } else { b.invokeVirtual(triggerVar.getType(), "before" + opType, - TypeDesc.OBJECT, new TypeDesc[] {TypeDesc.OBJECT}); + TypeDesc.OBJECT, + new TypeDesc[] {transactionType, TypeDesc.OBJECT}); } b.storeLocal(stateVar); } else { @@ -3347,13 +3350,13 @@ public final class StorableGenerator { b.ifZeroComparisonBranch(isForTry, "!="); b.invokeVirtual(triggerVar.getType(), "before" + opType, - TypeDesc.OBJECT, new TypeDesc[] {TypeDesc.OBJECT}); + TypeDesc.OBJECT, new TypeDesc[] {transactionType, TypeDesc.OBJECT}); b.storeLocal(stateVar); b.branch(cont); isForTry.setLocation(); b.invokeVirtual(triggerVar.getType(), "beforeTry" + opType, - TypeDesc.OBJECT, new TypeDesc[] {TypeDesc.OBJECT}); + TypeDesc.OBJECT, new TypeDesc[] {transactionType, TypeDesc.OBJECT}); b.storeLocal(stateVar); } diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/DerivedIndexesTrigger.java b/src/main/java/com/amazon/carbonado/repo/indexed/DerivedIndexesTrigger.java index 7a1073d..bcb866b 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/DerivedIndexesTrigger.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/DerivedIndexesTrigger.java @@ -75,7 +75,8 @@ class DerivedIndexesTrigger extends Trig } @Override - public Object beforeDelete(S storable) throws PersistException { + public Object beforeDelete(Transaction txn, S storable) throws PersistException { + txn.setForUpdate(true); try { if (storable.copy().tryLoad()) { return createDependentIndexEntries(storable); diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/IndexesTrigger.java b/src/main/java/com/amazon/carbonado/repo/indexed/IndexesTrigger.java index fc8a18a..67f29e8 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/IndexesTrigger.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/IndexesTrigger.java @@ -24,6 +24,7 @@ import com.amazon.carbonado.CorruptEncodingException; import com.amazon.carbonado.FetchException; import com.amazon.carbonado.PersistException; import com.amazon.carbonado.Storable; +import com.amazon.carbonado.Transaction; import com.amazon.carbonado.Trigger; import com.amazon.carbonado.UniqueConstraintException; @@ -63,7 +64,11 @@ class IndexesTrigger extends Trigger { } @Override - public Object beforeUpdate(S storable) throws PersistException { + public Object beforeUpdate(Transaction txn, S storable) throws PersistException { + // Ensure old storable is loaded with an upgradable lock, allowing + // update to proceed without deadlock. + txn.setForUpdate(true); + // Return old storable for afterUpdate. S copy = (S) storable.copy(); try { @@ -88,7 +93,11 @@ class IndexesTrigger extends Trigger { } @Override - public Object beforeDelete(S storable) throws PersistException { + public Object beforeDelete(Transaction txn, S storable) throws PersistException { + // Ensure old storable is loaded with an upgradable lock, allowing + // delete to proceed without deadlock. + txn.setForUpdate(true); + // Delete index entries referenced by existing storable. S copy = (S) storable.copy(); try { diff --git a/src/main/java/com/amazon/carbonado/repo/replicated/ReplicatedStorage.java b/src/main/java/com/amazon/carbonado/repo/replicated/ReplicatedStorage.java index cd65f46..f99276d 100644 --- a/src/main/java/com/amazon/carbonado/repo/replicated/ReplicatedStorage.java +++ b/src/main/java/com/amazon/carbonado/repo/replicated/ReplicatedStorage.java @@ -74,9 +74,9 @@ class ReplicatedStorage implements Storage { } /** - * For testing only. + * Exposed for testing only. */ - ReplicatedStorage(Repository aRepository, + ReplicatedStorage(ReplicatedRepository aRepository, Storage replicaStorage, Storage masterStorage) { diff --git a/src/main/java/com/amazon/carbonado/repo/replicated/ReplicationTrigger.java b/src/main/java/com/amazon/carbonado/repo/replicated/ReplicationTrigger.java index c646f8c..ce0ce53 100644 --- a/src/main/java/com/amazon/carbonado/repo/replicated/ReplicationTrigger.java +++ b/src/main/java/com/amazon/carbonado/repo/replicated/ReplicationTrigger.java @@ -49,13 +49,13 @@ import com.amazon.carbonado.spi.TriggerManager; * @author Brian S O'Neill */ class ReplicationTrigger extends Trigger { - private final Repository mRepository; + private final ReplicatedRepository mRepository; private final Storage mReplicaStorage; private final Storage mMasterStorage; private final TriggerManager mTriggerManager; - ReplicationTrigger(Repository repository, + ReplicationTrigger(ReplicatedRepository repository, Storage replicaStorage, Storage masterStorage) { @@ -231,9 +231,11 @@ class ReplicationTrigger extends Trigger { setReplicationDisabled(); try { - Transaction txn = mRepository.enterTransaction(); + Transaction masterTxn = mRepository.getMasterRepository().enterTransaction(); + Transaction replicaTxn = mRepository.getReplicaRepository().enterTransaction(); + try { - txn.setForUpdate(true); + replicaTxn.setForUpdate(true); if (reload) { if (masterEntry == null) { @@ -320,13 +322,18 @@ class ReplicationTrigger extends Trigger { } } - txn.commit(); + replicaTxn.commit(); } catch (Throwable e) { resyncFailed(listener, replicaEntry, masterEntry, newReplicaEntry, state); ThrowUnchecked.fire(e); } } finally { - txn.exit(); + try { + masterTxn.exit(); + } finally { + // Do second, favoring any exception thrown from it. + replicaTxn.exit(); + } } } finally { setReplicationEnabled(); diff --git a/src/main/java/com/amazon/carbonado/spi/TriggerManager.java b/src/main/java/com/amazon/carbonado/spi/TriggerManager.java index f7d4a10..6f4e6c7 100644 --- a/src/main/java/com/amazon/carbonado/spi/TriggerManager.java +++ b/src/main/java/com/amazon/carbonado/spi/TriggerManager.java @@ -30,6 +30,7 @@ import com.amazon.carbonado.FetchException; import com.amazon.carbonado.PersistException; import com.amazon.carbonado.RepositoryException; import com.amazon.carbonado.Storable; +import com.amazon.carbonado.Transaction; import com.amazon.carbonado.Trigger; import com.amazon.carbonado.TriggerFactory; @@ -47,52 +48,49 @@ public class TriggerManager extends Trigger { private static final int FOR_DELETE = 4; private static final int FOR_LOAD = 8; - private static final Method - BEFORE_INSERT_METHOD, - BEFORE_TRY_INSERT_METHOD, - AFTER_INSERT_METHOD, - AFTER_TRY_INSERT_METHOD, - FAILED_INSERT_METHOD, - - BEFORE_UPDATE_METHOD, - BEFORE_TRY_UPDATE_METHOD, - AFTER_UPDATE_METHOD, - AFTER_TRY_UPDATE_METHOD, - FAILED_UPDATE_METHOD, - - BEFORE_DELETE_METHOD, - BEFORE_TRY_DELETE_METHOD, - AFTER_DELETE_METHOD, - AFTER_TRY_DELETE_METHOD, - FAILED_DELETE_METHOD, - - AFTER_LOAD_METHOD; + private static final Method[] INSERT_METHODS; + private static final Method[] UPDATE_METHODS; + private static final Method[] DELETE_METHODS; + private static final Method AFTER_LOAD_METHOD; static { Class triggerClass = Trigger.class; - Class[] ONE_PARAM = {Object.class}; + Class[] ONE_PARAM = {Object.class}; + Class[] TXN_PARAMS = {Transaction.class, Object.class}; Class[] TWO_PARAMS = {Object.class, Object.class}; try { - BEFORE_INSERT_METHOD = triggerClass.getMethod("beforeInsert", ONE_PARAM); - BEFORE_TRY_INSERT_METHOD = triggerClass.getMethod("beforeTryInsert", ONE_PARAM); - AFTER_INSERT_METHOD = triggerClass.getMethod("afterInsert", TWO_PARAMS); - AFTER_TRY_INSERT_METHOD = triggerClass.getMethod("afterTryInsert", TWO_PARAMS); - FAILED_INSERT_METHOD = triggerClass.getMethod("failedInsert", TWO_PARAMS); - - BEFORE_UPDATE_METHOD = triggerClass.getMethod("beforeUpdate", ONE_PARAM); - BEFORE_TRY_UPDATE_METHOD = triggerClass.getMethod("beforeTryUpdate", ONE_PARAM); - AFTER_UPDATE_METHOD = triggerClass.getMethod("afterUpdate", TWO_PARAMS); - AFTER_TRY_UPDATE_METHOD = triggerClass.getMethod("afterTryUpdate", TWO_PARAMS); - FAILED_UPDATE_METHOD = triggerClass.getMethod("failedUpdate", TWO_PARAMS); - - BEFORE_DELETE_METHOD = triggerClass.getMethod("beforeDelete", ONE_PARAM); - BEFORE_TRY_DELETE_METHOD = triggerClass.getMethod("beforeTryDelete", ONE_PARAM); - AFTER_DELETE_METHOD = triggerClass.getMethod("afterDelete", TWO_PARAMS); - AFTER_TRY_DELETE_METHOD = triggerClass.getMethod("afterTryDelete", TWO_PARAMS); - FAILED_DELETE_METHOD = triggerClass.getMethod("failedDelete", TWO_PARAMS); - - AFTER_LOAD_METHOD = triggerClass.getMethod("afterLoad", ONE_PARAM); + INSERT_METHODS = new Method[] { + triggerClass.getMethod("beforeInsert", ONE_PARAM), + triggerClass.getMethod("beforeInsert", TXN_PARAMS), + triggerClass.getMethod("beforeTryInsert", ONE_PARAM), + triggerClass.getMethod("beforeTryInsert", TXN_PARAMS), + triggerClass.getMethod("afterInsert", TWO_PARAMS), + triggerClass.getMethod("afterTryInsert", TWO_PARAMS), + triggerClass.getMethod("failedInsert", TWO_PARAMS) + }; + + UPDATE_METHODS = new Method[] { + triggerClass.getMethod("beforeUpdate", ONE_PARAM), + triggerClass.getMethod("beforeUpdate", TXN_PARAMS), + triggerClass.getMethod("beforeTryUpdate", ONE_PARAM), + triggerClass.getMethod("beforeTryUpdate", TXN_PARAMS), + triggerClass.getMethod("afterUpdate", TWO_PARAMS), + triggerClass.getMethod("afterTryUpdate", TWO_PARAMS), + triggerClass.getMethod("failedUpdate", TWO_PARAMS) + }; + + DELETE_METHODS = new Method[] { + triggerClass.getMethod("beforeDelete", ONE_PARAM), + triggerClass.getMethod("beforeDelete", TXN_PARAMS), + triggerClass.getMethod("beforeTryDelete", ONE_PARAM), + triggerClass.getMethod("beforeTryDelete", TXN_PARAMS), + triggerClass.getMethod("afterDelete", TWO_PARAMS), + triggerClass.getMethod("afterTryDelete", TWO_PARAMS), + triggerClass.getMethod("failedDelete", TWO_PARAMS) + }; + + AFTER_LOAD_METHOD = triggerClass.getMethod("afterLoad", ONE_PARAM); } catch (NoSuchMethodException e) { Error error = new NoSuchMethodError(); error.initCause(e); @@ -316,11 +314,21 @@ public class TriggerManager extends Trigger { return mForInsert.beforeInsert(storable); } + @Override + public Object beforeInsert(Transaction txn, S storable) throws PersistException { + return mForInsert.beforeInsert(txn, storable); + } + @Override public Object beforeTryInsert(S storable) throws PersistException { return mForInsert.beforeTryInsert(storable); } + @Override + public Object beforeTryInsert(Transaction txn, S storable) throws PersistException { + return mForInsert.beforeTryInsert(txn, storable); + } + @Override public void afterInsert(S storable, Object state) throws PersistException { mForInsert.afterInsert(storable, state); @@ -341,11 +349,21 @@ public class TriggerManager extends Trigger { return mForUpdate.beforeUpdate(storable); } + @Override + public Object beforeUpdate(Transaction txn, S storable) throws PersistException { + return mForUpdate.beforeUpdate(txn, storable); + } + @Override public Object beforeTryUpdate(S storable) throws PersistException { return mForUpdate.beforeTryUpdate(storable); } + @Override + public Object beforeTryUpdate(Transaction txn, S storable) throws PersistException { + return mForUpdate.beforeTryUpdate(txn, storable); + } + @Override public void afterUpdate(S storable, Object state) throws PersistException { mForUpdate.afterUpdate(storable, state); @@ -366,11 +384,21 @@ public class TriggerManager extends Trigger { return mForDelete.beforeDelete(storable); } + @Override + public Object beforeDelete(Transaction txn, S storable) throws PersistException { + return mForDelete.beforeDelete(txn, storable); + } + @Override public Object beforeTryDelete(S storable) throws PersistException { return mForDelete.beforeTryDelete(storable); } + @Override + public Object beforeTryDelete(Transaction txn, S storable) throws PersistException { + return mForDelete.beforeTryDelete(txn, storable); + } + @Override public void afterDelete(S storable, Object state) throws PersistException { mForDelete.afterDelete(storable, state); @@ -399,30 +427,15 @@ public class TriggerManager extends Trigger { int types = 0; - if (overridesMethod(triggerClass, BEFORE_INSERT_METHOD) || - overridesMethod(triggerClass, BEFORE_TRY_INSERT_METHOD) || - overridesMethod(triggerClass, AFTER_INSERT_METHOD) || - overridesMethod(triggerClass, AFTER_TRY_INSERT_METHOD) || - overridesMethod(triggerClass, FAILED_INSERT_METHOD)) - { + if (overridesOneMethod(triggerClass, INSERT_METHODS)) { types |= FOR_INSERT; } - if (overridesMethod(triggerClass, BEFORE_UPDATE_METHOD) || - overridesMethod(triggerClass, BEFORE_TRY_UPDATE_METHOD) || - overridesMethod(triggerClass, AFTER_UPDATE_METHOD) || - overridesMethod(triggerClass, AFTER_TRY_UPDATE_METHOD) || - overridesMethod(triggerClass, FAILED_UPDATE_METHOD)) - { + if (overridesOneMethod(triggerClass, UPDATE_METHODS)) { types |= FOR_UPDATE; } - if (overridesMethod(triggerClass, BEFORE_DELETE_METHOD) || - overridesMethod(triggerClass, BEFORE_TRY_DELETE_METHOD) || - overridesMethod(triggerClass, AFTER_DELETE_METHOD) || - overridesMethod(triggerClass, AFTER_TRY_DELETE_METHOD) || - overridesMethod(triggerClass, FAILED_DELETE_METHOD)) - { + if (overridesOneMethod(triggerClass, DELETE_METHODS)) { types |= FOR_DELETE; } @@ -433,7 +446,18 @@ public class TriggerManager extends Trigger { return types; } - private boolean overridesMethod(Class triggerClass, Method method) { + private static boolean overridesOneMethod(Class triggerClass, + Method[] methods) + { + for (Method method : methods) { + if (overridesMethod(triggerClass, method)) { + return true; + } + } + return false; + } + + private static boolean overridesMethod(Class triggerClass, Method method) { try { return !method.equals(triggerClass.getMethod(method.getName(), method.getParameterTypes())); @@ -557,6 +581,28 @@ public class TriggerManager extends Trigger { return triggerStates == null ? triggers : triggerStates; } + @Override + public Object beforeInsert(Transaction txn, S storable) throws PersistException { + if (isLocallyDisabled()) { + return null; + } + + TriggerStates triggerStates = null; + Trigger[] triggers = mTriggers; + + for (int i=triggers.length; --i>=0; ) { + Object state = triggers[i].beforeInsert(txn, storable); + if (state != null) { + if (triggerStates == null) { + triggerStates = new TriggerStates(triggers); + } + triggerStates.mStates[i] = state; + } + } + + return triggerStates == null ? triggers : triggerStates; + } + @Override public Object beforeTryInsert(S storable) throws PersistException { if (isLocallyDisabled()) { @@ -579,6 +625,28 @@ public class TriggerManager extends Trigger { return triggerStates == null ? triggers : triggerStates; } + @Override + public Object beforeTryInsert(Transaction txn, S storable) throws PersistException { + if (isLocallyDisabled()) { + return null; + } + + TriggerStates triggerStates = null; + Trigger[] triggers = mTriggers; + + for (int i=triggers.length; --i>=0; ) { + Object state = triggers[i].beforeTryInsert(txn, storable); + if (state != null) { + if (triggerStates == null) { + triggerStates = new TriggerStates(triggers); + } + triggerStates.mStates[i] = state; + } + } + + return triggerStates == null ? triggers : triggerStates; + } + @Override public void afterInsert(S storable, Object state) throws PersistException { if (isLocallyDisabled()) { @@ -712,6 +780,28 @@ public class TriggerManager extends Trigger { return triggerStates == null ? triggers : triggerStates; } + @Override + public Object beforeUpdate(Transaction txn, S storable) throws PersistException { + if (isLocallyDisabled()) { + return null; + } + + TriggerStates triggerStates = null; + Trigger[] triggers = mTriggers; + + for (int i=triggers.length; --i>=0; ) { + Object state = triggers[i].beforeUpdate(txn, storable); + if (state != null) { + if (triggerStates == null) { + triggerStates = new TriggerStates(triggers); + } + triggerStates.mStates[i] = state; + } + } + + return triggerStates == null ? triggers : triggerStates; + } + @Override public Object beforeTryUpdate(S storable) throws PersistException { if (isLocallyDisabled()) { @@ -734,6 +824,28 @@ public class TriggerManager extends Trigger { return triggerStates == null ? triggers : triggerStates; } + @Override + public Object beforeTryUpdate(Transaction txn, S storable) throws PersistException { + if (isLocallyDisabled()) { + return null; + } + + TriggerStates triggerStates = null; + Trigger[] triggers = mTriggers; + + for (int i=triggers.length; --i>=0; ) { + Object state = triggers[i].beforeTryUpdate(txn, storable); + if (state != null) { + if (triggerStates == null) { + triggerStates = new TriggerStates(triggers); + } + triggerStates.mStates[i] = state; + } + } + + return triggerStates == null ? triggers : triggerStates; + } + @Override public void afterUpdate(S storable, Object state) throws PersistException { if (isLocallyDisabled()) { @@ -867,6 +979,29 @@ public class TriggerManager extends Trigger { return triggerStates == null ? triggers : triggerStates; } + @Override + public Object beforeDelete(Transaction txn, S storable) throws PersistException { + if (isLocallyDisabled()) { + return null; + } + + TriggerStates triggerStates = null; + Trigger[] triggers = mTriggers; + + for (int i=triggers.length; --i>=0; ) { + Object state = triggers[i].beforeDelete(txn, storable); + if (state != null) { + if (triggerStates == null) { + triggerStates = new TriggerStates(triggers); + } + triggerStates.mStates[i] = state; + } + } + + return triggerStates == null ? triggers : triggerStates; + } + + @Override public Object beforeTryDelete(S storable) throws PersistException { if (isLocallyDisabled()) { @@ -889,6 +1024,28 @@ public class TriggerManager extends Trigger { return triggerStates == null ? triggers : triggerStates; } + @Override + public Object beforeTryDelete(Transaction txn, S storable) throws PersistException { + if (isLocallyDisabled()) { + return null; + } + + TriggerStates triggerStates = null; + Trigger[] triggers = mTriggers; + + for (int i=triggers.length; --i>=0; ) { + Object state = triggers[i].beforeTryDelete(txn, storable); + if (state != null) { + if (triggerStates == null) { + triggerStates = new TriggerStates(triggers); + } + triggerStates.mStates[i] = state; + } + } + + return triggerStates == null ? triggers : triggerStates; + } + @Override public void afterDelete(S storable, Object state) throws PersistException { if (isLocallyDisabled()) { -- cgit v1.2.3