diff options
Diffstat (limited to 'src/main/java/com')
12 files changed, 335 insertions, 56 deletions
| 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 @@ -287,6 +287,16 @@ public abstract class Trigger<S> {      }
      /**
 +     * 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
       * unmodified.
 @@ -296,6 +306,7 @@ public abstract class Trigger<S> {       * @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<S> {       * @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 @@ -131,6 +131,14 @@ public final class StorableGenerator<S extends Storable> {      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
       * 32-bit field holds states for up to 16 properties.
 @@ -255,8 +263,8 @@ public final class StorableGenerator<S extends Storable> {       *
       * <p>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.
       *
       * <pre>
       * // Returns true if all primary key properties have been set.
 @@ -269,6 +277,11 @@ public final class StorableGenerator<S extends Storable> {       * // 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;
       * </pre>
       *
       * Property state field names are defined by the concatenation of
 @@ -357,6 +370,7 @@ public final class StorableGenerator<S extends Storable> {       *
       * @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 <S extends Storable> Class<? extends S> getWrappedClass(Class<S> type)
 @@ -480,6 +494,8 @@ public final class StorableGenerator<S extends Storable> {              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<S extends Storable> {                  // 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<S extends Storable> {                                    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<S extends Storable> {              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<S extends Storable> {                  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<S extends Storable> {          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<S extends Storable> {       * 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<S extends Storable> {          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<S extends Storable> {      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<S extends Storable> {          // 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<S extends Storable> extends StorableSupport<S> {       * @return null if no trigger
       */
      Trigger<? super S> getDeleteTrigger();
 +
 +    /**
 +     * Returns a trigger which must be run for all load and fetch operations.
 +     *
 +     * @return null if no trigger
 +     * @since 1.2
 +     */
 +    Trigger<? super S> 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<S extends Storable> extends TriggerSupport<S> {
      /**
 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<S extends Storable> {              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<S extends Storable> extends StandardQueryFactory<S>          return mTriggerManager.getDeleteTrigger();
      }
 +    public Trigger<? super S> 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<S extends Storable> extends Trigger<S> {          Log log = LogFactory.getLog(ReplicatedRepository.class);
 -        setReplicationDisabled(true);
 +        setReplicationDisabled();
          try {
              Transaction txn = mRepository.enterTransaction();
              try {
 @@ -279,7 +279,7 @@ class ReplicationTrigger<S extends Storable> extends Trigger<S> {                  txn.exit();
              }
          } finally {
 -            setReplicationDisabled(false);
 +            setReplicationEnabled();
          }
      }
 @@ -379,13 +379,13 @@ class ReplicationTrigger<S extends Storable> extends Trigger<S> {       * 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<S extends Storable> extends Trigger<S> {       * 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<Txn, S extends Storable> implements Storage<S>, Storag          public Trigger<? super S> getDeleteTrigger() {
              return mStorage.mTriggerManager.getDeleteTrigger();
          }
 +
 +        public Trigger<? super S> 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<S extends Storable> extends Trigger<S> {      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<S extends Storable> extends Trigger<S> {          AFTER_TRY_DELETE_METHOD,
          FAILED_DELETE_METHOD,
 +        AFTER_LOAD_METHOD,
 +
          ADAPT_BLOB_METHOD,
          ADAPT_CLOB_METHOD;
 @@ -95,6 +99,8 @@ public class TriggerManager<S extends Storable> extends Trigger<S> {              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<S extends Storable> extends Trigger<S> {      private final ForInsert<S> mForInsert = new ForInsert<S>();
      private final ForUpdate<S> mForUpdate = new ForUpdate<S>();
      private final ForDelete<S> mForDelete = new ForDelete<S>();
 +    private final ForLoad<S> mForLoad = new ForLoad<S>();
      private final ForAdaptLob<S> mForAdaptLob = new ForAdaptLob<S>();
      public TriggerManager() {
 @@ -157,6 +164,18 @@ public class TriggerManager<S extends Storable> extends Trigger<S> {      }
      /**
 +     * 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<? super S> getLoadTrigger() {
 +        ForLoad<S> 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
       * will change as the set of triggers in this manager changes.
 @@ -186,6 +205,9 @@ public class TriggerManager<S extends Storable> extends Trigger<S> {          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<S extends Storable> extends Trigger<S> {          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<S extends Storable> extends Trigger<S> {      }
      /**
 -     * 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
 @@ -328,6 +418,11 @@ public class TriggerManager<S extends Storable> extends Trigger<S> {      }
      @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<S extends Storable> extends Trigger<S> {              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<S extends Storable> extends Trigger<S> {          }
      }
 -    private static abstract class ForSomething<S> extends Trigger<S> {
 -        private static final AtomicReferenceFieldUpdater<ForSomething, ThreadLocal>
 +    private static abstract class ManagedTrigger<S> extends Trigger<S> {
 +        private static final AtomicReferenceFieldUpdater<ManagedTrigger, ThreadLocal>
              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<S extends Storable> extends Trigger<S> {          private volatile ThreadLocal<AtomicInteger> mDisabledFlag;
 -        ForSomething() {
 +        ManagedTrigger() {
              mTriggers = NO_TRIGGERS;
          }
 @@ -450,7 +549,7 @@ public class TriggerManager<S extends Storable> extends Trigger<S> {              return i != null && i.get() > 0;
          }
 -        void localDisable() {
 +        void locallyDisable() {
              // Using a count allows this method call to be nested.
              ThreadLocal<AtomicInteger> disabledFlag = disabledFlag();
              AtomicInteger i = disabledFlag.get();
 @@ -461,7 +560,7 @@ public class TriggerManager<S extends Storable> extends Trigger<S> {              }
          }
 -        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<S extends Storable> extends Trigger<S> {          }
      }
 -    private static class ForInsert<S> extends ForSomething<S> {
 +    private static class ForInsert<S> extends ManagedTrigger<S> {
          @Override
          public Object beforeInsert(S storable) throws PersistException {
              if (isLocallyDisabled()) {
 @@ -637,7 +736,7 @@ public class TriggerManager<S extends Storable> extends Trigger<S> {          }
      }
 -    private static class ForUpdate<S> extends ForSomething<S> {
 +    private static class ForUpdate<S> extends ManagedTrigger<S> {
          @Override
          public Object beforeUpdate(S storable) throws PersistException {
              if (isLocallyDisabled()) {
 @@ -792,7 +891,7 @@ public class TriggerManager<S extends Storable> extends Trigger<S> {          }
      }
 -    private static class ForDelete<S> extends ForSomething<S> {
 +    private static class ForDelete<S> extends ManagedTrigger<S> {
          @Override
          public Object beforeDelete(S storable) throws PersistException {
              if (isLocallyDisabled()) {
 @@ -947,7 +1046,19 @@ public class TriggerManager<S extends Storable> extends Trigger<S> {          }
      }
 -    private static class ForAdaptLob<S> extends ForSomething<S> {
 +    private static class ForLoad<S> extends ManagedTrigger<S> {
 +        @Override
 +        public void afterLoad(S storable) throws FetchException {
 +            if (!isLocallyDisabled()) {
 +                Trigger<? super S>[] triggers = mTriggers;
 +                for (int i=triggers.length; --i>=0; ) {
 +                    triggers[i].afterLoad(storable);
 +                }
 +            }
 +        }
 +    }
 +
 +    private static class ForAdaptLob<S> extends ManagedTrigger<S> {
          @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<S extends Storable> implements Storage<S> {       *
       * @param storable storable being wrapped
       * @see #createSupport
 +     * @deprecated
       */
      protected S wrap(S storable) {
          if (storable == null) {
 @@ -125,6 +126,7 @@ public abstract class WrappedStorage<S extends Storable> implements Storage<S> {       * 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<S extends Storable> implements Storage<S> {          public Trigger<? super S> getDeleteTrigger() {
              return mTriggerManager.getDeleteTrigger();
          }
 +
 +        public Trigger<? super S> getLoadTrigger() {
 +            return mTriggerManager.getLoadTrigger();
 +        }
 +
 +        public void locallyDisableLoadTrigger() {
 +            mTriggerManager.locallyDisableLoad();
 +        }
 +
 +        public void locallyEnableLoadTrigger() {
 +            mTriggerManager.locallyEnableLoad();
 +        }
      }
      /**
 | 
