mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Allow read methods against transactional caches during the afterCommit or afterRollback phase.
Writes during these phases are still not allowed. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@6680 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -33,6 +33,9 @@ import javax.transaction.UserTransaction;
|
|||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import net.sf.ehcache.CacheManager;
|
import net.sf.ehcache.CacheManager;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||||
|
import org.alfresco.repo.transaction.TransactionListenerAdapter;
|
||||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||||
import org.alfresco.service.ServiceRegistry;
|
import org.alfresco.service.ServiceRegistry;
|
||||||
import org.alfresco.service.transaction.TransactionService;
|
import org.alfresco.service.transaction.TransactionService;
|
||||||
@@ -168,13 +171,27 @@ public class CacheTest extends TestCase
|
|||||||
assertFalse("Transactionally removed item found in keys", transactionalKeys.contains(newGlobalOne));
|
assertFalse("Transactionally removed item found in keys", transactionalKeys.contains(newGlobalOne));
|
||||||
assertTrue("Transactionally added item not found in keys", transactionalKeys.contains(updatedTxnThree));
|
assertTrue("Transactionally added item not found in keys", transactionalKeys.contains(updatedTxnThree));
|
||||||
|
|
||||||
|
// Register a post-commit stresser. We do this here so that it is registered after the transactional cache
|
||||||
|
PostCommitCacheUser listener = new PostCommitCacheUser(transactionalCache, updatedTxnThree);
|
||||||
|
AlfrescoTransactionSupport.bindListener(listener);
|
||||||
|
|
||||||
// commit the transaction
|
// commit the transaction
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
|
||||||
|
// Check the post-commit stresser
|
||||||
|
if (listener.e != null)
|
||||||
|
{
|
||||||
|
throw listener.e;
|
||||||
|
}
|
||||||
|
|
||||||
// check that backing cache was updated with the in-transaction changes
|
// check that backing cache was updated with the in-transaction changes
|
||||||
assertFalse("Item was not removed from backing cache", backingCache.contains(newGlobalOne));
|
assertFalse("Item was not removed from backing cache", backingCache.contains(newGlobalOne));
|
||||||
assertNull("Item could still be fetched from backing cache", backingCache.get(newGlobalOne));
|
assertNull("Item could still be fetched from backing cache", backingCache.get(newGlobalOne));
|
||||||
assertEquals("Item not updated in backing cache", "XXX", backingCache.get(updatedTxnThree));
|
assertEquals("Item not updated in backing cache", "XXX", backingCache.get(updatedTxnThree));
|
||||||
|
|
||||||
|
// Check that the transactional cache serves get requests
|
||||||
|
assertEquals("Transactional cache must serve post-commit get requests", "XXX",
|
||||||
|
transactionalCache.get(updatedTxnThree));
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
@@ -186,6 +203,53 @@ public class CacheTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This transaction listener attempts to use the cache in the afterCommit phase. Technically the
|
||||||
|
* transaction has finished, but the transaction resources are still available.
|
||||||
|
*
|
||||||
|
* @author Derek Hulley
|
||||||
|
* @since 2.1
|
||||||
|
*/
|
||||||
|
private class PostCommitCacheUser extends TransactionListenerAdapter
|
||||||
|
{
|
||||||
|
private final SimpleCache<String, Object> transactionalCache;
|
||||||
|
private final String key;
|
||||||
|
private Throwable e;
|
||||||
|
private PostCommitCacheUser(SimpleCache<String, Object> transactionalCache, String key)
|
||||||
|
{
|
||||||
|
this.transactionalCache = transactionalCache;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void afterCommit()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
transactionalCache.get(key);
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
this.e = e;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
transactionalCache.put(key, "ZZZ");
|
||||||
|
e = new RuntimeException("Transactional caches should not allow puts in the after-commit phase");
|
||||||
|
}
|
||||||
|
catch (AlfrescoRuntimeException e)
|
||||||
|
{
|
||||||
|
// Expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return -100000;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preloads the cache, then performs a simultaneous addition of N new values and
|
* Preloads the cache, then performs a simultaneous addition of N new values and
|
||||||
* removal of the N preloaded values.
|
* removal of the N preloaded values.
|
||||||
|
@@ -291,6 +291,13 @@ public class TransactionalCache<K extends Serializable, V extends Object>
|
|||||||
if (AlfrescoTransactionSupport.getTransactionId() != null)
|
if (AlfrescoTransactionSupport.getTransactionId() != null)
|
||||||
{
|
{
|
||||||
TransactionData txnData = getTransactionData();
|
TransactionData txnData = getTransactionData();
|
||||||
|
if (txnData.isClosed)
|
||||||
|
{
|
||||||
|
// This check could have been done in the first if block, but that would have added another call to the
|
||||||
|
// txn resources.
|
||||||
|
}
|
||||||
|
else // The txn is still active
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!txnData.isClearOn) // deletions cache only useful before a clear
|
if (!txnData.isClearOn) // deletions cache only useful before a clear
|
||||||
@@ -333,6 +340,7 @@ public class TransactionalCache<K extends Serializable, V extends Object>
|
|||||||
// check if the cleared flag has been set - cleared flag means ignore shared as unreliable
|
// check if the cleared flag has been set - cleared flag means ignore shared as unreliable
|
||||||
ignoreSharedCache = txnData.isClearOn;
|
ignoreSharedCache = txnData.isClearOn;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// no value found - must we ignore the shared cache?
|
// no value found - must we ignore the shared cache?
|
||||||
if (!ignoreSharedCache)
|
if (!ignoreSharedCache)
|
||||||
{
|
{
|
||||||
@@ -384,6 +392,11 @@ public class TransactionalCache<K extends Serializable, V extends Object>
|
|||||||
else // transaction present
|
else // transaction present
|
||||||
{
|
{
|
||||||
TransactionData txnData = getTransactionData();
|
TransactionData txnData = getTransactionData();
|
||||||
|
// Ensure that the cache isn't being modified
|
||||||
|
if (txnData.isClosed)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("onCommit cache modifications are not allowed.");
|
||||||
|
}
|
||||||
// we have a transaction - add the item into the updated cache for this transaction
|
// we have a transaction - add the item into the updated cache for this transaction
|
||||||
// are we in an overflow condition?
|
// are we in an overflow condition?
|
||||||
if (txnData.updatedItemsCache.getMemoryStoreSize() >= maxCacheSize)
|
if (txnData.updatedItemsCache.getMemoryStoreSize() >= maxCacheSize)
|
||||||
@@ -444,6 +457,11 @@ public class TransactionalCache<K extends Serializable, V extends Object>
|
|||||||
else // transaction present
|
else // transaction present
|
||||||
{
|
{
|
||||||
TransactionData txnData = getTransactionData();
|
TransactionData txnData = getTransactionData();
|
||||||
|
// Ensure that the cache isn't being modified
|
||||||
|
if (txnData.isClosed)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("onCommit cache modifications are not allowed.");
|
||||||
|
}
|
||||||
// is the shared cache going to be cleared?
|
// is the shared cache going to be cleared?
|
||||||
if (txnData.isClearOn)
|
if (txnData.isClearOn)
|
||||||
{
|
{
|
||||||
@@ -509,6 +527,11 @@ public class TransactionalCache<K extends Serializable, V extends Object>
|
|||||||
}
|
}
|
||||||
|
|
||||||
TransactionData txnData = getTransactionData();
|
TransactionData txnData = getTransactionData();
|
||||||
|
// Ensure that the cache isn't being modified
|
||||||
|
if (txnData.isClosed)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("onCommit cache modifications are not allowed.");
|
||||||
|
}
|
||||||
// the shared cache must be cleared at the end of the transaction
|
// the shared cache must be cleared at the end of the transaction
|
||||||
// and also serves to ensure that the shared cache will be ignored
|
// and also serves to ensure that the shared cache will be ignored
|
||||||
// for the remainder of the transaction
|
// for the remainder of the transaction
|
||||||
@@ -630,6 +653,7 @@ public class TransactionalCache<K extends Serializable, V extends Object>
|
|||||||
{
|
{
|
||||||
cacheManager.removeCache(txnData.updatedItemsCache.getName());
|
cacheManager.removeCache(txnData.updatedItemsCache.getName());
|
||||||
cacheManager.removeCache(txnData.removedItemsCache.getName());
|
cacheManager.removeCache(txnData.removedItemsCache.getName());
|
||||||
|
txnData.isClosed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -760,8 +784,9 @@ public class TransactionalCache<K extends Serializable, V extends Object>
|
|||||||
/** Data holder to bind data to the transaction */
|
/** Data holder to bind data to the transaction */
|
||||||
private class TransactionData
|
private class TransactionData
|
||||||
{
|
{
|
||||||
public Cache updatedItemsCache;
|
private Cache updatedItemsCache;
|
||||||
public Cache removedItemsCache;
|
private Cache removedItemsCache;
|
||||||
public boolean isClearOn;
|
private boolean isClearOn;
|
||||||
|
private boolean isClosed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,7 @@ package org.alfresco.repo.transaction;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -461,7 +462,7 @@ public abstract class AlfrescoTransactionSupport
|
|||||||
private final Set<TransactionalDao> daoServices;
|
private final Set<TransactionalDao> daoServices;
|
||||||
private final Set<IntegrityChecker> integrityCheckers;
|
private final Set<IntegrityChecker> integrityCheckers;
|
||||||
private final Set<LuceneIndexerAndSearcher> lucenes;
|
private final Set<LuceneIndexerAndSearcher> lucenes;
|
||||||
private final Set<TransactionListener> listeners;
|
private final LinkedHashSet<TransactionListener> listeners;
|
||||||
private final Map<Object, Object> resources;
|
private final Map<Object, Object> resources;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -476,7 +477,7 @@ public abstract class AlfrescoTransactionSupport
|
|||||||
daoServices = new HashSet<TransactionalDao>(3);
|
daoServices = new HashSet<TransactionalDao>(3);
|
||||||
integrityCheckers = new HashSet<IntegrityChecker>(3);
|
integrityCheckers = new HashSet<IntegrityChecker>(3);
|
||||||
lucenes = new HashSet<LuceneIndexerAndSearcher>(3);
|
lucenes = new HashSet<LuceneIndexerAndSearcher>(3);
|
||||||
listeners = new HashSet<TransactionListener>(5);
|
listeners = new LinkedHashSet<TransactionListener>(5);
|
||||||
resources = new HashMap<Object, Object>(17);
|
resources = new HashMap<Object, Object>(17);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user