diff options
Diffstat (limited to 'src/main/java/com/amazon/carbonado')
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 @@ -63,9 +63,26 @@ public abstract class Trigger<S> { }
/**
+ * 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.
+ *
+ * <p>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.
*
* <p>Any exception thrown by this method will cause the tryInsert operation
* to rollback and all remaining triggers to not run. The exception is
@@ -80,6 +97,25 @@ public abstract class Trigger<S> { }
/**
+ * 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.
+ *
+ * <p>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.
*
@@ -141,9 +177,26 @@ public abstract class Trigger<S> { }
/**
+ * 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.
+ *
+ * <p>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.
*
* <p>Any exception thrown by this method will cause the tryUpdate operation
* to rollback and all remaining triggers to not run. The exception is
@@ -158,6 +211,25 @@ public abstract class Trigger<S> { }
/**
+ * 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.
+ *
+ * <p>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.
*
@@ -220,9 +292,26 @@ public abstract class Trigger<S> { }
/**
+ * 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.
+ *
+ * <p>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.
*
* <p>Any exception thrown by this method will cause the tryDelete operation
* to rollback and all remaining triggers to not run. The exception is
@@ -237,6 +326,25 @@ public abstract class Trigger<S> { }
/**
+ * 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.
+ *
+ * <p>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<S extends Storable> { // 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<S extends Storable> { 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<S extends Storable> { 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<S extends Storable, D extends Storable> 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<S extends Storable> extends Trigger<S> { }
@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<S extends Storable> extends Trigger<S> { }
@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<S extends Storable> implements Storage<S> { }
/**
- * For testing only.
+ * Exposed for testing only.
*/
- ReplicatedStorage(Repository aRepository,
+ ReplicatedStorage(ReplicatedRepository aRepository,
Storage<S> replicaStorage,
Storage<S> 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<S extends Storable> extends Trigger<S> {
- private final Repository mRepository;
+ private final ReplicatedRepository mRepository;
private final Storage<S> mReplicaStorage;
private final Storage<S> mMasterStorage;
private final TriggerManager<S> mTriggerManager;
- ReplicationTrigger(Repository repository,
+ ReplicationTrigger(ReplicatedRepository repository,
Storage<S> replicaStorage,
Storage<S> masterStorage)
{
@@ -231,9 +231,11 @@ class ReplicationTrigger<S extends Storable> extends Trigger<S> { 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<S extends Storable> extends Trigger<S> { }
}
- 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<S extends Storable> extends Trigger<S> { 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);
@@ -317,11 +315,21 @@ public class TriggerManager<S extends Storable> extends Trigger<S> { }
@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);
}
@@ -342,11 +350,21 @@ public class TriggerManager<S extends Storable> extends Trigger<S> { }
@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);
}
@@ -367,11 +385,21 @@ public class TriggerManager<S extends Storable> extends Trigger<S> { }
@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<S extends Storable> extends Trigger<S> { 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<S extends Storable> extends Trigger<S> { return types;
}
- private boolean overridesMethod(Class<? extends Trigger> triggerClass, Method method) {
+ private static boolean overridesOneMethod(Class<? extends Trigger> triggerClass,
+ Method[] methods)
+ {
+ for (Method method : methods) {
+ if (overridesMethod(triggerClass, method)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean overridesMethod(Class<? extends Trigger> triggerClass, Method method) {
try {
return !method.equals(triggerClass.getMethod(method.getName(),
method.getParameterTypes()));
@@ -558,6 +582,28 @@ public class TriggerManager<S extends Storable> extends Trigger<S> { }
@Override
+ public Object beforeInsert(Transaction txn, S storable) throws PersistException {
+ if (isLocallyDisabled()) {
+ return null;
+ }
+
+ TriggerStates<S> triggerStates = null;
+ Trigger<? super S>[] 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<S>(triggers);
+ }
+ triggerStates.mStates[i] = state;
+ }
+ }
+
+ return triggerStates == null ? triggers : triggerStates;
+ }
+
+ @Override
public Object beforeTryInsert(S storable) throws PersistException {
if (isLocallyDisabled()) {
return null;
@@ -580,6 +626,28 @@ public class TriggerManager<S extends Storable> extends Trigger<S> { }
@Override
+ public Object beforeTryInsert(Transaction txn, S storable) throws PersistException {
+ if (isLocallyDisabled()) {
+ return null;
+ }
+
+ TriggerStates<S> triggerStates = null;
+ Trigger<? super S>[] 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<S>(triggers);
+ }
+ triggerStates.mStates[i] = state;
+ }
+ }
+
+ return triggerStates == null ? triggers : triggerStates;
+ }
+
+ @Override
public void afterInsert(S storable, Object state) throws PersistException {
if (isLocallyDisabled()) {
return;
@@ -713,6 +781,28 @@ public class TriggerManager<S extends Storable> extends Trigger<S> { }
@Override
+ public Object beforeUpdate(Transaction txn, S storable) throws PersistException {
+ if (isLocallyDisabled()) {
+ return null;
+ }
+
+ TriggerStates<S> triggerStates = null;
+ Trigger<? super S>[] 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<S>(triggers);
+ }
+ triggerStates.mStates[i] = state;
+ }
+ }
+
+ return triggerStates == null ? triggers : triggerStates;
+ }
+
+ @Override
public Object beforeTryUpdate(S storable) throws PersistException {
if (isLocallyDisabled()) {
return null;
@@ -735,6 +825,28 @@ public class TriggerManager<S extends Storable> extends Trigger<S> { }
@Override
+ public Object beforeTryUpdate(Transaction txn, S storable) throws PersistException {
+ if (isLocallyDisabled()) {
+ return null;
+ }
+
+ TriggerStates<S> triggerStates = null;
+ Trigger<? super S>[] 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<S>(triggers);
+ }
+ triggerStates.mStates[i] = state;
+ }
+ }
+
+ return triggerStates == null ? triggers : triggerStates;
+ }
+
+ @Override
public void afterUpdate(S storable, Object state) throws PersistException {
if (isLocallyDisabled()) {
return;
@@ -868,6 +980,29 @@ public class TriggerManager<S extends Storable> extends Trigger<S> { }
@Override
+ public Object beforeDelete(Transaction txn, S storable) throws PersistException {
+ if (isLocallyDisabled()) {
+ return null;
+ }
+
+ TriggerStates<S> triggerStates = null;
+ Trigger<? super S>[] 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<S>(triggers);
+ }
+ triggerStates.mStates[i] = state;
+ }
+ }
+
+ return triggerStates == null ? triggers : triggerStates;
+ }
+
+
+ @Override
public Object beforeTryDelete(S storable) throws PersistException {
if (isLocallyDisabled()) {
return null;
@@ -890,6 +1025,28 @@ public class TriggerManager<S extends Storable> extends Trigger<S> { }
@Override
+ public Object beforeTryDelete(Transaction txn, S storable) throws PersistException {
+ if (isLocallyDisabled()) {
+ return null;
+ }
+
+ TriggerStates<S> triggerStates = null;
+ Trigger<? super S>[] 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<S>(triggers);
+ }
+ triggerStates.mStates[i] = state;
+ }
+ }
+
+ return triggerStates == null ? triggers : triggerStates;
+ }
+
+ @Override
public void afterDelete(S storable, Object state) throws PersistException {
if (isLocallyDisabled()) {
return;
|