package com.smartitengineering.cms.repo.dao.impl.tx;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Semaphore;
import org.apache.commons.collections.keyvalue.MultiKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author imyousuf
*/
@Singleton
class TransactionInMemoryCacheImpl implements TransactionInMemoryCache {
private final ConcurrentMap<MultiKey, Pair<TransactionStoreKey, TransactionStoreValue>> isolatedTxCache;
private final ConcurrentMap<MultiKey, Deque<Pair<TransactionStoreKey, TransactionStoreValue>>> globalCache;
private final Semaphore semaphore;
private static final Logger LOGGER = LoggerFactory.getLogger(TransactionInMemoryCacheImpl.class);
private final boolean nonIsolatedLookupEnabled;
@Inject
public TransactionInMemoryCacheImpl(@Named("nonIsolatedLookupEnabled") boolean nonIsolatedLookupEnabled) {
this.isolatedTxCache = new ConcurrentHashMap<MultiKey, Pair<TransactionStoreKey, TransactionStoreValue>>();
this.globalCache = new ConcurrentHashMap<MultiKey, Deque<Pair<TransactionStoreKey, TransactionStoreValue>>>();
this.semaphore = new Semaphore(1);
this.nonIsolatedLookupEnabled = nonIsolatedLookupEnabled;
}
public Pair<TransactionStoreKey, TransactionStoreValue> getValueForIsolatedTransaction(TransactionStoreKey key) {
return getValueForIsolatedTransaction(key.getTransactionId(), key.getObjectType().getName(), key.getObjectId());
}
public Pair<TransactionStoreKey, TransactionStoreValue> getValueForNonIsolatedTransaction(TransactionStoreKey key) {
return getValueForNonIsolatedTransaction(key.getObjectType().getName(), key.getObjectId());
}
public Pair<TransactionStoreKey, TransactionStoreValue> getValueForIsolatedTransaction(String txId, String objectType,
String objectId) {
MultiKey isolatedTxKey = new MultiKey(new Object[]{txId, objectType, objectId});
return isolatedTxCache.get(isolatedTxKey);
}
public Pair<TransactionStoreKey, TransactionStoreValue> getValueForNonIsolatedTransaction(String objectType,
String objectId) {
if (!this.nonIsolatedLookupEnabled) {
throw new UnsupportedOperationException("Non isolated lookup disabled!");
}
MultiKey gKey = new MultiKey(new Object[]{objectType, objectId});
final Deque<Pair<TransactionStoreKey, TransactionStoreValue>> stack = globalCache.get(gKey);
if (stack != null) {
return stack.peek();
}
else {
return null;
}
}
public void storeTransactionValue(TransactionStoreKey key, TransactionStoreValue val) {
Pair<TransactionStoreKey, TransactionStoreValue> pairVal = new Pair<TransactionStoreKey, TransactionStoreValue>(key,
val);
MultiKey iKey = new MultiKey(new Object[]{key.getTransactionId(), key.getObjectType().getName(), key.getObjectId()});
isolatedTxCache.put(iKey, pairVal);
if (this.nonIsolatedLookupEnabled) {
MultiKey gKey = new MultiKey(new Object[]{key.getObjectType().getName(), key.getObjectId()});
ArrayDeque<Pair<TransactionStoreKey, TransactionStoreValue>> newStack =
new ArrayDeque<Pair<TransactionStoreKey, TransactionStoreValue>>();
try {
semaphore.acquire();
try {
Deque<Pair<TransactionStoreKey, TransactionStoreValue>> stack = globalCache.putIfAbsent(gKey, newStack);
if (stack == null) {
stack = newStack;
}
stack.push(pairVal);
}
finally {
semaphore.release();
}
}
catch (InterruptedException ie) {
throw new IllegalStateException(ie);
}
}
}
public List<Pair<TransactionStoreKey, TransactionStoreValue>> getTransactionParticipants(String txId) {
List<Pair<TransactionStoreKey, TransactionStoreValue>> pairs =
new ArrayList<Pair<TransactionStoreKey, TransactionStoreValue>>();
for (MultiKey tKey : isolatedTxCache.keySet()) {
if (txId.equals(tKey.getKey(0))) {
pairs.add(isolatedTxCache.get(tKey));
}
}
return pairs;
}
public void removeTransactionReferences(String txId) {
final Iterator<Entry<MultiKey, Pair<TransactionStoreKey, TransactionStoreValue>>> iterator =
isolatedTxCache.entrySet().
iterator();
while (iterator.hasNext()) {
Entry<MultiKey, Pair<TransactionStoreKey, TransactionStoreValue>> entry = iterator.next();
MultiKey tKey = entry.getKey();
if (txId.equals(tKey.getKey(0))) {
iterator.remove();
if (this.nonIsolatedLookupEnabled) {
MultiKey gKey = new MultiKey(Arrays.copyOfRange(tKey.getKeys(), 1, 3));
final Deque<Pair<TransactionStoreKey, TransactionStoreValue>> mainStack = globalCache.get(gKey);
mainStack.remove(entry.getValue());
try {
semaphore.acquire();
try {
if (mainStack.isEmpty()) {
globalCache.remove(gKey);
}
}
finally {
semaphore.release();
}
}
catch (Exception ex) {
LOGGER.error("Error acquiring lock for global cache cleanup", ex);
}
}
}
}
}
}