package net.enilink.komma.em.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import net.enilink.komma.core.IUnitOfWork;
public class UnitOfWork implements IUnitOfWork {
private ThreadLocal<Integer> countLocal = new ThreadLocal<>();
private ThreadLocal<List<AutoCloseable>> trackedCloseablesLocal = new ThreadLocal<>();
// track client threads for debugging purposes
private Set<Thread> clientThreads = Collections
.newSetFromMap(new ConcurrentHashMap<Thread, Boolean>());
private final boolean debug;
public UnitOfWork() {
this(false);
}
public UnitOfWork(boolean debug) {
this.debug = debug;
}
public void addCloseable(AutoCloseable closeable) {
List<AutoCloseable> trackedCloseables = trackedCloseablesLocal.get();
if (trackedCloseables == null) {
trackedCloseables = new ArrayList<>();
trackedCloseablesLocal.set(trackedCloseables);
}
trackedCloseables.add(closeable);
}
@Override
public void begin() {
Integer count = countLocal.get();
countLocal.set(count == null ? 1 : count + 1);
if (debug) {
clientThreads.add(Thread.currentThread());
}
}
@Override
public void end() {
Integer count = countLocal.get();
if (count == null) {
return;
}
int newCount = count - 1;
countLocal.set(newCount);
if (newCount <= 0) {
List<AutoCloseable> trackedCloseables = trackedCloseablesLocal
.get();
if (trackedCloseables != null) {
for (AutoCloseable closeable : trackedCloseables) {
try {
closeable.close();
} catch (Exception e) {
// ignore
}
}
trackedCloseables.clear();
}
trackedCloseablesLocal.remove();
countLocal.remove();
if (debug) {
clientThreads.remove(Thread.currentThread());
}
}
}
/**
* Tests if this unit of work is currently active.
*/
public boolean isActive() {
return countLocal.get() != null;
}
public Collection<Thread> getClientThreads() {
return Collections.unmodifiableCollection(clientThreads);
}
}