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> 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 EntryReference> getRoot() { try { checkClosed(); if (exists(0)) { return get(0); } else { LightList newRoot = new LightBigList<>(this); return set(0, newRoot); } } catch (IOException e) { throw new IOError(e); } } @SuppressWarnings("unchecked") public EntryReference getRootItem(int index) { return ((LightList) getRoot().getValueReadOnlyUnsafe()).getReference(index); } @SuppressWarnings("unchecked") public EntryReference getRootItem(int index, Supplier defaultValue) { return ((LightList) getRoot().getValueReadOnlyUnsafe()).getReferenceOrInitialize(index, defaultValue); } public EntryReference> getRoot(Class clazz) { return getRoot().cast(); } public EntryReference get(long index) throws IOException { checkClosed(); int type; long hash; synchronized (indicesAccessLock) { type = this.indices.getType(index); hash = this.indices.getHash(index); } DBTypeParser typeParser = this.typesManager.get(type); return new EntryReference<>(entryReferenceTools, index, hash, typeParser); } protected EntryReference add(T value) throws IOException { checkClosed(); DBTypeParser typeParser = this.typesManager.get((Class) 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 EntryReference set(long index, T value) throws IOException { checkClosed(); EntryReference ref; if (exists(index)) { ref = get(index); ref.setValue(value); return ref; } else { @SuppressWarnings("unchecked") DBTypeParser typeParser = this.typesManager.get((Class) 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 void registerType(Class clazz, short type, DBTypeParser parser) { final int addition = 0xEFFF8000; int extendedType = addition | (type & 0x7FFF); typesManager.registerType(clazz, extendedType, parser); } public void registerClass(Class 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> 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 read(long index, DBReader reader) throws IOException { return indices.get(index, reader); } public IndexDetails write(long index, DBDataOutput writer) throws IOException { return indices.set(index, writer); } public void setFlushingAllowed(long index, boolean isFlushingAllowed) { indices.setFlushingAllowed(index, isFlushingAllowed); } public void setUsed(EntryReference ref) { usedReferences.add(ref); } } @SuppressWarnings("unchecked") protected long calculateHash(T o) { return ((DBTypeParser) typesManager.get(o.getClass())).calculateHash(o); } }