198 lines
5.3 KiB
Java
198 lines
5.3 KiB
Java
package org.warp.jcwdb;
|
|
|
|
import java.io.IOError;
|
|
import java.io.IOException;
|
|
import java.nio.file.Path;
|
|
import java.util.Iterator;
|
|
import java.util.LinkedList;
|
|
import java.util.function.Supplier;
|
|
|
|
public class JCWDatabase implements AutoCloseable, Cleanable {
|
|
public final static long MAX_LOADED_INDICES = 1000;
|
|
|
|
private final TypesManager typesManager;
|
|
private final MixedIndexDatabase indices;
|
|
private final Cleaner databaseCleaner;
|
|
private final EntryReferenceTools entryReferenceTools = new EntryReferenceTools();
|
|
private volatile boolean closed;
|
|
private final Object closeLock = new Object();
|
|
private final Object indicesAccessLock = new Object();
|
|
private final LinkedList<EntryReference<?>> usedReferences = new LinkedList<>();
|
|
|
|
public JCWDatabase(Path dataFile, Path metadataFile) throws IOException {
|
|
this.typesManager = new TypesManager(this);
|
|
this.indices = new MixedIndexDatabase(dataFile, metadataFile);
|
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
try {
|
|
JCWDatabase.this.close();
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}));
|
|
this.databaseCleaner = new Cleaner(this);
|
|
|
|
this.databaseCleaner.start();
|
|
}
|
|
|
|
public <T> EntryReference<LightList<T>> getRoot() {
|
|
try {
|
|
checkClosed();
|
|
if (exists(0)) {
|
|
return get(0);
|
|
} else {
|
|
LightList<T> newRoot = new LightBigList<>(this);
|
|
return set(0, newRoot);
|
|
}
|
|
} catch (IOException e) {
|
|
throw new IOError(e);
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public <T> EntryReference<T> getRootItem(int index) {
|
|
return ((LightList<T>) getRoot().getValueReadOnlyUnsafe()).getReference(index);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public <T> EntryReference<T> getRootItem(int index, Supplier<T> defaultValue) {
|
|
return ((LightList<T>) getRoot().getValueReadOnlyUnsafe()).getReferenceOrInitialize(index, defaultValue);
|
|
}
|
|
|
|
public <T> EntryReference<LightList<T>> getRoot(Class<T> clazz) {
|
|
return getRoot().cast();
|
|
}
|
|
|
|
public <T> EntryReference<T> get(long index) throws IOException {
|
|
checkClosed();
|
|
int type;
|
|
long hash;
|
|
synchronized (indicesAccessLock) {
|
|
type = this.indices.getType(index);
|
|
hash = this.indices.getHash(index);
|
|
}
|
|
DBTypeParser<T> typeParser = this.typesManager.get(type);
|
|
return new EntryReference<>(entryReferenceTools, index, hash, typeParser);
|
|
}
|
|
|
|
protected <T> EntryReference<T> add(T value) throws IOException {
|
|
checkClosed();
|
|
DBTypeParser<T> typeParser = this.typesManager.get((Class<T>) value.getClass());
|
|
long index;
|
|
long hash;
|
|
synchronized (indicesAccessLock) {
|
|
FullIndexDetails fullIndexDetails = indices.addAndGetDetails(typeParser.getWriter(value));
|
|
index = fullIndexDetails.getIndex();
|
|
hash = fullIndexDetails.getHash();
|
|
}
|
|
return new EntryReference<>(entryReferenceTools, index, hash, typeParser, value);
|
|
}
|
|
|
|
protected boolean exists(long index) {
|
|
checkClosed();
|
|
synchronized (indicesAccessLock) {
|
|
return this.indices.has(index);
|
|
}
|
|
}
|
|
|
|
protected <T> EntryReference<T> set(long index, T value) throws IOException {
|
|
checkClosed();
|
|
EntryReference<T> ref;
|
|
if (exists(index)) {
|
|
ref = get(index);
|
|
ref.setValue(value);
|
|
return ref;
|
|
} else {
|
|
@SuppressWarnings("unchecked")
|
|
DBTypeParser<T> typeParser = this.typesManager.get((Class<T>) value.getClass());
|
|
long hash;
|
|
synchronized (indicesAccessLock) {
|
|
IndexDetails returnedDetails = indices.set(index, typeParser.getWriter(value));
|
|
hash = returnedDetails.getHash();
|
|
}
|
|
return new EntryReference<>(entryReferenceTools, index, hash, typeParser);
|
|
}
|
|
}
|
|
|
|
public <U> void registerType(Class<U> clazz, short type, DBTypeParser<U> parser) {
|
|
final int addition = 0xEFFF8000;
|
|
int extendedType = addition | (type & 0x7FFF);
|
|
typesManager.registerType(clazz, extendedType, parser);
|
|
}
|
|
|
|
public <U> void registerClass(Class<U> clazz, int type) {
|
|
typesManager.registerGenericClass(clazz, type);
|
|
}
|
|
|
|
public boolean isOpen() {
|
|
return !closed;
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException {
|
|
if (closed) {
|
|
return;
|
|
}
|
|
synchronized (closeLock) {
|
|
if (closed) {
|
|
return;
|
|
}
|
|
closed = true;
|
|
}
|
|
|
|
this.databaseCleaner.stop();
|
|
|
|
synchronized (indicesAccessLock) {
|
|
this.indices.close();
|
|
}
|
|
System.out.println("Database closed.");
|
|
}
|
|
|
|
private void checkClosed() {
|
|
if (closed) {
|
|
throw new RuntimeException("Index Manager is closed.");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public long clean() {
|
|
long removedItems = indices.clean();
|
|
Iterator<EntryReference<?>> usedReferencesIterator = usedReferences.iterator();
|
|
while(usedReferencesIterator.hasNext()) {
|
|
EntryReference<?> entryReference = usedReferencesIterator.next();
|
|
if (entryReference.isClosed()) {
|
|
usedReferencesIterator.remove();
|
|
removedItems += 1;
|
|
}
|
|
}
|
|
return removedItems;
|
|
}
|
|
|
|
public class EntryReferenceTools {
|
|
private EntryReferenceTools() {
|
|
|
|
}
|
|
|
|
public <T> T read(long index, DBReader<T> reader) throws IOException {
|
|
return indices.get(index, reader);
|
|
}
|
|
|
|
public <T> IndexDetails write(long index, DBDataOutput<T> writer) throws IOException {
|
|
return indices.set(index, writer);
|
|
}
|
|
|
|
public void setFlushingAllowed(long index, boolean isFlushingAllowed) {
|
|
indices.setFlushingAllowed(index, isFlushingAllowed);
|
|
}
|
|
|
|
public <T> void setUsed(EntryReference<T> ref) {
|
|
usedReferences.add(ref);
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
protected <T> long calculateHash(T o) {
|
|
return ((DBTypeParser<T>) typesManager.get(o.getClass())).calculateHash(o);
|
|
}
|
|
|
|
}
|