Skip to content

Commit

Permalink
version 2.1.14: added callback to adjust values before commit to mod-…
Browse files Browse the repository at this point in the history
…listener
  • Loading branch information
JanWiemer committed Jan 14, 2025
1 parent a8b7abb commit 99df035
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 25 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
############################
# JACIS VERSION:
############################
version=2.1.13
version=2.1.14
#=format for release: 2.1.25
#=format for snapshot: 2.0.26-2023-01-01 (on the way to 2.0.26)
#
Expand Down
22 changes: 18 additions & 4 deletions src/main/java/org/jacis/plugin/JacisModificationListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,27 @@
@JacisApi
public interface JacisModificationListener<K, V> {

/**
* Callback method called before the prepare phase of a transaction for value modified during the transaction.
* Implementations of this method may do further modifications on the passes value to commit, based on the tracked modifications.
* This is done before the prepare phase of the transaction is actually started for the store.
*
* @param key The key of the modified object
* @param oldValue The original value of the modified object when it was copied to the transactional view (read only).
* @param valueToCommit The new modified value that was updated during the transaction and may be adjusted in this method (writable).
* @param tx The transaction that is currently committed.
*/
default void onAdjustBeforePrepare(K key, V oldValue, V valueToCommit, JacisTransactionHandle tx) {
// default implementation empty
}

/**
* Callback method called during the prepare phase of a transaction for each modified value.
* Note that implementing methods can throw an {@link JacisModificationVetoException} to deny the modification and rollback the transaction.
*
* @param key The key of the modified object
* @param oldValue The original value of the modified object when it was copied to the transactional view.
* @param newValue The new modified value that is written back to the committed values.
* @param oldValue The original value of the modified object when it was copied to the transactional view (read only).
* @param newValue The new modified value that is written back to the committed values (read only).
* @param tx The transaction that is currently committed.
* @throws JacisModificationVetoException to deny the modification and rollback the transaction
*/
Expand All @@ -48,8 +62,8 @@ default void onPrepareModification(K key, V oldValue, V newValue, JacisTransacti
* Note that implementing methods should not throw an exception since the original transaction could be broken by this.
*
* @param key The key of the modified object
* @param oldValue The original value of the modified object when it was copied to the transactional view.
* @param newValue The new modified value that is written back to the committed values.
* @param oldValue The original value of the modified object when it was copied to the transactional view (read only).
* @param newValue The new modified value that is written back to the committed values (read only).
* @param tx The transaction that is currently committed.
*/
void onModification(K key, V oldValue, V newValue, JacisTransactionHandle tx);
Expand Down
8 changes: 6 additions & 2 deletions src/main/java/org/jacis/store/StoreEntryTxView.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import org.jacis.exception.JacisStaleObjectException;
import org.jacis.plugin.objectadapter.JacisObjectAdapter;
import org.jacis.plugin.readonly.object.JacisReadonlyModeSupport;

import java.util.Objects;

Expand All @@ -17,8 +18,8 @@
* @param <CV> Type of the objects as they are stored in the internal map of committed values. This type is not visible from the outside.
* @author Jan Wiemer
*/
class StoreEntryTxView<K, TV, CV> implements Comparable<StoreEntryTxView<K, TV, CV>>{
class StoreEntryTxView<K, TV, CV> implements Comparable<StoreEntryTxView<K, TV, CV>> {

/** link to the committed entry (note this is the real committed instance that might be changed by other TXs) */
private final StoreEntry<K, TV, CV> committedEntry;
/** current value of the entry in this TX */
Expand All @@ -40,6 +41,9 @@ class StoreEntryTxView<K, TV, CV> implements Comparable<StoreEntryTxView<K, TV,
this.origVersion = committedEntry.getVersion();
if (trackOriginal) {
origValue = ca.cloneCommitted2WritableTxView(committedEntry.getValue());
if (origValue instanceof JacisReadonlyModeSupport) {
((JacisReadonlyModeSupport) origValue).switchToReadOnlyMode();
}
} else {
origValue = null;
}
Expand Down
16 changes: 14 additions & 2 deletions src/main/java/org/jacis/store/StoreTxDemarcationExecutor.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,16 @@ <K, TV, CV> void executePrepare(JacisStoreImpl<K, TV, CV> store, JacisTransactio
}
}
for (StoreEntryTxView<K, TV, CV> entryTxView : txView.getUpdatedEntriesForCommit()) {
StoreEntry<K, TV, CV> entryCommitted = entryTxView.getCommittedEntry();
K key = entryTxView.getKey();
if (entryTxView.getValue() != null) {
trackBeforePrepareModification(store, key, entryTxView.getOrigValue(), entryTxView.getValue(), txView.getTransaction());
}
StoreEntry<K, TV, CV> entryCommitted = entryTxView.getCommittedEntry();
entryTxView.assertNotStale(txView);
entryCommitted.lockedFor(txView);
if (entryTxView.getValue() != null && store.getObjectTypeSpec().isSwitchToReadOnlyModeInPrepare()) {
if (entryTxView.getValue() != null //
&& entryTxView.getValue() instanceof JacisReadonlyModeSupport //
&& store.getObjectTypeSpec().isSwitchToReadOnlyModeInPrepare()) {
((JacisReadonlyModeSupport) entryTxView.getValue()).switchToReadOnlyMode();
}
trackPrepareModification(store, key, entryTxView.getOrigValue(), entryTxView.getValue(), txView.getTransaction());
Expand Down Expand Up @@ -247,6 +252,13 @@ <K, TV, CV> void executeDestroy(JacisStoreImpl<K, TV, CV> store, JacisTransactio
}
}

private <K, TV, CV> void trackBeforePrepareModification(JacisStoreImpl<K, TV, CV> store, K key, TV oldValue, TV newValue, JacisTransactionHandle tx) {
assert store.getObjectTypeSpec().isTrackOriginalValueEnabled() : "Tracking prepared modification is only possible if original value is tracked";
for (JacisModificationListener<K, TV> listener : store.getModificationListeners()) {
listener.onAdjustBeforePrepare(key, oldValue, newValue, tx);
}
}

private <K, TV, CV> void trackPrepareModification(JacisStoreImpl<K, TV, CV> store, K key, TV oldValue, TV newValue, JacisTransactionHandle tx) {
assert store.getObjectTypeSpec().isTrackOriginalValueEnabled() : "Tracking prepared modification is only possible if original value is tracked";
for (JacisModificationListener<K, TV> listener : store.getModificationListeners()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

package org.jacis.modificationlistener;

import static org.junit.Assert.assertEquals;

import org.jacis.container.JacisTransactionHandle;
import org.jacis.exception.JacisModificationVetoException;
import org.jacis.plugin.JacisModificationListener;
Expand All @@ -14,44 +12,66 @@
import org.jacis.testhelper.TestObject;
import org.junit.Test;

import static org.junit.Assert.*;

@SuppressWarnings("CodeBlock2Expr")
public class JacisModificationListenerTest {

static class TestModificationListener implements JacisModificationListener<String,TestObject> {
static class TestModificationListener implements JacisModificationListener<String, TestObject> {
@Override
public void onAdjustBeforePrepare(String key, TestObject oldValue, TestObject valueToCommit, JacisTransactionHandle tx) {
if (oldValue != null) {
assertTrue(oldValue.isReadOnly());
}
assertFalse(valueToCommit.isReadOnly());
if (valueToCommit != null) {
if ("A".equals(valueToCommit.getStrValue())) {
valueToCommit.setValue(oldValue.getValue() + 1);
}
if (oldValue != null && "A".equals(oldValue.getStrValue())) {
valueToCommit.setValue(oldValue.getValue() - 1);
}
}
}

@Override
public void onPrepareModification(String key, TestObject oldValue, TestObject newValue, JacisTransactionHandle tx) throws JacisModificationVetoException {
if(newValue!=null) {
if( "A".equals(newValue.getStrValue())) {
newValue.setValue(oldValue.getValue()+1);
}
if(oldValue!=null && "A".equals(oldValue.getStrValue())) {
newValue.setValue(oldValue.getValue()-1);
}
if (oldValue != null) {
assertTrue(oldValue.isReadOnly());
}
if (newValue != null) {
assertTrue(newValue.isReadOnly());
}
}

@Override
public void onModification(String key, TestObject oldValue, TestObject newValue, JacisTransactionHandle tx) {
if (oldValue != null) {
assertTrue(oldValue.isReadOnly());
}
if (newValue != null) {
assertTrue(newValue.isReadOnly());
}
// do nothing
}
}
}

@Test
public void testGetCommittedVersion() {
String objName = "OBJ";
JacisTestHelper testHelper = new JacisTestHelper();
JacisStore<String, TestObject> store = testHelper.createTestStoreWithCloning();
store.registerModificationListener(new TestModificationListener());
store.getContainer().withLocalTx(()->store.update(objName, new TestObject(objName, 0)));
store.getContainer().withLocalTx(() -> store.update(objName, new TestObject(objName, 0)));
assertEquals(0, store.getCommittedValue(objName).getValue());
// --------------------
store.getContainer().withLocalTx(()->store.update(objName, new TestObject(objName, 0).setStrValue("A")));
store.getContainer().withLocalTx(() -> store.update(objName, new TestObject(objName, 0).setStrValue("A")));
assertEquals(1, store.getCommittedValue(objName).getValue());
// --------------------
store.getContainer().withLocalTx(()->store.update(objName, new TestObject(objName, 0).setStrValue("B")));
store.getContainer().withLocalTx(() -> store.update(objName, new TestObject(objName, 0).setStrValue("B")));
assertEquals(0, store.getCommittedValue(objName).getValue());
// --------------------
}


}
1 change: 1 addition & 0 deletions src/test/java/org/jacis/testhelper/JacisTestHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public JacisStore<String, TestObject> createTestStoreWithCloning() {
public JacisStore<String, TestObject> createTestStoreWithCloning(JacisContainer container) {
JacisCloningObjectAdapter<TestObject> cloningAdapter = new JacisCloningObjectAdapter<>();
JacisObjectTypeSpec<String, TestObject, TestObject> objectTypeSpec = new JacisObjectTypeSpec<>(String.class, TestObject.class, cloningAdapter);
objectTypeSpec.setSwitchToReadOnlyModeInPrepare(true);
container.createStore(objectTypeSpec);
return container.getStore(String.class, TestObject.class);
}
Expand Down

0 comments on commit 99df035

Please sign in to comment.