From 00adea5bc6916b717132c72768ab3fea1b2a1a22 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sun, 26 Aug 2007 01:11:29 +0000 Subject: Added support for load and query trigger. --- RELEASE-NOTES.txt | 1 + src/main/java/com/amazon/carbonado/Trigger.java | 12 ++ .../amazon/carbonado/gen/CommonMethodNames.java | 5 +- .../amazon/carbonado/gen/StorableGenerator.java | 98 +++++++++++-- .../com/amazon/carbonado/gen/TriggerSupport.java | 26 ++++ .../com/amazon/carbonado/gen/WrappedSupport.java | 1 + .../amazon/carbonado/raw/RawStorableGenerator.java | 5 +- .../carbonado/repo/jdbc/JDBCStorableGenerator.java | 5 +- .../amazon/carbonado/repo/jdbc/JDBCStorage.java | 12 ++ .../repo/replicated/ReplicationTrigger.java | 38 +++-- .../carbonado/repo/sleepycat/BDBStorage.java | 12 ++ .../com/amazon/carbonado/spi/TriggerManager.java | 163 +++++++++++++++++---- .../com/amazon/carbonado/spi/WrappedStorage.java | 14 ++ 13 files changed, 336 insertions(+), 56 deletions(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 4c8edb7..1f5141f 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -30,6 +30,7 @@ Carbonado change history - Added convenience method, Query.exists(). - Support query filters against one-to-many join properties. - Added support for defining alternate keys in synthetic Storables. +- Added trigger support for after loads and queries. 1.1 to 1.1.2 ------------------------------- diff --git a/src/main/java/com/amazon/carbonado/Trigger.java b/src/main/java/com/amazon/carbonado/Trigger.java index 7c1bbc9..4b6041c 100644 --- a/src/main/java/com/amazon/carbonado/Trigger.java +++ b/src/main/java/com/amazon/carbonado/Trigger.java @@ -286,6 +286,16 @@ public abstract class Trigger { public void failedDelete(S storable, Object state) { } + /** + * Called right after a storable has been successfully loaded or + * fetched. The default implementation does nothing. + * + * @param storable storable after being loaded or fetched + * @since 1.2 + */ + public void afterLoad(S storable) throws FetchException { + } + /** * Called after a Blob is loaded. Override to return an adapted Blob which * can listen for changes. By default, the original Blob is returned @@ -296,6 +306,7 @@ public abstract class Trigger { * @param blob non-null Blob property instance * @return adapted Blob * @since 1.2 + * @deprecated use afterLoad instead to adapt Blobs */ public Blob adaptBlob(S storable, String name, Blob blob) { return blob; @@ -311,6 +322,7 @@ public abstract class Trigger { * @param clob non-null Clob property instance * @return adapted Clob * @since 1.2 + * @deprecated use afterLoad instead to adapt Clobs */ public Clob adaptClob(S storable, String name, Clob clob) { return clob; diff --git a/src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java b/src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java index f0262ea..7187f38 100644 --- a/src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java +++ b/src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java @@ -85,6 +85,9 @@ public class CommonMethodNames { COMMIT_METHOD_NAME = "commit", EXIT_METHOD_NAME = "exit"; - /** WrappedStorage.Support API method name */ + /** + * WrappedStorage.Support API method name + * @deprecated + */ public static final String CREATE_WRAPPED_SUPPORT_METHOD_NAME = "createSupport"; } diff --git a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java index 388b52a..a753015 100644 --- a/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/gen/StorableGenerator.java @@ -130,6 +130,14 @@ public final class StorableGenerator { */ public static final String IS_VERSION_INITIALIZED_METHOD_NAME = "isVersionInitialized$"; + /** + * Name of protected method which must be called after load to identify all + * properties as valid and to fire any load triggers. + * + * @since 1.2 + */ + public static final String LOAD_COMPLETED_METHOD_NAME = "loadCompleted$"; + /** * Prefix of protected field in generated storable that holds property * states. Each property consumes two bits to hold its state, and so each @@ -255,8 +263,8 @@ public final class StorableGenerator { * *

As a convenience, protected methods are provided to test and alter * the property state bits. Subclass constructors that fill all properties - * with loaded values must call markAllPropertiesClean to ensure all - * properties are identified as being valid. + * with loaded values must call loadCompleted to ensure all properties are + * identified as being valid and to fire any load triggers. * *

      * // Returns true if all primary key properties have been set.
@@ -269,6 +277,11 @@ public final class StorableGenerator {
      * // Returns true if a version property has been set.
      * // Note: This method is not generated if there is no version property.
      * protected boolean isVersionInitialized();
+     *
+     * // Must be called after load to identify all properties as valid
+     * // and to fire any load triggers.
+     * // Actual method name defined by LOAD_COMPLETED_METHOD_NAME.
+     * protected void loadCompleted() throws FetchException;
      * 
* * Property state field names are defined by the concatenation of @@ -357,6 +370,7 @@ public final class StorableGenerator { * * @throws com.amazon.carbonado.MalformedTypeException if Storable type is not well-formed * @throws IllegalArgumentException if type is null + * @deprecated no replacement */ @SuppressWarnings("unchecked") public static Class getWrappedClass(Class type) @@ -480,6 +494,8 @@ public final class StorableGenerator { b.checkCast(TypeDesc.forClass(mStorableType)); b.storeField(WRAPPED_STORABLE_FIELD_NAME, TypeDesc.forClass(mStorableType)); + // FIXME: call loadCompleted + b.returnVoid(); } @@ -1237,11 +1253,28 @@ public final class StorableGenerator { // Run query sitting on the stack. runQuery.setLocation(); + // Locally disable load triggers, to hide the fact that we're + // using a query to load by alternate key. + + b.loadThis(); + b.loadField(SUPPORT_FIELD_NAME, mSupportType); + b.invoke(lookupMethod(mSupportType.toClass(), "locallyDisableLoadTrigger")); + + // try-finally start label + Label disableTriggerStart = b.createLabel().setLocation(); + b.invokeInterface(queryType, TRY_LOAD_ONE_METHOD_NAME, TypeDesc.forClass(Storable.class), null); LocalVariable fetchedVar = b.createLocalVariable(null, TypeDesc.OBJECT); b.storeLocal(fetchedVar); + // try-finally end label + Label disableTriggerEnd = b.createLabel().setLocation(); + + b.loadThis(); + b.loadField(SUPPORT_FIELD_NAME, mSupportType); + b.invoke(lookupMethod(mSupportType.toClass(), "locallyEnableLoadTrigger")); + // If query fetch is null, then object not found. Return false. b.loadLocal(fetchedVar); b.ifNullBranch(notLoaded, true); @@ -1260,6 +1293,16 @@ public final class StorableGenerator { new TypeDesc[] {TypeDesc.forClass(Storable.class)}); b.branch(loaded); + + // Handler for exception when load trigger is disabled. + b.exceptionHandler(disableTriggerStart, disableTriggerEnd, null); + LocalVariable exceptionVar = b.createLocalVariable(null, TypeDesc.OBJECT); + b.storeLocal(exceptionVar); + b.loadThis(); + b.loadField(SUPPORT_FIELD_NAME, mSupportType); + b.invoke(lookupMethod(mSupportType.toClass(), "locallyEnableLoadTrigger")); + b.loadLocal(exceptionVar); + b.throwObject(); } pkInitialized.setLocation(); @@ -1271,9 +1314,9 @@ public final class StorableGenerator { b.ifZeroComparisonBranch(notLoaded, "=="); loaded.setLocation(); - // Only mark properties clean if doTryLoad returned true. + // Only indicate load completed if doTryLoad returned true. b.loadThis(); - b.invokeVirtual(MARK_ALL_PROPERTIES_CLEAN, null, null); + b.invokeVirtual(LOAD_COMPLETED_METHOD_NAME, null, null); b.loadConstant(true); b.returnValue(TypeDesc.BOOLEAN); @@ -1788,7 +1831,7 @@ public final class StorableGenerator { b.loadLocal(copiedVar); // storeField later b.loadThis(); b.loadField(WRAPPED_STORABLE_FIELD_NAME, TypeDesc.forClass(mStorableType)); - b.invoke(lookupMethod(mStorableType, COPY_METHOD_NAME, null)); + b.invoke(lookupMethod(mStorableType, COPY_METHOD_NAME)); b.checkCast(TypeDesc.forClass(mStorableType)); b.storeField(WRAPPED_STORABLE_FIELD_NAME, TypeDesc.forClass(mStorableType)); @@ -1897,6 +1940,41 @@ public final class StorableGenerator { addMarkDirtyMethod(MARK_PROPERTIES_DIRTY); addMarkDirtyMethod(MARK_ALL_PROPERTIES_DIRTY); + // Define loadCompleted method. + { + MethodInfo mi = mClassFile.addMethod + (Modifiers.PROTECTED, LOAD_COMPLETED_METHOD_NAME, null, null); + mi.addException(TypeDesc.forClass(FetchException.class)); + + CodeBuilder b = new CodeBuilder(mi); + + if (mGenMode == GEN_ABSTRACT) { + b.loadThis(); + b.invokeVirtual(MARK_ALL_PROPERTIES_CLEAN, null, null); + } + + // Now invoke trigger. + b.loadThis(); + b.loadField(SUPPORT_FIELD_NAME, mSupportType); + b.invoke(lookupMethod(mSupportType.toClass(), "getLoadTrigger")); + LocalVariable triggerVar = + b.createLocalVariable(null, TypeDesc.forClass(Trigger.class)); + b.storeLocal(triggerVar); + b.loadLocal(triggerVar); + Label noTrigger = b.createLabel(); + b.ifNullBranch(noTrigger, true); + b.loadLocal(triggerVar); + b.loadThis(); + b.invoke(lookupMethod(triggerVar.getType().toClass(), "afterLoad", Object.class)); + + // In case trigger modified the properties, make sure they're still clean. + b.loadThis(); + b.invokeVirtual(MARK_ALL_PROPERTIES_CLEAN, null, null); + + noTrigger.setLocation(); + b.returnVoid(); + } + if (mGenMode == GEN_ABSTRACT) { // Define protected isPkInitialized method. addIsInitializedMethod @@ -1964,11 +2042,13 @@ public final class StorableGenerator { * WrappedSupport. Also clears join property state if called method * returns normally. * - * @param opType optional, is one of INSERT_OP, UPDATE_OP, or DELETE_OP, for trigger support + * @param opType optional, is one of INSERT_OP, UPDATE_OP, or DELETE_OP for trigger support * @param forTry used for INSERT_OP, UPDATE_OP, or DELETE_OP * @param exceptionType optional - if called method throws this exception, * join property state is still cleared. */ + // FIXME: support LOAD_OP? Just chuck all support for wrapped storables. It + // is no longer needed. private void callWrappedSupport(MethodInfo mi, String opType, boolean forTry, @@ -2083,7 +2163,7 @@ public final class StorableGenerator { return lookupMethod(type, mi.getName(), args); } - private static Method lookupMethod(Class type, String name, Class[] args) { + private static Method lookupMethod(Class type, String name, Class... args) { try { return type.getMethod(name, args); } catch (NoSuchMethodException e) { @@ -2393,7 +2473,7 @@ public final class StorableGenerator { private void addMarkCleanMethod(String name) { MethodInfo mi = - addMethodIfNotFinal (Modifiers.PUBLIC.toSynchronized(true), name, null, null); + addMethodIfNotFinal(Modifiers.PUBLIC.toSynchronized(true), name, null, null); if (mi == null) { return; @@ -3512,7 +3592,7 @@ public final class StorableGenerator { // trigger = support$.getXxxTrigger(); b.loadThis(); b.loadField(SUPPORT_FIELD_NAME, mSupportType); - Method m = lookupMethod(mSupportType.toClass(), "get" + opType + "Trigger", null); + Method m = lookupMethod(mSupportType.toClass(), "get" + opType + "Trigger"); b.invoke(m); b.storeLocal(triggerVar); // state = null; diff --git a/src/main/java/com/amazon/carbonado/gen/TriggerSupport.java b/src/main/java/com/amazon/carbonado/gen/TriggerSupport.java index 32b4e0b..d66239e 100644 --- a/src/main/java/com/amazon/carbonado/gen/TriggerSupport.java +++ b/src/main/java/com/amazon/carbonado/gen/TriggerSupport.java @@ -48,4 +48,30 @@ public interface TriggerSupport extends StorableSupport { * @return null if no trigger */ Trigger getDeleteTrigger(); + + /** + * Returns a trigger which must be run for all load and fetch operations. + * + * @return null if no trigger + * @since 1.2 + */ + Trigger getLoadTrigger(); + + /** + * Disables execution of load trigger for the current thread. Call + * localEnableLoadTrigger to enable again. This call can be made multiple + * times, but be sure to call localEnableLoadTrigger the same number of + * times to fully enable. + * + * @since 1.2 + */ + void locallyDisableLoadTrigger(); + + /** + * Enables execution of load trigger for the current thread, if they it + * been disabled before. + * + * @since 1.2 + */ + void locallyEnableLoadTrigger(); } diff --git a/src/main/java/com/amazon/carbonado/gen/WrappedSupport.java b/src/main/java/com/amazon/carbonado/gen/WrappedSupport.java index d4712e7..255bce2 100644 --- a/src/main/java/com/amazon/carbonado/gen/WrappedSupport.java +++ b/src/main/java/com/amazon/carbonado/gen/WrappedSupport.java @@ -27,6 +27,7 @@ import com.amazon.carbonado.Storable; * * @author Brian S O'Neill * @since 1.2 + * @deprecated */ public interface WrappedSupport extends TriggerSupport { /** diff --git a/src/main/java/com/amazon/carbonado/raw/RawStorableGenerator.java b/src/main/java/com/amazon/carbonado/raw/RawStorableGenerator.java index f475cab..0b0cfc7 100644 --- a/src/main/java/com/amazon/carbonado/raw/RawStorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/raw/RawStorableGenerator.java @@ -240,9 +240,10 @@ public class RawStorableGenerator { b.loadLocal(b.getParameter(2)); b.invokeVirtual(DECODE_DATA_METHOD_NAME, null, params); - // Indicate that object is clean by calling markAllPropertiesClean. + // Indicate load completed in order to mark properties as valid and + // invoke load triggers. b.loadThis(); - b.invokeVirtual(MARK_ALL_PROPERTIES_CLEAN, null, null); + b.invokeVirtual(StorableGenerator.LOAD_COMPLETED_METHOD_NAME, null, null); } else { // Only the primary key is clean. Calling // markPropertiesClean might have no effect since subclass diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java index 2f1c35c..c8f636c 100644 --- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java @@ -208,9 +208,10 @@ class JDBCStorableGenerator { b.invokePrivate(EXTRACT_ALL_METHOD_NAME, null, new TypeDesc[] {resultSetType, TypeDesc.INT}); - // Indicate that object is clean by calling markAllPropertiesClean. + // Indicate load completed in order to mark properties as valid and + // invoke load triggers. b.loadThis(); - b.invokeVirtual(MARK_ALL_PROPERTIES_CLEAN, null, null); + b.invokeVirtual(StorableGenerator.LOAD_COMPLETED_METHOD_NAME, null, null); b.returnVoid(); } diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorage.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorage.java index fd36c6e..19a6bab 100644 --- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorage.java +++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorage.java @@ -189,6 +189,18 @@ class JDBCStorage extends StandardQueryFactory return mTriggerManager.getDeleteTrigger(); } + public Trigger getLoadTrigger() { + return mTriggerManager.getLoadTrigger(); + } + + public void locallyDisableLoadTrigger() { + mTriggerManager.locallyDisableLoad(); + } + + public void locallyEnableLoadTrigger() { + mTriggerManager.locallyEnableLoad(); + } + /** * @param loader used to reload Blob outside original transaction */ 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 7edec6b..7b9804d 100644 --- a/src/main/java/com/amazon/carbonado/repo/replicated/ReplicationTrigger.java +++ b/src/main/java/com/amazon/carbonado/repo/replicated/ReplicationTrigger.java @@ -230,7 +230,7 @@ class ReplicationTrigger extends Trigger { Log log = LogFactory.getLog(ReplicatedRepository.class); - setReplicationDisabled(true); + setReplicationDisabled(); try { Transaction txn = mRepository.enterTransaction(); try { @@ -279,7 +279,7 @@ class ReplicationTrigger extends Trigger { txn.exit(); } } finally { - setReplicationDisabled(false); + setReplicationEnabled(); } } @@ -379,13 +379,13 @@ class ReplicationTrigger extends Trigger { * Deletes the replica entry with replication disabled. */ boolean tryDeleteReplica(Storable replica) throws PersistException { - // Disable replication to prevent trigger from being invoked by - // deleting replica. - setReplicationDisabled(true); + // Prevent trigger from being invoked by deleting replica. + TriggerManager tm = mTriggerManager; + tm.locallyDisableDelete(); try { return replica.tryDelete(); } finally { - setReplicationDisabled(false); + tm.locallyEnableDelete(); } } @@ -393,23 +393,29 @@ class ReplicationTrigger extends Trigger { * Deletes the replica entry with replication disabled. */ void deleteReplica(Storable replica) throws PersistException { - // Disable replication to prevent trigger from being invoked by - // deleting replica. - setReplicationDisabled(true); + // Prevent trigger from being invoked by deleting replica. + TriggerManager tm = mTriggerManager; + tm.locallyDisableDelete(); try { replica.delete(); } finally { - setReplicationDisabled(false); + tm.locallyEnableDelete(); } } - void setReplicationDisabled(boolean disabled) { + void setReplicationDisabled() { // This method disables not only this trigger, but all triggers added // to manager. - if (disabled) { - mTriggerManager.localDisable(); - } else { - mTriggerManager.localEnable(); - } + TriggerManager tm = mTriggerManager; + tm.locallyDisableInsert(); + tm.locallyDisableUpdate(); + tm.locallyDisableDelete(); + } + + void setReplicationEnabled() { + TriggerManager tm = mTriggerManager; + tm.locallyEnableInsert(); + tm.locallyEnableUpdate(); + tm.locallyEnableDelete(); } } diff --git a/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBStorage.java b/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBStorage.java index 03874aa..1f14c4f 100644 --- a/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBStorage.java +++ b/src/main/java/com/amazon/carbonado/repo/sleepycat/BDBStorage.java @@ -1122,5 +1122,17 @@ abstract class BDBStorage implements Storage, Storag public Trigger getDeleteTrigger() { return mStorage.mTriggerManager.getDeleteTrigger(); } + + public Trigger getLoadTrigger() { + return mStorage.mTriggerManager.getLoadTrigger(); + } + + public void locallyDisableLoadTrigger() { + mStorage.mTriggerManager.locallyDisableLoad(); + } + + public void locallyEnableLoadTrigger() { + mStorage.mTriggerManager.locallyEnableLoad(); + } } } diff --git a/src/main/java/com/amazon/carbonado/spi/TriggerManager.java b/src/main/java/com/amazon/carbonado/spi/TriggerManager.java index c4caf72..e7b653c 100644 --- a/src/main/java/com/amazon/carbonado/spi/TriggerManager.java +++ b/src/main/java/com/amazon/carbonado/spi/TriggerManager.java @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import com.amazon.carbonado.FetchException; import com.amazon.carbonado.PersistException; import com.amazon.carbonado.RepositoryException; import com.amazon.carbonado.Storable; @@ -47,7 +48,8 @@ public class TriggerManager extends Trigger { private static final int FOR_INSERT = 1; private static final int FOR_UPDATE = 2; private static final int FOR_DELETE = 4; - private static final int FOR_ADAPT_LOB = 8; + private static final int FOR_LOAD = 8; + private static final int FOR_ADAPT_LOB = 16; private static final Method BEFORE_INSERT_METHOD, @@ -68,6 +70,8 @@ public class TriggerManager extends Trigger { AFTER_TRY_DELETE_METHOD, FAILED_DELETE_METHOD, + AFTER_LOAD_METHOD, + ADAPT_BLOB_METHOD, ADAPT_CLOB_METHOD; @@ -95,6 +99,8 @@ public class TriggerManager extends Trigger { 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); + ADAPT_BLOB_METHOD = triggerClass .getMethod("adaptBlob", Object.class, String.class, Blob.class); ADAPT_CLOB_METHOD = triggerClass @@ -109,6 +115,7 @@ public class TriggerManager extends Trigger { private final ForInsert mForInsert = new ForInsert(); private final ForUpdate mForUpdate = new ForUpdate(); private final ForDelete mForDelete = new ForDelete(); + private final ForLoad mForLoad = new ForLoad(); private final ForAdaptLob mForAdaptLob = new ForAdaptLob(); public TriggerManager() { @@ -156,6 +163,18 @@ public class TriggerManager extends Trigger { return forDelete.isEmpty() ? null : forDelete; } + /** + * Returns a consolidated trigger to call for load operations, or null if + * none. If not null, the consolidated trigger is not a snapshot -- it will + * change as the set of triggers in this manager changes. + * + * @since 1.2 + */ + public Trigger getLoadTrigger() { + ForLoad forLoad = mForLoad; + return forLoad.isEmpty() ? null : forLoad; + } + /** * Returns a consolidated trigger to call for adapt LOB operations, or null * if none. If not null, the consolidated trigger is not a snapshot -- it @@ -186,6 +205,9 @@ public class TriggerManager extends Trigger { if ((types & FOR_DELETE) != 0) { retValue |= mForDelete.add(trigger); } + if ((types & FOR_LOAD) != 0) { + retValue |= mForLoad.add(trigger); + } if ((types & FOR_ADAPT_LOB) != 0) { retValue |= mForAdaptLob.add(trigger); } @@ -211,6 +233,9 @@ public class TriggerManager extends Trigger { if ((types & FOR_DELETE) != 0) { retValue |= mForDelete.remove(trigger); } + if ((types & FOR_LOAD) != 0) { + retValue |= mForLoad.remove(trigger); + } if ((types & FOR_ADAPT_LOB) != 0) { retValue |= mForAdaptLob.remove(trigger); } @@ -230,26 +255,91 @@ public class TriggerManager extends Trigger { } /** - * Disables execution of all managed triggers for the current thread. Call - * localEnable to enable again. This call can be made multiple times, but - * be sure to call localEnable the same number of times to fully enable. + * Disables execution of all managed insert triggers for the current + * thread. Call locallyEnableInsert to enable again. This call can be made + * multiple times, but be sure to call locallyEnableInsert the same number of + * times to fully enable. + * + * @since 1.2 + */ + public void locallyDisableInsert() { + mForInsert.locallyDisable(); + } + + /** + * Enables execution of all managed insert triggers for the current thread, + * if they had been disabled before. + * + * @since 1.2 + */ + public void locallyEnableInsert() { + mForInsert.locallyEnable(); + } + + /** + * Disables execution of all managed update triggers for the current + * thread. Call locallyEnableUpdate to enable again. This call can be made + * multiple times, but be sure to call locallyEnableUpdate the same number of + * times to fully enable. + * + * @since 1.2 */ - public void localDisable() { - mForInsert.localDisable(); - mForUpdate.localDisable(); - mForDelete.localDisable(); - mForAdaptLob.localDisable(); + public void locallyDisableUpdate() { + mForUpdate.locallyDisable(); } /** - * Enables execution of all managed triggers for the current thread, if - * they had been disabled before. + * Enables execution of all managed update triggers for the current thread, + * if they had been disabled before. + * + * @since 1.2 */ - public void localEnable() { - mForInsert.localEnable(); - mForUpdate.localEnable(); - mForDelete.localEnable(); - mForAdaptLob.localEnable(); + public void locallyEnableUpdate() { + mForUpdate.locallyEnable(); + } + + /** + * Disables execution of all managed delete triggers for the current + * thread. Call locallyEnableDelete to enable again. This call can be made + * multiple times, but be sure to call locallyEnableDelete the same number of + * times to fully enable. + * + * @since 1.2 + */ + public void locallyDisableDelete() { + mForDelete.locallyDisable(); + } + + /** + * Enables execution of all managed delete triggers for the current thread, + * if they had been disabled before. + * + * @since 1.2 + */ + public void locallyEnableDelete() { + mForDelete.locallyEnable(); + } + + /** + * Disables execution of all managed load triggers for the current + * thread. Call locallyEnableLoad to enable again. This call can be made + * multiple times, but be sure to call locallyEnableLoad the same number of + * times to fully enable. + * + * @since 1.2 + */ + public void locallyDisableLoad() { + mForLoad.locallyDisable(); + } + + /** + * Enables execution of all managed load triggers for the current thread, + * if they had been disabled before. + * + * @since 1.2 + */ + public void locallyEnableLoad() { + mForLoad.locallyEnable(); } @Override @@ -327,6 +417,11 @@ public class TriggerManager extends Trigger { mForDelete.failedDelete(storable, state); } + @Override + public void afterLoad(S storable) throws FetchException { + mForLoad.afterLoad(storable); + } + @Override public Blob adaptBlob(S storable, String name, Blob blob) { return mForAdaptLob.adaptBlob(storable, name, blob); @@ -372,6 +467,10 @@ public class TriggerManager extends Trigger { types |= FOR_DELETE; } + if (overridesMethod(triggerClass, AFTER_LOAD_METHOD)) { + types |= FOR_LOAD; + } + if (overridesMethod(triggerClass, ADAPT_BLOB_METHOD) || overridesMethod(triggerClass, ADAPT_CLOB_METHOD)) { @@ -400,10 +499,10 @@ public class TriggerManager extends Trigger { } } - private static abstract class ForSomething extends Trigger { - private static final AtomicReferenceFieldUpdater + private static abstract class ManagedTrigger extends Trigger { + private static final AtomicReferenceFieldUpdater cDisabledFlagRef = AtomicReferenceFieldUpdater - .newUpdater(ForSomething.class, ThreadLocal.class, "mDisabledFlag"); + .newUpdater(ManagedTrigger.class, ThreadLocal.class, "mDisabledFlag"); private static Trigger[] NO_TRIGGERS = new Trigger[0]; @@ -411,7 +510,7 @@ public class TriggerManager extends Trigger { private volatile ThreadLocal mDisabledFlag; - ForSomething() { + ManagedTrigger() { mTriggers = NO_TRIGGERS; } @@ -450,7 +549,7 @@ public class TriggerManager extends Trigger { return i != null && i.get() > 0; } - void localDisable() { + void locallyDisable() { // Using a count allows this method call to be nested. ThreadLocal disabledFlag = disabledFlag(); AtomicInteger i = disabledFlag.get(); @@ -461,7 +560,7 @@ public class TriggerManager extends Trigger { } } - void localEnable() { + void locallyEnable() { // Using a count allows this method call to be nested. AtomicInteger i = disabledFlag().get(); if (i != null) { @@ -482,7 +581,7 @@ public class TriggerManager extends Trigger { } } - private static class ForInsert extends ForSomething { + private static class ForInsert extends ManagedTrigger { @Override public Object beforeInsert(S storable) throws PersistException { if (isLocallyDisabled()) { @@ -637,7 +736,7 @@ public class TriggerManager extends Trigger { } } - private static class ForUpdate extends ForSomething { + private static class ForUpdate extends ManagedTrigger { @Override public Object beforeUpdate(S storable) throws PersistException { if (isLocallyDisabled()) { @@ -792,7 +891,7 @@ public class TriggerManager extends Trigger { } } - private static class ForDelete extends ForSomething { + private static class ForDelete extends ManagedTrigger { @Override public Object beforeDelete(S storable) throws PersistException { if (isLocallyDisabled()) { @@ -947,7 +1046,19 @@ public class TriggerManager extends Trigger { } } - private static class ForAdaptLob extends ForSomething { + private static class ForLoad extends ManagedTrigger { + @Override + public void afterLoad(S storable) throws FetchException { + if (!isLocallyDisabled()) { + Trigger[] triggers = mTriggers; + for (int i=triggers.length; --i>=0; ) { + triggers[i].afterLoad(storable); + } + } + } + } + + private static class ForAdaptLob extends ManagedTrigger { @Override public Blob adaptBlob(S storable, String name, Blob blob) { if (isLocallyDisabled()) { diff --git a/src/main/java/com/amazon/carbonado/spi/WrappedStorage.java b/src/main/java/com/amazon/carbonado/spi/WrappedStorage.java index 54db355..cdae0e3 100644 --- a/src/main/java/com/amazon/carbonado/spi/WrappedStorage.java +++ b/src/main/java/com/amazon/carbonado/spi/WrappedStorage.java @@ -102,6 +102,7 @@ public abstract class WrappedStorage implements Storage { * * @param storable storable being wrapped * @see #createSupport + * @deprecated */ protected S wrap(S storable) { if (storable == null) { @@ -125,6 +126,7 @@ public abstract class WrappedStorage implements Storage { * Create a handler used by wrapped storables. * * @param storable storable being wrapped + * @deprecated */ protected abstract Support createSupport(S storable); @@ -150,6 +152,18 @@ public abstract class WrappedStorage implements Storage { public Trigger getDeleteTrigger() { return mTriggerManager.getDeleteTrigger(); } + + public Trigger getLoadTrigger() { + return mTriggerManager.getLoadTrigger(); + } + + public void locallyDisableLoadTrigger() { + mTriggerManager.locallyDisableLoad(); + } + + public void locallyEnableLoadTrigger() { + mTriggerManager.locallyEnableLoad(); + } } /** -- cgit v1.2.3