summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian S. O'Neill <bronee@gmail.com>2007-08-26 01:11:29 +0000
committerBrian S. O'Neill <bronee@gmail.com>2007-08-26 01:11:29 +0000
commit00adea5bc6916b717132c72768ab3fea1b2a1a22 (patch)
treeba923df8ea6fcd0ebb17557cea16c355274a43ce
parent3c0acb2e614373452107aa54066d3398a54f338e (diff)
Added support for load and query trigger.
-rw-r--r--RELEASE-NOTES.txt1
-rw-r--r--src/main/java/com/amazon/carbonado/Trigger.java12
-rw-r--r--src/main/java/com/amazon/carbonado/gen/CommonMethodNames.java5
-rw-r--r--src/main/java/com/amazon/carbonado/gen/StorableGenerator.java98
-rw-r--r--src/main/java/com/amazon/carbonado/gen/TriggerSupport.java26
-rw-r--r--src/main/java/com/amazon/carbonado/gen/WrappedSupport.java1
-rw-r--r--src/main/java/com/amazon/carbonado/raw/RawStorableGenerator.java5
-rw-r--r--src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java5
-rw-r--r--src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorage.java12
-rw-r--r--src/main/java/com/amazon/carbonado/repo/replicated/ReplicationTrigger.java38
-rw-r--r--src/main/java/com/amazon/carbonado/repo/sleepycat/BDBStorage.java12
-rw-r--r--src/main/java/com/amazon/carbonado/spi/TriggerManager.java163
-rw-r--r--src/main/java/com/amazon/carbonado/spi/WrappedStorage.java14
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
@@ -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();
+ }
}
/**