package pt.ist.fenixframework.util;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.ConcurrentHashMap;
import pt.ist.fenixframework.Transaction;
public class TxMap {
/**
* Key based on a WeakReference, to be used with the txToLocalMap. This
* design allows old TxLocal objects to be GC'd along with their
* corresponding Transaction objects.
*/
private static final class MapKey extends WeakReference<javax.transaction.Transaction> {
private static final ReferenceQueue<javax.transaction.Transaction> REFERENCE_QUEUE = new ReferenceQueue<javax.transaction.Transaction>();
static {
// This thread monitors the reference queue and removes the
// TxLocal object corresponding
// to a GC'd transaction from the txToLocalMap
(new Thread(TxMap.class.getName() + " GC Thread") {
{
setDaemon(true);
}
@Override
public void run() {
while (true)
try {
Object ref = REFERENCE_QUEUE.remove();
while (TxMap.txToLocalMap.remove(ref) != null)
;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
}
private final int hashCode;
private MapKey(javax.transaction.Transaction transaction) {
super(transaction, REFERENCE_QUEUE);
hashCode = transaction.hashCode();
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object other) {
if (other instanceof MapKey) {
Object myObject = get();
Object otherObject = ((MapKey) other).get();
// NOTE: By design, two mapkeys originally pointing to
// different transactions are
// considered equal once both are cleared (for GC purposes)
return myObject == otherObject || (myObject != null && myObject.equals(otherObject));
}
return false;
}
}
private static final ConcurrentHashMap<MapKey, Transaction> txToLocalMap = new ConcurrentHashMap<MapKey, Transaction>();
public static Transaction getTx(javax.transaction.Transaction transaction) {
MapKey mapKey = new MapKey(transaction);
Transaction txLocal = txToLocalMap.get(mapKey);
if (txLocal == null) {
txLocal = new JTADelegatingTransaction(transaction);
Transaction oldTxLocal = txToLocalMap.putIfAbsent(mapKey, txLocal);
if (oldTxLocal != null) {
txLocal = oldTxLocal;
mapKey.clear(); // not going to be needed anymore, help out
// GC
}
} else {
mapKey.clear(); // not going to be needed anymore, help out GC
}
return txLocal;
}
}