package be.bagofwords.db;
import be.bagofwords.application.ApplicationContext;
import be.bagofwords.application.BowTaskScheduler;
import be.bagofwords.application.LateCloseableComponent;
import be.bagofwords.application.memory.MemoryManager;
import be.bagofwords.cache.CachesManager;
import be.bagofwords.db.bloomfilter.BloomFilterDataInterface;
import be.bagofwords.db.bloomfilter.LongBloomFilterWithCheckSum;
import be.bagofwords.db.cached.CachedDataInterface;
import be.bagofwords.db.combinator.Combinator;
import be.bagofwords.db.combinator.LongCombinator;
import be.bagofwords.db.combinator.OverWriteCombinator;
import be.bagofwords.db.memory.InMemoryDataInterface;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
public abstract class DataInterfaceFactory implements LateCloseableComponent {
private int tmpDataInterfaceCount = 0;
private CachesManager cachesManager;
private MemoryManager memoryManager;
protected BowTaskScheduler taskScheduler;
private List<DataInterfaceReference> allInterfaces;
private ReferenceQueue<DataInterface> allInterfacesReferenceQueue;
private DataInterface<LongBloomFilterWithCheckSum> cachedBloomFilters;
public DataInterfaceFactory(ApplicationContext context) {
this.cachesManager = context.getBean(CachesManager.class);
this.memoryManager = context.getBean(MemoryManager.class);
this.taskScheduler = context.getBean(BowTaskScheduler.class);
this.allInterfaces = new ArrayList<>();
this.allInterfacesReferenceQueue = new ReferenceQueue<>();
}
public abstract <T extends Object> DataInterface<T> createBaseDataInterface(String nameOfSubset, Class<T> objectClass, Combinator<T> combinator, boolean isTemporaryDataInterface);
public DataInterface<Long> createCountDataInterface(String subset) {
return createDataInterface(DatabaseCachingType.CACHED, subset, Long.class, new LongCombinator(), false);
}
public DataInterface<Long> createTmpCountDataInterface(String subset) {
return createDataInterface(DatabaseCachingType.CACHED, createNameForTemporaryInterface(subset), Long.class, new LongCombinator(), true);
}
public <T extends Object> DataInterface<T> createDataInterface(String subset, Class<T> objectClass, Combinator<T> combinator) {
return createDataInterface(DatabaseCachingType.CACHED, subset, objectClass, combinator, false);
}
public <T extends Object> DataInterface<T> createDataInterface(DatabaseCachingType cachingType, String subset, Class<T> objectClass, Combinator<T> combinator) {
return createDataInterface(cachingType, subset, objectClass, combinator, false);
}
public DataInterface<Long> createInMemoryCountDataInterface(String name) {
return createInMemoryDataInterface(DatabaseCachingType.CACHED, name, Long.class, new LongCombinator());
}
public <T extends Object> DataInterface<T> createInMemoryDataInterface(DatabaseCachingType cachingType, String name, Class<T> objectClass, Combinator<T> combinator) {
DataInterface<T> result = new InMemoryDataInterface<>(name, objectClass, combinator);
result = decorateAndAdd(cachingType, result);
return result;
}
public <T extends Object> DataInterface<T> createTmpDataInterface(String subset, Class<T> objectClass, Combinator<T> combinator) {
return createDataInterface(DatabaseCachingType.CACHED, createNameForTemporaryInterface(subset), objectClass, combinator, true);
}
public <T extends Object> DataInterface<T> createTmpDataInterface(DatabaseCachingType cachingType, String subset, Class<T> objectClass, Combinator<T> combinator) {
return createDataInterface(cachingType, createNameForTemporaryInterface(subset), objectClass, combinator, true);
}
public <T extends Object> DataInterface<T> createDataInterface(DatabaseCachingType cachingType, String subset, Class<T> objectClass, Combinator<T> combinator, boolean isTemporaryDataInterface) {
DataInterface<T> result = createBaseDataInterface(subset, objectClass, combinator, isTemporaryDataInterface);
result = decorateAndAdd(cachingType, result);
return result;
}
private <T extends Object> DataInterface<T> decorateAndAdd(DatabaseCachingType type, DataInterface<T> result) {
if (type.useCache()) {
result = cached(result);
}
if (type.useBloomFilter()) {
result = bloom(result);
}
synchronized (allInterfaces) {
allInterfaces.add(new DataInterfaceReference(result, allInterfacesReferenceQueue));
}
return result;
}
protected <T extends Object> DataInterface<T> cached(DataInterface<T> baseDataInterface) {
return new CachedDataInterface<>(memoryManager, cachesManager, baseDataInterface, taskScheduler);
}
protected <T extends Object> DataInterface<T> bloom(DataInterface<T> dataInterface) {
checkInitialisationCachedBloomFilters();
return new BloomFilterDataInterface<>(dataInterface, cachedBloomFilters, taskScheduler);
}
private void checkInitialisationCachedBloomFilters() {
if (cachedBloomFilters == null) {
cachedBloomFilters = createBaseDataInterface("system/bloomFilter", LongBloomFilterWithCheckSum.class, new OverWriteCombinator<>(), false);
synchronized (allInterfaces) {
allInterfaces.add(new DataInterfaceReference(cachedBloomFilters, allInterfacesReferenceQueue));
}
}
}
public List<DataInterfaceReference> getAllInterfaces() {
return allInterfaces;
}
@Override
public synchronized void terminate() {
closeAllInterfaces();
}
public void closeAllInterfaces() {
synchronized (allInterfaces) {
for (WeakReference<DataInterface> referenceToDI : allInterfaces) {
final DataInterface dataInterface = referenceToDI.get();
if (dataInterface != null && dataInterface != cachedBloomFilters) {
dataInterface.close();
}
}
if (cachedBloomFilters != null) {
cachedBloomFilters.close();
cachedBloomFilters = null;
}
allInterfaces.clear();
}
}
@Override
public String toString() {
return getClass().getSimpleName();
}
public void cleanupClosedInterfaces() {
DataInterfaceReference reference = (DataInterfaceReference) allInterfacesReferenceQueue.poll();
while (reference != null) {
synchronized (allInterfaces) {
allInterfaces.remove(reference);
}
reference = (DataInterfaceReference) allInterfacesReferenceQueue.poll();
}
}
private String createNameForTemporaryInterface(String subset) {
return "tmp/" + subset + "_" + System.currentTimeMillis() + "_" + tmpDataInterfaceCount++ + "/";
}
public static class DataInterfaceReference extends WeakReference<DataInterface> {
private String subsetName;
public DataInterfaceReference(DataInterface referent, ReferenceQueue<DataInterface> referenceQueue) {
super(referent, referenceQueue);
this.subsetName = referent.getName();
}
public String getSubsetName() {
return subsetName;
}
}
}