Added CowList

This commit is contained in:
Andrea Cavalli 2019-02-01 00:04:51 +01:00
parent a772ac5431
commit b06ee07ad2
38 changed files with 172 additions and 2868 deletions

View File

@ -5,7 +5,12 @@ import com.esotericsoftware.kryo.io.ByteBufferInput;
import com.esotericsoftware.kryo.io.ByteBufferInputStream;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
import it.unimi.dsi.fastutil.chars.CharArrayList;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.warp.jcwdb.ann.*;
@ -20,8 +25,7 @@ import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.*;
import java.util.function.Supplier;
import static org.warp.cowdb.IBlocksMetadata.EMPTY_BLOCK_ID;
@ -102,6 +106,10 @@ public class Database implements IDatabase {
return root;
}
protected void registerClass(Class<?> type, int id) {
this.objectsIO.registerClass(type, id);
}
public static class DatabaseDataInitializer implements IDataInitializer {
private final DatabaseObjectsIO objectsIO;
@ -203,6 +211,59 @@ public class Database implements IDatabase {
private DatabaseObjectsIO(IDatabase database, DatabaseReferencesIO referencesIO) {
this.database = database;
this.referencesIO = referencesIO;
kryo.setRegistrationRequired(false);
int id = -90;
registerClass(boolean[].class, id++);
registerClass(byte[].class, id++);
registerClass(short[].class, id++);
registerClass(char[].class, id++);
registerClass(int[].class, id++);
registerClass(long[].class, id++);
registerClass(Boolean[].class, id++);
registerClass(Byte[].class, id++);
registerClass(Short[].class, id++);
registerClass(Character[].class, id++);
registerClass(Integer[].class, id++);
registerClass(Long[].class, id++);
registerClass(String.class, id++);
registerClass(String[].class, id++);
registerClass(Boolean.class, id++);
registerClass(Byte.class, id++);
registerClass(Short.class, id++);
registerClass(Character.class, id++);
registerClass(Integer.class, id++);
registerClass(Class.class, id++);
registerClass(Object.class, id++);
registerClass(Object[].class, id++);
registerClass(Long.class, id++);
registerClass(String.class, id++);
registerClass(String[].class, id++);
registerClass(boolean[][].class, id++);
registerClass(byte[][].class, id++);
registerClass(short[][].class, id++);
registerClass(char[][].class, id++);
registerClass(int[][].class, id++);
registerClass(long[][].class, id++);
registerClass(String[][].class, id++);
registerClass(List.class, id++);
registerClass(ArrayList.class, id++);
registerClass(LinkedList.class, id++);
registerClass(Set.class, id++);
registerClass(HashSet.class, id++);
registerClass(LinkedHashSet.class, id++);
registerClass(Map.class, id++);
registerClass(HashMap.class, id++);
registerClass(LinkedHashMap.class, id++);
registerClass(TreeMap.class, id++);
registerClass(BooleanArrayList.class, id++);
registerClass(ByteArrayList.class, id++);
registerClass(ShortArrayList.class, id++);
registerClass(CharArrayList.class, id++);
registerClass(IntArrayList.class, id++);
registerClass(LongArrayList.class, id++);
registerClass(TreeSet.class, id++);
registerClass(SortedSet.class, id++);
registerClass(SortedMap.class, id++);
}
@Override
@ -301,7 +362,7 @@ public class Database implements IDatabase {
try {
setData(objectFullInfo.getFieldReferences()[i], objectFullInfo.getFieldTypes()[i], objectFullInfo.getFields()[i].get(value));
} catch (IllegalAccessException e) {
throw new IOError(e);
throw new IOException(e);
}
}
for (int i = 0; i < objectFullInfo.getPropertyReferences().length; i++) {
@ -483,6 +544,14 @@ public class Database implements IDatabase {
obj.setProperty(propertyId, loadData(propertyType, propertyUID, property::getReturnType));
}
@Override
public void registerClass(Class<?> type, int id) {
if (id < -100) {
throw new IllegalArgumentException();
}
kryo.register(type, 100 + id);
}
private <T extends EnhancedObject> void preloadEnhancedObjectProperties(T obj, long[] propertyReferences) {
// Declare the variables needed to get the biggest property Id
Method[] unorderedPropertyGetters = obj.getPropertyGetters();
@ -671,19 +740,17 @@ public class Database implements IDatabase {
public static class DatabaseReferencesMetadata implements IReferencesMetadata {
private final SeekableByteChannel metaFileChannel;
private final int REF_META_BYTES_COUNT = Long.BYTES;
private long metaFileChannelSize;
private long firstFreeReference;
private DatabaseReferencesMetadata(Path refMetaFile) throws IOException {
metaFileChannel = Files.newByteChannel(refMetaFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
metaFileChannelSize = metaFileChannel.size();
firstFreeReference = metaFileChannelSize / REF_META_BYTES_COUNT;
firstFreeReference = metaFileChannel.size() / REF_META_BYTES_COUNT;
}
@Override
public long getReference(long reference) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(REF_META_BYTES_COUNT);
if (reference >= firstFreeReference || reference * REF_META_BYTES_COUNT > metaFileChannelSize) {
if (reference >= firstFreeReference) {
return EMPTY_BLOCK_ID;
}
SeekableByteChannel currentFileChannel = metaFileChannel.position(reference * REF_META_BYTES_COUNT);

View File

@ -12,7 +12,7 @@ public class EnhancedObjectFullInfo {
private final DBDataType[] propertyTypes;
private final Object[] loadedPropertyValues;
public EnhancedObjectFullInfo(long[] fieldReferences, DBDataType[] fieldTypes, Field[] fields, long[] propertyReferences, DBDataType[] propertyTypes, Object[] loadedPropertyValues) {
EnhancedObjectFullInfo(long[] fieldReferences, DBDataType[] fieldTypes, Field[] fields, long[] propertyReferences, DBDataType[] propertyTypes, Object[] loadedPropertyValues) {
this.fieldReferences = fieldReferences;
this.fieldTypes = fieldTypes;
this.fields = fields;
@ -21,11 +21,11 @@ public class EnhancedObjectFullInfo {
this.loadedPropertyValues = loadedPropertyValues;
}
public long[] getFieldReferences() {
long[] getFieldReferences() {
return fieldReferences;
}
public DBDataType[] getFieldTypes() {
DBDataType[] getFieldTypes() {
return fieldTypes;
}
@ -33,15 +33,15 @@ public class EnhancedObjectFullInfo {
return fields;
}
public long[] getPropertyReferences() {
long[] getPropertyReferences() {
return propertyReferences;
}
public DBDataType[] getPropertyTypes() {
DBDataType[] getPropertyTypes() {
return propertyTypes;
}
public Object[] getLoadedPropertyValues() {
Object[] getLoadedPropertyValues() {
return loadedPropertyValues;
}
}

View File

@ -6,7 +6,7 @@ import org.warp.jcwdb.ann.DBDataType;
import java.io.IOException;
import java.lang.reflect.Method;
interface IObjectsIO {
public interface IObjectsIO {
<T extends EnhancedObject> T loadEnhancedObject(long reference, Class<T> objectType) throws IOException;
<T> T loadObject(long reference) throws IOException;
@ -106,4 +106,6 @@ interface IObjectsIO {
}
void loadProperty(EnhancedObject enhancedObject, int propertyId, Method propertyGetter, DBDataType propertyType, long propertyUID) throws IOException;
void registerClass(Class<?> type, int id);
}

View File

@ -1,85 +1,72 @@
package org.warp.jcwdb.ann;
package org.warp.cowdb.lists;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.warp.cowdb.EnhancedObject;
import org.warp.cowdb.IDatabase;
import org.warp.jcwdb.ann.DBDataType;
import org.warp.jcwdb.ann.DBField;
import java.io.IOError;
import java.io.IOException;
import java.util.StringJoiner;
public abstract class DBArrayList<T> extends DBObject {
public abstract class CowList<T> extends EnhancedObject {
private final Object indicesAccessLock = new Object();
@DBField(id = 0, type = DBDataType.UID_LIST)
@DBField(id = 0, type = DBDataType.REFERENCES_LIST)
private LongArrayList indices;
public DBArrayList() {
super();
public CowList() {
}
public DBArrayList(JCWDatabase database) throws IOException {
public CowList(IDatabase database) throws IOException {
super(database);
}
@Override
public void initialize() {
public void initialize() throws IOException {
indices = new LongArrayList();
}
public T get(int index) {
public T get(int index) throws IOException {
synchronized (indicesAccessLock) {
try {
long uid = indices.getLong(index);
return loadItem(uid);
} catch (IOException e) {
throw new IOError(e);
}
}
}
public void add(T value) {
long uid = database.getDataLoader().allocateNullValue();
public void add(T value) throws IOException {
long uid = database.getObjectsIO().newNullObject();
synchronized (indicesAccessLock) {
indices.add(uid);
try {
writeItemToDisk(uid, value);
} catch (IOException e) {
throw new IOError(e);
}
}
}
public void update(int index, T value) {
public void update(int index, T value) throws IOException {
synchronized (indicesAccessLock) {
set(index, value);
}
}
public void set(int index, T value) {
long uid = database.getDataLoader().allocateNullValue();
public void set(int index, T value) throws IOException {
long uid = database.getObjectsIO().newNullObject();
synchronized (indicesAccessLock) {
indices.set(index, uid);
try {
writeItemToDisk(uid, value);
} catch (IOException e) {
throw new IOError(e);
}
}
}
public void add(int index, T value) {
long uid = database.getDataLoader().allocateNullValue();
public void add(int index, T value) throws IOException {
long uid = database.getObjectsIO().newNullObject();
synchronized (indicesAccessLock) {
indices.add(index, uid);
try {
writeItemToDisk(uid, value);
} catch (IOException e) {
throw new IOError(e);
}
}
}
public T getLast() {
public T getLast() throws IOException {
synchronized (indicesAccessLock) {
if (indices.size() > 0) {
return get(indices.size() - 1);
@ -107,7 +94,7 @@ public abstract class DBArrayList<T> extends DBObject {
@Override
public String toString() {
return new StringJoiner(", ", DBArrayList.class.getSimpleName() + "[", "]")
return new StringJoiner(", ", CowList.class.getSimpleName() + "[", "]")
.add(indices.size() + " items")
.toString();
}

View File

@ -0,0 +1,33 @@
package org.warp.cowdb.lists;
import org.warp.cowdb.EnhancedObject;
import org.warp.cowdb.IDatabase;
import org.warp.jcwdb.ann.DBDataType;
import org.warp.jcwdb.ann.DBField;
import java.io.IOException;
public class EnhancedObjectCowList<T extends EnhancedObject> extends CowList<T> {
@DBField(id = 1, type = DBDataType.OBJECT)
private Class<T> type;
public EnhancedObjectCowList() {
super();
}
public EnhancedObjectCowList(IDatabase database, Class<T> type) throws IOException {
super(database);
this.type = type;
}
@Override
protected T loadItem(long uid) throws IOException {
return database.getObjectsIO().loadEnhancedObject(uid, type);
}
@Override
protected void writeItemToDisk(long uid, T item) throws IOException {
database.getObjectsIO().setEnhancedObject(uid, item);
}
}

View File

@ -0,0 +1,29 @@
package org.warp.cowdb.lists;
import org.warp.cowdb.EnhancedObject;
import org.warp.cowdb.IDatabase;
import org.warp.jcwdb.ann.DBDataType;
import org.warp.jcwdb.ann.DBField;
import java.io.IOException;
public class ObjectCowList<T> extends CowList<T> {
public ObjectCowList() {
super();
}
public ObjectCowList(IDatabase database) throws IOException {
super(database);
}
@Override
protected T loadItem(long uid) throws IOException {
return database.getObjectsIO().loadObject(uid);
}
@Override
protected void writeItemToDisk(long uid, T item) throws IOException {
database.getObjectsIO().setObject(uid, item);
}
}

View File

@ -1,5 +0,0 @@
package org.warp.jcwdb;
public interface Castable {
<T> T cast();
}

View File

@ -1,9 +0,0 @@
package org.warp.jcwdb;
public interface Cleanable {
/**
* Clean the object
* @return the approximated number of cleaned items
*/
public long clean();
}

View File

@ -1,116 +0,0 @@
package org.warp.jcwdb;
public class Cleaner {
public static final boolean DISABLE_CLEANER = false;
public static final boolean ENABLE_CLEANER_LOGGING = false;
private static final double MAXIMUM_SLEEP_INTERVAL = 8d * 1000d; // 8 seconds
private static final double MINIMUM_SLEEP_INTERVAL = 1d * 1000d; // 1 second
private static final double NORMAL_REMOVED_ITEMS = 2500l;
private static final double REMOVED_ITEMS_RATIO = 2.5d; // 250%
private final Cleanable[] objectsToClean;
private final Thread cleanerThread;
private int sleepInterval = (int) MINIMUM_SLEEP_INTERVAL;
private volatile boolean stopRequest = false;
public Cleaner(Cleanable... objectsToClean) {
this.objectsToClean = objectsToClean;
this.cleanerThread = new Thread(new CleanLoop());
this.cleanerThread.setName("Cleaner thread");
this.cleanerThread.setDaemon(true);
}
public void start() {
if (!DISABLE_CLEANER) {
this.cleanerThread.start();
}
}
/**
* Clean
* @return number of removed items
*/
private long clean() {
long cleanedItems = 0;
for (Cleanable cleanable : objectsToClean) {
cleanedItems += cleanable.clean();
}
//System.gc();
return cleanedItems;
}
public void stop() {
if (cleanerThread != null) {
stopRequest = true;
while (cleanerThread.isAlive()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private class CleanLoop implements Runnable {
@Override
public void run() {
while(!stopRequest) {
try {
if (ENABLE_CLEANER_LOGGING) System.out.println("[CLEANER] Waiting " + sleepInterval + "ms.");
sleepFor(sleepInterval);
final long time1 = System.currentTimeMillis();
final double removedItems = clean();
final long time2 = System.currentTimeMillis();
if (ENABLE_CLEANER_LOGGING) System.out.println("[CLEANER] CLEAN_TIME " + (time2 - time1));
double suggestedExecutionTimeByItemsCalculations = (sleepInterval + MAXIMUM_SLEEP_INTERVAL) / 2;
if (ENABLE_CLEANER_LOGGING) System.out.println("[CLEANER] REMOVED_ITEMS: " + removedItems);
if (removedItems > 0) {
final double removedItemsRatio = removedItems / NORMAL_REMOVED_ITEMS;
if (ENABLE_CLEANER_LOGGING) System.out.println("[CLEANER] REMOVED_ITEMS_RATIO: " + removedItemsRatio);
if (removedItemsRatio < 1d / REMOVED_ITEMS_RATIO || removedItemsRatio >= REMOVED_ITEMS_RATIO) {
suggestedExecutionTimeByItemsCalculations = sleepInterval / removedItemsRatio;
}
}
if (ENABLE_CLEANER_LOGGING) System.out.println("[CLEANER] Items: SUGGESTING SLEEP_INTERVAL FROM " + sleepInterval + "ms TO " + suggestedExecutionTimeByItemsCalculations + "ms");
double newSleepInterval = suggestedExecutionTimeByItemsCalculations;
if (ENABLE_CLEANER_LOGGING) System.out.println("[CLEANER] Total: SUGGESTING SLEEP_INTERVAL FROM " + sleepInterval + "ms TO " + newSleepInterval + "ms");
if (newSleepInterval > MAXIMUM_SLEEP_INTERVAL) {
sleepInterval = (int) MAXIMUM_SLEEP_INTERVAL;
} else if (newSleepInterval < MINIMUM_SLEEP_INTERVAL) {
sleepInterval = (int) MINIMUM_SLEEP_INTERVAL;
} else {
if (ENABLE_CLEANER_LOGGING) System.out.println("[CLEANER] CHANGED SLEEP_INTERVAL FROM " + sleepInterval + "ms TO " + newSleepInterval + "ms");
sleepInterval = (int) newSleepInterval;
}
if (ENABLE_CLEANER_LOGGING) System.out.println("[CLEANER] Cleaned " + removedItems + " items.");
}catch (InterruptedException e) {
}
}
}
private void sleepFor(int sleepInterval) throws InterruptedException {
int lastI = (int) Math.ceil(((double) sleepInterval) / 1000d);
for (int i = 0; i < lastI; i++) {
if (stopRequest) {
return;
}
if (i == lastI) {
Thread.sleep(sleepInterval % 1000);
} else {
Thread.sleep(lastI);
}
Thread.sleep(sleepInterval);
}
}
}
}

View File

@ -1,7 +0,0 @@
package org.warp.jcwdb;
import com.esotericsoftware.kryo.io.Input;
public interface DBReader<T> {
T read(Input i, int size);
}

View File

@ -1,7 +0,0 @@
package org.warp.jcwdb;
import com.esotericsoftware.kryo.io.Output;
public interface DBWriter {
void write(Output o);
}

View File

@ -1,151 +0,0 @@
package org.warp.jcwdb;
import it.unimi.dsi.fastutil.longs.Long2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2IntMap;
import java.io.IOException;
import java.nio.channels.SeekableByteChannel;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
public class FileAllocator implements AutoCloseable {
private static final int MAXIMUM_UNALLOCATED_ENTRIES = 50000;
private final SeekableByteChannel dataFileChannel;
private volatile long fileSize;
private volatile boolean closed;
private final Object closeLock = new Object();
private final Object allocateLock = new Object();
/**
* index -> free space size
*/
private final Long2IntMap freeBytes = new Long2IntLinkedOpenHashMap();
public FileAllocator(SeekableByteChannel dataFileChannel) throws IOException {
this.dataFileChannel = dataFileChannel;
this.fileSize = this.dataFileChannel.size();
}
public FileAllocator(SeekableByteChannel dataFileChannel, long fileSize, Long2IntMap freeBytes) {
this.dataFileChannel = dataFileChannel;
this.fileSize = fileSize;
this.freeBytes.putAll(freeBytes);
}
/**
* TODO: not implemented
*
* @param size
* @return offset
*/
public long allocate(int size) {
checkClosed();
synchronized (allocateLock) {
long offset;
if ((offset = allocateIntoUnusedParts(size)) != -1) {
if (offset + size > fileSize) {
fileSize = offset + size;
}
return offset;
} else {
return allocateToEnd(size);
}
}
}
private long allocateIntoUnusedParts(int size) {
if (FileIndexManager.ALWAYS_ALLOCATE_NEW) return -1;
Stream<Map.Entry<Long,Integer>> sorted =
freeBytes.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()));
final VariableWrapper<Long> holeOffset = new VariableWrapper<>(-1L);
final VariableWrapper<Integer> holeSize = new VariableWrapper<>(0);
sorted.anyMatch((entry) -> {
int currentHoleSize = entry.getValue();
if (currentHoleSize < size) {
return true;
}
holeOffset.var = entry.getKey();
holeSize.var = currentHoleSize;
return false;
});
if (holeOffset.var != -1L) {
freeBytes.remove(holeOffset.var);
if (holeSize.var > size) {
freeBytes.put(holeOffset.var + size, holeSize.var - size);
}
}
return holeOffset.var;
}
private long allocateToEnd(int size) {
long allocatedOffset = fileSize;
fileSize += size;
return allocatedOffset;
}
public void close() throws IOException {
if (closed) {
return;
}
synchronized (closeLock) {
if (closed) {
return;
}
closed = true;
}
}
/**
* Frees the unused bytes
*
* @param startPosition
* @param length
*/
public void markFree(long startPosition, int length) {
checkClosed();
if (FileIndexManager.ALWAYS_ALLOCATE_NEW) return;
if (freeBytes.containsKey(startPosition + length)) {
int secondLength = freeBytes.remove(startPosition + length);
freeBytes.put(startPosition, length + secondLength);
} else {
boolean addedToList = false;
for (Long2IntMap.Entry entry : freeBytes.long2IntEntrySet()) {
if (entry.getLongKey() + entry.getIntValue() == startPosition) {
freeBytes.put(entry.getLongKey(), entry.getIntValue() + length);
addedToList = true;
break;
}
}
if (!addedToList && length > 0) {
freeBytes.put(startPosition, length);
}
}
if (startPosition + length >= fileSize) {
fileSize = startPosition;
}
// Remove the smallest hole in the file
if (freeBytes.size() > MAXIMUM_UNALLOCATED_ENTRIES) {
Stream<Map.Entry<Long,Integer>> sorted =
freeBytes.entrySet().stream()
.sorted(Map.Entry.comparingByValue());
Optional<Map.Entry<Long, Integer>> first = sorted.findFirst();
if (first.isPresent()) {
freeBytes.remove(first.get().getKey());
}
}
}
private void checkClosed() {
if (closed) {
throw new RuntimeException("Index Manager is closed.");
}
}
}

View File

@ -1,495 +0,0 @@
package org.warp.jcwdb;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import it.unimi.dsi.fastutil.longs.*;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import org.warp.jcwdb.ann.DatabaseManager;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class FileIndexManager implements IndexManager {
public static final boolean ALWAYS_ALLOCATE_NEW = true;
private final SeekableByteChannel dataFileChannel, metadataFileChannel;
private volatile long metadataFileChannelSize;
private final FileAllocator fileAllocator;
private final ByteBuffer metadataByteBuffer = ByteBuffer.allocateDirect(IndexDetails.TOTAL_BYTES);
private final ByteBuffer maskByteBuffer = ByteBuffer.allocateDirect(Long.BYTES);
private volatile boolean closed;
private final Object closeLock = new Object();
private final Object metadataByteBufferLock = new Object();
private final Object maskByteBufferLock = new Object();
private final Object indicesMapsAccessLock = new Object();
/**
* Edit this using editIndex()
* Get using getIndexMetadata()
* This hashmap must contain all indices.
*/
private final Long2ObjectMap<IndexDetails> loadedIndices;
/**
* Edit this using editIndex()
*/
private final LongSet dirtyLoadedIndices, removedIndices;
private long firstAllocableIndex;
public FileIndexManager(Path dataFile, Path metadataFile) throws IOException {
if (Cleaner.DISABLE_CLEANER) {
loadedIndices = new Long2ObjectOpenHashMap<>();
dirtyLoadedIndices = new LongOpenHashSet();
removedIndices = new LongOpenHashSet();
} else {
loadedIndices = new Long2ObjectLinkedOpenHashMap<>();
dirtyLoadedIndices = new LongLinkedOpenHashSet();
removedIndices = new LongLinkedOpenHashSet();
}
if (Files.notExists(dataFile)) {
Files.createFile(dataFile);
}
if (Files.notExists(metadataFile)) {
Files.createFile(metadataFile);
}
dataFileChannel = Files.newByteChannel(dataFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
metadataFileChannel = Files.newByteChannel(metadataFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
metadataFileChannelSize = metadataFileChannel.size();
fileAllocator = createFileAllocator(dataFileChannel, metadataFileChannel.position(0));
firstAllocableIndex = getMetadataFileChannelSize() / (long) IndexDetails.TOTAL_BYTES;
}
private long getMetadataFileChannelSize() {
return metadataFileChannelSize;
}
private FileAllocator createFileAllocator(final SeekableByteChannel dataFileChannel, final SeekableByteChannel metadataFileChannel) throws IOException {
if (ALWAYS_ALLOCATE_NEW) {
return new FileAllocator(dataFileChannel);
} else {
Long2IntMap freeBytes = new Long2IntRBTreeMap();
Long2IntMap usedBytes = new Long2IntRBTreeMap();
long firstOffset = 0;
metadataFileChannel.position(0);
while (metadataFileChannel.position() + IndexDetails.TOTAL_BYTES <= getMetadataFileChannelSize()) {
IndexDetails indexDetails = readIndexDetailsAt(metadataFileChannel);
if (indexDetails != null) {
long offset = indexDetails.getOffset();
if (!usedBytes.containsKey(offset) || indexDetails.getSize() > usedBytes.get(offset)) {
usedBytes.put(offset, indexDetails.getSize());
}
if (offset < firstOffset) {
firstOffset = offset;
}
}
}
long previousEntryOffset = 0;
long previousEntrySize = 0;
ObjectIterator<Long2IntMap.Entry> it = usedBytes.long2IntEntrySet().iterator();
while (it.hasNext()) {
final Long2IntMap.Entry entry = it.next();
final long entryOffset = entry.getLongKey();
final long entrySize = entry.getIntValue();
it.remove();
if (previousEntryOffset + previousEntrySize < entryOffset) {
freeBytes.put(previousEntryOffset + previousEntrySize, (int) (entryOffset - (previousEntryOffset + previousEntrySize)));
}
previousEntryOffset = entryOffset;
previousEntrySize = entrySize;
}
final long fileSize = previousEntryOffset + previousEntrySize;
return new FileAllocator(dataFileChannel, fileSize, freeBytes);
}
}
@Override
public <T> T get(long index, DBReader<T> reader) throws IOException {
checkClosed();
IndexDetails details = getIndexMetadata(index);
Input i = new Input(Channels.newInputStream(dataFileChannel.position(details.getOffset())), details.getSize());
T result = reader.read(i, details.getSize());
return result;
}
@Override
public IndexDetails set(long index, int size, DBWriter data) throws IOException {
checkClosed();
IndexDetails indexDetails = getIndexMetadataUnsafe(index);
if (ALWAYS_ALLOCATE_NEW || indexDetails == null || indexDetails.getSize() < size) {
// Allocate new space
IndexDetails newDetails = allocateAndWrite(index, size, data);
if (indexDetails != null) {
// Mark free the old bytes
fileAllocator.markFree(indexDetails.getOffset(), indexDetails.getSize());
}
return newDetails;
} else {
// Check if size changed
if (size < indexDetails.getSize()) {
// Mark free the unused bytes
fileAllocator.markFree(indexDetails.getOffset() + size, size);
}
// Update index details
indexDetails = editIndex(index, indexDetails, indexDetails.getOffset(), size);
// Write data
writeExact(indexDetails, size, data);
// Before returning, return IndexDetails
return indexDetails;
}
}
@Override
public long add(int size) {
checkClosed();
final long offset = fileAllocator.allocate(size);
final IndexDetails indexDetails = new IndexDetails(offset, size);
final long index = createIndexMetadata(indexDetails);
return index;
}
@Override
public long add(int size, DBWriter data) throws IOException {
checkClosed();
final long offset = fileAllocator.allocate(size);
final IndexDetails indexDetails = new IndexDetails(offset, size);
final long index = createIndexMetadata(indexDetails);
writeExact(indexDetails, size, data);
return index;
}
@Override
public FullIndexDetails addAndGetDetails(int size, DBWriter data) throws IOException {
checkClosed();
final long offset = fileAllocator.allocate(size);
final IndexDetails indexDetails = new IndexDetails(offset, size);
final long index = createIndexMetadata(indexDetails);
writeExact(indexDetails, size, data);
return new FullIndexDetails(index, indexDetails);
}
/**
* Write the data at index.
* The input size must be equal to the index size!
*/
private void writeExact(final IndexDetails indexDetails, int size, DBWriter data) throws IOException {
if (indexDetails.getSize() != size) {
throw new IOException("Unable to write " + size + " in a space of " + indexDetails.getSize());
}
final long offset = indexDetails.getOffset();
OutputStream os = Channels.newOutputStream(dataFileChannel.position(offset));
final Output o = new Output(os, size);
data.write(o);
os.flush();
o.flush();
}
private IndexDetails allocateAndWrite(final long index, int size, DBWriter w) throws IOException {
final long offset = fileAllocator.allocate(size);
IndexDetails details = editIndex(index, offset, size);
writeExact(details, size, w);
return details;
}
@Override
public void delete(long index) throws IOException {
checkClosed();
IndexDetails indexDetails = getIndexMetadataUnsafe(index);
if (indexDetails != null) {
fileAllocator.markFree(indexDetails.getOffset(), indexDetails.getSize());
}
synchronized (indicesMapsAccessLock) {
dirtyLoadedIndices.remove(index);
loadedIndices.remove(index);
removedIndices.add(index);
}
}
public void flushAndUnload(long index) throws IOException {
if (removedIndices.contains(index)) {
synchronized (indicesMapsAccessLock) {
removedIndices.remove(index);
dirtyLoadedIndices.remove(index);
loadedIndices.remove(index);
}
// Update indices metadata
SeekableByteChannel metadata = metadataFileChannel.position(index * IndexDetails.TOTAL_BYTES);
eraseIndexDetails(metadata);
}
boolean isDirty = false;
IndexDetails indexDetails = null;
synchronized (indicesMapsAccessLock) {
if (dirtyLoadedIndices.contains(index)) {
indexDetails = loadedIndices.get(index);
dirtyLoadedIndices.remove(index);
}
}
if (isDirty) {
// Update indices metadata
long position = index * IndexDetails.TOTAL_BYTES;
resizeMetadataFileChannel(position);
SeekableByteChannel metadata = metadataFileChannel.position(position);
writeIndexDetails(metadata, indexDetails);
}
synchronized (indicesMapsAccessLock) {
loadedIndices.remove(index);
}
}
@Override
public boolean has(long index) {
checkClosed();
try {
return getIndexMetadataUnsafe(index) != null;
} catch (IOException ex) {
ex.printStackTrace();
return false;
}
}
/**
* Edit index data if a change is detected
* @param index
* @param oldData Old index data to check
* @param offset offset
* @param size size
* @return
*/
private IndexDetails editIndex(long index, IndexDetails oldData, long offset, int size) {
if (oldData.getOffset() != offset || oldData.getSize() != size) {
return editIndex(index, offset, size);
} else {
return oldData;
}
}
/**
* Edit index data
* @param index
* @param offset
* @param size
* @return
*/
private IndexDetails editIndex(long index, long offset, int size) {
IndexDetails indexDetails = new IndexDetails(offset, size);
editIndex(index, indexDetails);
return indexDetails;
}
/**
* Edit index data
* @param index
* @param details
*/
private void editIndex(long index, IndexDetails details) {
synchronized (indicesMapsAccessLock) {
loadedIndices.put(index, details);
dirtyLoadedIndices.add(index);
}
}
private long createIndexMetadata(IndexDetails indexDetails) {
synchronized (indicesMapsAccessLock) {
long newIndex = firstAllocableIndex++;
loadedIndices.put(newIndex, indexDetails);
dirtyLoadedIndices.add(newIndex);
removedIndices.remove(newIndex);
return newIndex;
}
}
private IndexDetails getIndexMetadataUnsafe(long index) throws IOException {
// Return index details if loaded
IndexDetails details;
synchronized (indicesMapsAccessLock) {
details = loadedIndices.getOrDefault(index, null);
}
if (details != null) return details;
// Try to load the details from file
final long metadataPosition = index * IndexDetails.TOTAL_BYTES;
if (metadataPosition + IndexDetails.TOTAL_BYTES > getMetadataFileChannelSize()) {
// Avoid underflow exception
return null;
}
SeekableByteChannel currentMetadataFileChannel = metadataFileChannel.position(metadataPosition);
IndexDetails indexDetails = readIndexDetailsAt(currentMetadataFileChannel);
if (indexDetails != null) {
editIndex(index, indexDetails);
return indexDetails;
}
// No results found. Returning null
return null;
}
private IndexDetails readIndexDetailsAt(SeekableByteChannel currentMetadataFileChannel) throws IOException {
IndexDetails indexDetails = null;
synchronized (metadataByteBufferLock) {
metadataByteBuffer.rewind();
currentMetadataFileChannel.read(metadataByteBuffer);
metadataByteBuffer.rewind();
// If it's not deleted continue
final long offset = metadataByteBuffer.getLong();
if (offset >= 0) { // If it's < 0 it means that the index has been deleted
final int size = metadataByteBuffer.getInt();
indexDetails = new IndexDetails(offset, size);
}
}
return indexDetails;
}
private IndexDetails getIndexMetadata(long index) throws IOException {
IndexDetails details = getIndexMetadataUnsafe(index);
if (details == null)
throw new IOException("Index " + index + " not found");
else
return details;
}
@Override
public void close() throws IOException {
if (closed) {
return;
}
synchronized (closeLock) {
if (closed) {
return;
}
closed = true;
}
// Update indices metadata
flushAllFlushableIndices();
// Remove removed indices
removeRemovedIndices();
fileAllocator.close();
}
private void writeIndexDetails(SeekableByteChannel position, IndexDetails indexDetails) throws IOException {
synchronized (metadataByteBufferLock) {
final int size = indexDetails.getSize();
final long offset = indexDetails.getOffset();
metadataByteBuffer.rewind();
metadataByteBuffer.putLong(offset);
metadataByteBuffer.putInt(size);
metadataByteBuffer.rewind();
position.write(metadataByteBuffer);
}
}
private void eraseIndexDetails(SeekableByteChannel position) throws IOException {
synchronized (maskByteBufferLock) {
maskByteBuffer.rewind();
maskByteBuffer.putLong(-1); // -1 = deleted
maskByteBuffer.rewind();
position.write(maskByteBuffer);
}
}
private void checkClosed() {
if (closed) {
throw new RuntimeException("Index Manager is closed.");
}
}
@Override
public long clean() {
long cleaned = 0;
long tim1 = System.currentTimeMillis();
try {
cleaned += flushAllFlushableIndices();
} catch (IOException ex) {
ex.printStackTrace();
}
long tim2 = System.currentTimeMillis();
try {
cleaned += removeRemovedIndices();
} catch (IOException ex) {
ex.printStackTrace();
}
long tim3 = System.currentTimeMillis();
cleaned += cleanExtraIndices();
long tim4 = System.currentTimeMillis();
if (Cleaner.ENABLE_CLEANER_LOGGING) System.out.println("[CLEANER] FileIndexManager CLEAN_TIME: " + (tim2-tim1) + "," + (tim3-tim2) + "," + (tim4-tim3));
return cleaned;
}
private long flushAllFlushableIndices() throws IOException {
long flushedIndices = 0;
SeekableByteChannel metadata = metadataFileChannel;
long lastIndex = -2;
synchronized (indicesMapsAccessLock) {
for (long index : dirtyLoadedIndices) {
IndexDetails indexDetails = loadedIndices.get(index);
long position = index * IndexDetails.TOTAL_BYTES;
resizeMetadataFileChannel(position);
if (index - lastIndex != 1) {
metadata = metadata.position(position);
}
writeIndexDetails(metadata, indexDetails);
lastIndex = index;
flushedIndices++;
}
dirtyLoadedIndices.clear();
}
return flushedIndices;
}
private void resizeMetadataFileChannel(long position) {
if (position + IndexDetails.TOTAL_BYTES > metadataFileChannelSize) {
metadataFileChannelSize = position + IndexDetails.TOTAL_BYTES;
}
}
private long removeRemovedIndices() throws IOException {
SeekableByteChannel metadata = metadataFileChannel;
synchronized (indicesMapsAccessLock) {
long removed = this.removedIndices.size();
for (long index : this.removedIndices) {
metadata = metadata.position(index * IndexDetails.TOTAL_BYTES);
eraseIndexDetails(metadata);
}
this.removedIndices.clear();
return removed;
}
}
private long cleanExtraIndices() {
long removedIndices = 0;
LongArrayList toUnload = new LongArrayList();
synchronized (indicesMapsAccessLock) {
if (loadedIndices.size() > DatabaseManager.MAX_LOADED_INDICES) {
long count = loadedIndices.size();
LongIterator it = loadedIndices.keySet().iterator();
while (it.hasNext()) {
long loadedIndex = it.nextLong();
if (count < DatabaseManager.MAX_LOADED_INDICES * 3l / 2l) {
break;
}
toUnload.add(loadedIndex);
removedIndices++;
count--;
}
}
}
for (long index : toUnload.elements()) {
try {
flushAndUnload(index);
} catch (IOException e) {
e.printStackTrace();
}
}
return removedIndices;
}
}

View File

@ -1,14 +0,0 @@
package org.warp.jcwdb;
public class FullIndexDetails extends IndexDetails {
private final long index;
public FullIndexDetails(long index, IndexDetails details) {
super(details);
this.index = index;
}
public long getIndex() {
return index;
}
}

View File

@ -1,62 +0,0 @@
package org.warp.jcwdb;
public class IndexDetails {
/**
* The bitmask is used to determine if an index has been deleted
*/
public static final int OFFSET_BYTES = Long.BYTES;
public static final int DATA_SIZE_BYTES = Integer.BYTES;
public static final int TOTAL_BYTES = OFFSET_BYTES + DATA_SIZE_BYTES;
private final long offset;
private final int size;
public IndexDetails(long offset, int size) {
this.offset = offset;
this.size = size;
}
public IndexDetails(IndexDetails indexDetails) {
this.offset = indexDetails.offset;
this.size = indexDetails.size;
}
public long getOffset() {
return offset;
}
public int getSize() {
return size;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (offset ^ (offset >>> 32));
result = prime * result + size;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
IndexDetails other = (IndexDetails) obj;
if (offset != other.offset)
return false;
if (size != other.size)
return false;
return true;
}
@Override
public String toString() {
return "IndexDetails [offset=" + offset + ", size=" + size + "]";
}
}

View File

@ -1,14 +0,0 @@
package org.warp.jcwdb;
import java.io.IOException;
public interface IndexManager extends Cleanable {
<T> T get(long index, DBReader<T> reader) throws IOException;
long add(int size);
long add(int size, DBWriter writer) throws IOException;
FullIndexDetails addAndGetDetails(int size, DBWriter writer) throws IOException;
IndexDetails set(long index, int size, DBWriter writer) throws IOException;
void delete(long index) throws IOException;
boolean has(long index);
void close() throws IOException;
}

View File

@ -1,14 +0,0 @@
package org.warp.jcwdb;
public class NoParserFoundException extends NullPointerException {
public NoParserFoundException(String string) {
super(string);
}
/**
*
*/
private static final long serialVersionUID = 701010818132241139L;
}

View File

@ -1,6 +0,0 @@
package org.warp.jcwdb;
public interface Saveable {
void save();
void saveAndFlush();
}

View File

@ -1,28 +0,0 @@
package org.warp.jcwdb.ann;
import java.io.IOException;
public class DBDBObjectList<T extends DBObject> extends DBArrayList<T> {
@DBField(id = 1, type = DBDataType.OBJECT)
private Class<T> type;
public DBDBObjectList() {
super();
}
public DBDBObjectList(JCWDatabase database, Class<T> type) throws IOException {
super(database);
this.type = type;
}
@Override
protected T loadItem(long uid) throws IOException {
return database.getDataLoader().loadDBObject(type, uid);
}
@Override
protected void writeItemToDisk(long uid, T item) throws IOException {
database.getDataLoader().writeObjectProperty(uid, DBDataType.DATABASE_OBJECT, item);
}
}

View File

@ -9,6 +9,5 @@ public enum DBDataType {
CHAR,
INTEGER,
LONG,
UID_LIST,
REFERENCES_LIST
}

View File

@ -1,137 +0,0 @@
package org.warp.jcwdb.ann;
import org.apache.commons.lang3.reflect.MethodUtils;
import java.io.IOError;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
public abstract class DBObject {
protected JCWDatabase database;
private Field[] fields;
private DBDataType[] fieldTypes;
private long[] fieldUIDs;
private Method[] propertyGetters;
private Method[] propertySetters;
private DBDataType[] propertyTypes;
private long[] propertyUIDs;
private boolean[] loadedProperties;
private Object[] loadedPropertyValues;
private Map<String, DBPropertySetter> setterMethods;
private Map<String, DBPropertyGetter> getterMethods;
private final Object fieldsAccessLock = new Object();
private final Object propertiesAccessLock = new Object();
public DBObject() {
}
public DBObject(JCWDatabase database) throws IOException {
this.database = database;
database.initializeDBObject(this);
}
public abstract void initialize() throws IOException;
public <T> T getProperty() {
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null));
try {
int propertyId = stackFrame.getDeclaringClass().getDeclaredMethod(stackFrame.getMethodName()).getAnnotation(DBPropertyGetter.class).id();
return getProperty(propertyId);
} catch (IOException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
<T> void setLoadedProperty(int propertyId, T value) {
loadedPropertyValues[propertyId] = value;
loadedProperties[propertyId] = true;
}
@SuppressWarnings("unchecked")
private <T> T getProperty(int propertyId) throws IOException {
synchronized (propertiesAccessLock) {
if (!loadedProperties[propertyId]) {
long propertyUID = propertyUIDs[propertyId];
database.getDataLoader().loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID);
}
return (T) loadedPropertyValues[propertyId];
}
}
public <T> void setProperty(T value) {
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null));
DBPropertySetter propertyAnnotation = setterMethods.get(stackFrame.getMethodName());
setProperty(propertyAnnotation.id(), propertyAnnotation.type(), value);
}
public <T> void setProperty(int propertyId, DBDataType propertyType, T value) {
synchronized (propertiesAccessLock) {
loadedPropertyValues[propertyId] = value;
loadedProperties[propertyId] = true;
}
}
public void writeToDisk(long uid) {
//System.err.println("Saving object " + uid + ":" + this);
try {
synchronized (propertiesAccessLock) {
synchronized (fieldsAccessLock) {
database.getDataLoader().writeObjectInfo(uid, fieldUIDs, propertyUIDs);
}
}
synchronized (fieldsAccessLock) {
for (int i = 0; i < fieldUIDs.length; i++) {
try {
database.getDataLoader().writeObjectProperty(fieldUIDs[i], fieldTypes[i], fields[i].get(this));
} catch (IllegalAccessException e) {
throw new IOError(e);
}
}
}
synchronized (propertiesAccessLock) {
for (int i = 0; i < propertyUIDs.length; i++) {
database.getDataLoader().writeObjectProperty(propertyUIDs[i], propertyTypes[i], loadedPropertyValues[i]);
}
}
} catch (IOException e) {
throw new IOError(e);
}
}
public final void setFields(Field[] fields, DBDataType[] fieldTypes, long[] fieldUIDs) {
synchronized (fieldsAccessLock) {
this.fields = fields;
this.fieldTypes = fieldTypes;
this.fieldUIDs = fieldUIDs;
}
}
public final void setProperties(Method[] propertyGetters, Method[] propertySetters, DBDataType[] propertyTypes, long[] propertyUIDs, Map<String, DBPropertySetter> setterMethods, Map<String, DBPropertyGetter> getterMethods) {
synchronized (propertiesAccessLock) {
this.propertyGetters = propertyGetters;
this.propertySetters = propertySetters;
this.propertyTypes = propertyTypes;
this.propertyUIDs = propertyUIDs;
this.loadedProperties = new boolean[this.propertyUIDs.length];
this.loadedPropertyValues = new Object[this.propertyUIDs.length];
this.setterMethods = setterMethods;
this.getterMethods = getterMethods;
}
}
Method[] getPropertyGetters() {
return MethodUtils.getMethodsWithAnnotation(this.getClass(), DBPropertyGetter.class);
}
public Method[] getPropertySetters() {
return MethodUtils.getMethodsWithAnnotation(this.getClass(), DBPropertySetter.class);
}
}

View File

@ -1,87 +0,0 @@
package org.warp.jcwdb.ann;
import org.warp.jcwdb.FileIndexManager;
import java.io.IOError;
import java.io.IOException;
public class DBObjectIndicesManager {
private final FileIndexManager indices;
DBObjectIndicesManager(FileIndexManager indices) {
this.indices = indices;
}
public long allocate(int fieldsCount, int propertiesCount) {
long uid = indices.add(calculateObjectSize(fieldsCount, propertiesCount));
//System.err.println("ALLOCATED UID " + uid);
return uid;
}
public void setNull(long uid) throws IOException {
indices.set(uid, 0, (w) -> w.write(new byte[0]));
}
public void set(long uid, long[] fields, long[] properties) throws IOException {
indices.set(uid, calculateObjectSize(fields, properties), (w) -> {
w.writeInt(fields.length);
w.writeInt(properties.length);
for (int i = 0; i < fields.length; i++) {
w.writeLong(fields[i]);
}
for (int i = 0; i < properties.length; i++) {
w.writeLong(properties[i]);
}
});
}
public DBObjectInfo get(long uid) throws IOException {
return indices.get(uid, (i, size) -> {
if (size < Integer.BYTES * 2) {
return null;
}
long[] indices = new long[i.readInt()];
long[] properties = new long[i.readInt()];
if (size != calculateObjectSize(indices, properties)) {
throw new IOError(new IOException("The size of the object is different!"));
}
for (int indicesI = 0; indicesI < indices.length; indicesI++) {
indices[indicesI] = i.readLong();
}
for (int propertiesI = 0; propertiesI < properties.length; propertiesI++) {
properties[propertiesI] = i.readLong();
}
return new DBObjectInfo(indices, properties);
});
}
public boolean has(long uid) {
return indices.has(uid);
}
private int calculateObjectSize(long[] fields, long[] properties) {
return calculateObjectSize(fields.length, properties.length);
}
private int calculateObjectSize(int fieldsCount, int propertiesCount) {
return Integer.BYTES * 2 + (fieldsCount + propertiesCount) * Long.BYTES;
}
public class DBObjectInfo {
private final long[] fields;
private final long[] properties;
public DBObjectInfo(long[] fields, long[] properties) {
this.fields = fields;
this.properties = properties;
}
public long[] getFields() {
return fields;
}
public long[] getProperties() {
return properties;
}
}
}

View File

@ -1,24 +0,0 @@
package org.warp.jcwdb.ann;
import java.io.IOException;
public class DBObjectList<T> extends DBArrayList<T> {
public DBObjectList() {
}
public DBObjectList(JCWDatabase database) throws IOException {
super(database);
}
@Override
public T loadItem(long uid) throws IOException {
return database.getDataLoader().loadObject(uid);
}
@Override
public void writeItemToDisk(long uid, T item) throws IOException {
database.getDataLoader().writeObjectProperty(uid, DBDataType.OBJECT, item);
}
}

View File

@ -1,96 +0,0 @@
package org.warp.jcwdb.ann;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
public class DataInitializer {
private final DataLoader dataLoader;
public DataInitializer(DataLoader dataLoader) {
this.dataLoader = dataLoader;
}
public void initializeDBObject(DBObject obj) throws IOException {
initializeDBObjectFields(obj);
initializeDBObjectProperties(obj);
obj.initialize();
}
private void initializeDBObjectFields(DBObject obj) throws IOException {
// Declare the variables needed to get the biggest field Id
Field[] unorderedFields = dataLoader.getFields(obj);
// Find the biggest field Id
int biggestFieldId = dataLoader.getBiggestFieldId(unorderedFields);
// Allocate new UIDs
long[] fieldUIDs = dataLoader.allocateNewUIDs(biggestFieldId + 1);
// Declare the other variables
Field[] fields = new Field[biggestFieldId + 1];
DBDataType[] orderedFieldTypes = new DBDataType[biggestFieldId + 1];
// Load all fields metadata and load them
for (Field field : unorderedFields) {
DBField fieldAnnotation = field.getAnnotation(DBField.class);
int fieldId = fieldAnnotation.id();
DBDataType fieldType = fieldAnnotation.type();
dataLoader.loadField(obj, field, fieldType, fieldUIDs[fieldId]);
fields[fieldId] = field;
orderedFieldTypes[fieldId] = fieldType;
}
// Set fields metadata
obj.setFields(fields, orderedFieldTypes, fieldUIDs);
}
private void initializeDBObjectProperties(DBObject obj) {
// Declare the variables needed to get the biggest property Id
Method[] unorderedPropertyGetters = obj.getPropertyGetters();
Method[] unorderedPropertySetters = obj.getPropertySetters();
// Find the biggest property Id
int biggestGetter = dataLoader.getBiggestPropertyGetterId(unorderedPropertyGetters);
int biggestSetter = dataLoader.getBiggestPropertySetterId(unorderedPropertySetters);
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
// Allocate new UIDs
long[] propertyUIDs = dataLoader.allocateNewUIDs(biggestPropertyId + 1);
for (Method property : unorderedPropertySetters) {
DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
int propertyId = fieldAnnotation.id();
if (propertyId > biggestPropertyId) {
biggestPropertyId = propertyId;
}
}
// Declare the other variables
DBDataType[] propertyTypes = new DBDataType[biggestPropertyId + 1];
Method[] propertyGetters = new Method[biggestPropertyId + 1];
Method[] propertySetters = new Method[biggestPropertyId + 1];
Map<String, DBPropertySetter> setterMethods = new LinkedHashMap<>();
Map<String, DBPropertyGetter> getterMethods = new LinkedHashMap<>();
// Load the properties metadata
for (Method property : unorderedPropertyGetters) {
DBPropertyGetter propertyAnnotation = property.getAnnotation(DBPropertyGetter.class);
int propertyId = propertyAnnotation.id();
DBDataType propertyType = propertyAnnotation.type();
propertyTypes[propertyId] = propertyType;
propertyGetters[propertyId] = property;
getterMethods.put(property.getName(), propertyAnnotation);
}
for (Method property : unorderedPropertySetters) {
DBPropertySetter propertyAnnotation = property.getAnnotation(DBPropertySetter.class);
int propertyId = propertyAnnotation.id();
DBDataType propertyType = propertyAnnotation.type();
propertyTypes[propertyId] = propertyType;
propertySetters[propertyId] = property;
setterMethods.put(property.getName(), propertyAnnotation);
}
// Set properties metadata
obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs, setterMethods, getterMethods);
}
}

View File

@ -1,645 +0,0 @@
package org.warp.jcwdb.ann;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Output;
import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
import it.unimi.dsi.fastutil.chars.CharArrayList;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.warp.jcwdb.FileIndexManager;
import java.io.ByteArrayOutputStream;
import java.io.IOError;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class DataLoader {
private final Kryo kryo = new Kryo();
private final DBObjectIndicesManager objectIndicesManager;
private final FileIndexManager indices;
private final Object indicesAccessLock = new Object();
private volatile boolean closed;
/**
* DO NOT USE
*/
private JCWDatabase databaseInstance;
public DataLoader(JCWDatabase databaseInstance, Path dataFile, Path metadataFile, boolean registrationRequired) throws IOException {
synchronized (indicesAccessLock) {
this.databaseInstance = databaseInstance;
this.indices = new FileIndexManager(dataFile, metadataFile);
if (!indices.has(0)) {
allocateNullValue();
}
this.objectIndicesManager = new DBObjectIndicesManager(this.indices);
kryo.setRegistrationRequired(registrationRequired);
registerDefaultClasses();
}
}
private void registerDefaultClasses() {
int id = -90;
registerClass(boolean[].class, id++);
registerClass(byte[].class, id++);
registerClass(short[].class, id++);
registerClass(char[].class, id++);
registerClass(int[].class, id++);
registerClass(long[].class, id++);
registerClass(Boolean[].class, id++);
registerClass(Byte[].class, id++);
registerClass(Short[].class, id++);
registerClass(Character[].class, id++);
registerClass(Integer[].class, id++);
registerClass(Long[].class, id++);
registerClass(String.class, id++);
registerClass(String[].class, id++);
registerClass(Boolean.class, id++);
registerClass(Byte.class, id++);
registerClass(Short.class, id++);
registerClass(Character.class, id++);
registerClass(Integer.class, id++);
registerClass(Class.class, id++);
registerClass(Object.class, id++);
registerClass(Object[].class, id++);
registerClass(Long.class, id++);
registerClass(String.class, id++);
registerClass(String[].class, id++);
registerClass(boolean[][].class, id++);
registerClass(byte[][].class, id++);
registerClass(short[][].class, id++);
registerClass(char[][].class, id++);
registerClass(int[][].class, id++);
registerClass(long[][].class, id++);
registerClass(String[][].class, id++);
registerClass(List.class, id++);
registerClass(ArrayList.class, id++);
registerClass(LinkedList.class, id++);
registerClass(Set.class, id++);
registerClass(HashSet.class, id++);
registerClass(LinkedHashSet.class, id++);
registerClass(Map.class, id++);
registerClass(HashMap.class, id++);
registerClass(LinkedHashMap.class, id++);
registerClass(TreeMap.class, id++);
registerClass(BooleanArrayList.class, id++);
registerClass(ByteArrayList.class, id++);
registerClass(ShortArrayList.class, id++);
registerClass(CharArrayList.class, id++);
registerClass(IntArrayList.class, id++);
registerClass(LongArrayList.class, id++);
registerClass(TreeSet.class, id++);
registerClass(SortedSet.class, id++);
registerClass(SortedMap.class, id++);
}
public void close() throws IOException {
synchronized (indicesAccessLock) {
if (!closed) {
closed = true;
indices.close();
}
}
}
public void preloadDBObject(DBObject obj, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException {
synchronized (indicesAccessLock) {
preloadDBObjectFields(obj, objectInfo.getFields());
preloadDBObjectProperties(obj, objectInfo.getProperties());
}
}
<T extends DBObject> T loadRoot(Class<T> rootType, SupplierWithIO<T> ifAbsent) throws IOException {
synchronized (indicesAccessLock) {
if (isDBObjectNull(0)) {
return ifAbsent.getWithIO();
} else {
return loadDBObject(rootType, 0);
}
}
}
private <T extends DBObject> T instantiateDBObject(Class<T> type) throws IOException {
synchronized (indicesAccessLock) {
try {
T obj = type.getConstructor().newInstance();
obj.database = databaseInstance;
return obj;
} catch (NoSuchMethodException e) {
throw new IOException("You must declare a public empty constructor in class " + type + ": public " + type.getSimpleName() + "()", e);
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
throw new IOException(e);
}
}
}
private void preloadDBObjectFields(DBObject obj, long[] fieldUIDs) throws IOException {
synchronized (indicesAccessLock) {
// Declare the variables needed to get the biggest field Id
Field[] unorderedFields = getFields(obj);
// Find the biggest field Id
int biggestFieldId = getBiggestFieldId(unorderedFields);
// Declare the other variables
Field[] fields = new Field[biggestFieldId + 1];
DBDataType[] orderedFieldTypes = new DBDataType[biggestFieldId + 1];
// Load all fields metadata and load them
for (Field field : unorderedFields) {
DBField fieldAnnotation = field.getAnnotation(DBField.class);
int fieldId = fieldAnnotation.id();
DBDataType fieldType = fieldAnnotation.type();
loadField(obj, field, fieldType, fieldUIDs[fieldId]);
fields[fieldId] = field;
orderedFieldTypes[fieldId] = fieldType;
}
// Set fields metadata
obj.setFields(fields, orderedFieldTypes, fieldUIDs);
}
}
private void preloadDBObjectProperties(DBObject obj, long[] propertyUIDs) {
synchronized (indicesAccessLock) {
// Declare the variables needed to get the biggest property Id
Method[] unorderedPropertyGetters = obj.getPropertyGetters();
Method[] unorderedPropertySetters = obj.getPropertySetters();
// Find the biggest property Id
int biggestGetter = getBiggestPropertyGetterId(unorderedPropertyGetters);
int biggestSetter = getBiggestPropertySetterId(unorderedPropertySetters);
int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter;
for (Method property : unorderedPropertySetters) {
DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
int propertyId = fieldAnnotation.id();
if (propertyId > biggestPropertyId) {
biggestPropertyId = propertyId;
}
}
// Declare the other variables
DBDataType[] propertyTypes = new DBDataType[biggestPropertyId + 1];
Method[] propertyGetters = new Method[biggestPropertyId + 1];
Method[] propertySetters = new Method[biggestPropertyId + 1];
Map<String, DBPropertySetter> setterMethods = new LinkedHashMap<>();
Map<String, DBPropertyGetter> getterMethods = new LinkedHashMap<>();
// Load the properties metadata
for (Method property : unorderedPropertyGetters) {
DBPropertyGetter propertyAnnotation = property.getAnnotation(DBPropertyGetter.class);
int propertyId = propertyAnnotation.id();
DBDataType propertyType = propertyAnnotation.type();
propertyTypes[propertyId] = propertyType;
propertyGetters[propertyId] = property;
getterMethods.put(property.getName(), propertyAnnotation);
}
for (Method property : unorderedPropertySetters) {
DBPropertySetter propertyAnnotation = property.getAnnotation(DBPropertySetter.class);
int propertyId = propertyAnnotation.id();
DBDataType propertyType = propertyAnnotation.type();
propertyTypes[propertyId] = propertyType;
propertySetters[propertyId] = property;
setterMethods.put(property.getName(), propertyAnnotation);
}
// Set properties metadata
obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs, setterMethods, getterMethods);
}
}
protected Field[] getFields(DBObject obj) {
synchronized (indicesAccessLock) {
return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class);
}
}
int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) {
synchronized (indicesAccessLock) {
int biggestPropertyId = -1;
for (Method property : unorderedPropertyGetters) {
DBPropertyGetter fieldAnnotation = property.getAnnotation(DBPropertyGetter.class);
int propertyId = fieldAnnotation.id();
if (propertyId > biggestPropertyId) {
biggestPropertyId = propertyId;
}
}
return biggestPropertyId;
}
}
int getBiggestPropertySetterId(Method[] unorderedPropertySetters) {
synchronized (indicesAccessLock) {
int biggestPropertyId = -1;
for (Method property : unorderedPropertySetters) {
DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class);
int propertyId = fieldAnnotation.id();
if (propertyId > biggestPropertyId) {
biggestPropertyId = propertyId;
}
}
return biggestPropertyId;
}
}
protected int getBiggestFieldId(Field[] unorderedFields) {
synchronized (indicesAccessLock) {
int biggestFieldId = -1;
for (Field field : unorderedFields) {
DBField fieldAnnotation = field.getAnnotation(DBField.class);
int propertyId = fieldAnnotation.id();
if (propertyId > biggestFieldId) {
biggestFieldId = propertyId;
}
}
return biggestFieldId;
}
}
public void loadProperty(DBObject obj, int propertyId, Method property, DBDataType propertyType, long propertyUID) throws IOException {
synchronized (indicesAccessLock) {
loadData(propertyType, propertyUID, property::getReturnType, (data) -> {
synchronized (indicesAccessLock) {
obj.setLoadedProperty(propertyId, data);
}
});
}
}
public void loadField(DBObject obj, Field field, DBDataType fieldType, long fieldUID) throws IOException {
synchronized (indicesAccessLock) {
loadData(fieldType, fieldUID, field::getType, (data) -> {
synchronized (indicesAccessLock) {
try {
if (fieldType == DBDataType.OBJECT && data != null) {
if (!field.getType().isInstance(data)) {
throw new IOException("There is an attempt to load an object of type " + data.getClass() + " into a field of type " + field.getType());
}
}
FieldUtils.writeField(field, obj, data, true);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
});
}
}
@SuppressWarnings("unchecked")
private void loadData(DBDataType propertyType, long dataUID, Supplier<Class<?>> returnType, ConsumerWithIO result) throws IOException {
synchronized (indicesAccessLock) {
switch (propertyType) {
case DATABASE_OBJECT:
DBObject fieldDBObjectValue = loadDBObject((Class<? extends DBObject>) returnType.get(), dataUID);
//System.err.println("Loading data DBObj " + dataUID + ":" + fieldDBObjectValue);
result.accept(fieldDBObjectValue);
return;
case OBJECT:
Object fieldObjectValue = loadObject(dataUID);
//System.err.println("Loading data Obj " + dataUID + ":" + fieldObjectValue);
result.accept(fieldObjectValue);
return;
case UID_LIST:
LongArrayList fieldListObjectValue = loadListObject(dataUID);
//System.err.println("Loading data LOb " + dataUID + ":" + fieldListObjectValue);
result.accept(fieldListObjectValue);
return;
case BOOLEAN:
boolean fieldBooleanValue = loadBoolean(dataUID);
//System.err.println("Loading data Boo " + dataUID + ":" + fieldBooleanValue);
result.accept(fieldBooleanValue);
return;
case BYTE:
byte fieldByteValue = loadByte(dataUID);
//System.err.println("Loading data Byt " + dataUID + ":" + fieldByteValue);
result.accept(fieldByteValue);
return;
case SHORT:
short fieldShortValue = loadShort(dataUID);
//System.err.println("Loading data Shr " + dataUID + ":" + fieldShortValue);
result.accept(fieldShortValue);
return;
case CHAR:
char fieldCharValue = loadChar(dataUID);
//System.err.println("Loading data Chr " + dataUID + ":" + fieldCharValue);
result.accept(fieldCharValue);
return;
case INTEGER:
int fieldIntValue = loadInt(dataUID);
//System.err.println("Loading data Int " + dataUID + ":" + fieldIntValue);
result.accept(fieldIntValue);
return;
case LONG:
long fieldLongValue = loadLong(dataUID);
//System.err.println("Loading data Lng " + dataUID + ":" + fieldLongValue);
result.accept(fieldLongValue);
return;
default:
throw new NullPointerException("Unknown data type");
}
}
}
public <T extends DBObject> T loadDBObject(Class<T> type, long propertyUID) throws IOException {
synchronized (indicesAccessLock) {
DBObjectIndicesManager.DBObjectInfo objectInfo = readUIDs(propertyUID);
if (objectInfo == null) return null;
T obj = instantiateDBObject(type);
preloadDBObject(obj, objectInfo);
return obj;
}
}
private boolean isDBObjectNull(long uid) {
synchronized (indicesAccessLock) {
try {
return !objectIndicesManager.has(uid) || objectIndicesManager.get(uid) == null;
} catch (IOException ex) {
throw new IOError(ex);
}
}
}
@SuppressWarnings("unchecked")
public <T> T loadObject(long uid) throws IOException {
synchronized (indicesAccessLock) {
return indices.get(uid, (i, size) -> {
synchronized (indicesAccessLock) {
if (size != 0) {
return (T) kryo.readClassAndObject(i);
} else {
return null;
}
}
});
}
}
private LongArrayList loadListObject(long uid) throws IOException {
synchronized (indicesAccessLock) {
return indices.get(uid, (i, size) -> {
synchronized (indicesAccessLock) {
if (size != 0) {
LongArrayList list = new LongArrayList();
int listSize = i.readVarInt(true);
for (int li = 0; li < listSize; li++) {
list.add(i.readVarLong(true));
}
return list;
} else {
return null;
}
}
});
}
}
public boolean loadBoolean(long uid) throws IOException {
synchronized (indicesAccessLock) {
return indices.get(uid, (i, size) -> {
synchronized (indicesAccessLock) {
if (size != 0) {
return i.readBoolean();
} else {
return false;
}
}
});
}
}
public byte loadByte(long uid) throws IOException {
synchronized (indicesAccessLock) {
return indices.get(uid, (i, size) -> {
synchronized (indicesAccessLock) {
if (size != 0) {
return i.readByte();
} else {
return (byte) 0;
}
}
});
}
}
public short loadShort(long uid) throws IOException {
synchronized (indicesAccessLock) {
return indices.get(uid, (i, size) -> {
synchronized (indicesAccessLock) {
if (size != 0) {
return i.readShort();
} else {
return (short) 0;
}
}
});
}
}
public char loadChar(long uid) throws IOException {
synchronized (indicesAccessLock) {
return indices.get(uid, (i, size) -> {
synchronized (indicesAccessLock) {
if (size != 0) {
return i.readChar();
} else {
return (char) 0;
}
}
});
}
}
public int loadInt(long uid) throws IOException {
synchronized (indicesAccessLock) {
return indices.get(uid, (i, size) -> {
if (size != 0) {
return i.readInt();
} else {
return 0;
}
});
}
}
public long loadLong(long uid) throws IOException {
synchronized (indicesAccessLock) {
return indices.get(uid, (i, size) -> {
synchronized (indicesAccessLock) {
if (size != 0) {
return i.readLong();
} else {
return 0L;
}
}
});
}
}
public boolean exists(long uid) {
synchronized (indicesAccessLock) {
return objectIndicesManager.has(uid);
}
}
public void writeObjectInfo(long uid, long[] fieldUIDs, long[] propertyUIDs) throws IOException {
synchronized (indicesAccessLock) {
//System.err.println("Saving obj. " + uid);
this.objectIndicesManager.set(uid, fieldUIDs, propertyUIDs);
}
}
private void writeObjectInfoNull(long uid) throws IOException {
synchronized (indicesAccessLock) {
this.objectIndicesManager.setNull(uid);
}
}
/**
*
* @param uid
* @return
*/
public DBObjectIndicesManager.DBObjectInfo readUIDs(long uid) {
synchronized (indicesAccessLock) {
try {
return objectIndicesManager.get(uid);
} catch (IOException e) {
throw new IOError(e);
}
}
}
public <T> void writeObjectProperty(long uid, DBDataType propertyType, T loadedPropertyValue) throws IOException {
synchronized (indicesAccessLock) {
switch (propertyType) {
case BOOLEAN:
indices.set(uid, 1, (o) -> {
synchronized (indicesAccessLock) {
o.writeBoolean(loadedPropertyValue == null ? false : (boolean) loadedPropertyValue);
}
});
//System.err.println("Saving data Boo " + uid + ":" + loadedPropertyValue);
break;
case BYTE:
indices.set(uid, Byte.BYTES, (o) -> {
synchronized (indicesAccessLock) {
o.writeByte(loadedPropertyValue == null ? 0 : (byte) loadedPropertyValue);
}
});
//System.err.println("Saving data Byt " + uid + ":" + loadedPropertyValue);
break;
case SHORT:
indices.set(uid, Short.BYTES, (o) -> {
synchronized (indicesAccessLock) {
o.writeShort(loadedPropertyValue == null ? 0 : (short) loadedPropertyValue);
}
});
//System.err.println("Saving data Shr " + uid + ":" + loadedPropertyValue);
break;
case CHAR:
indices.set(uid, Character.BYTES, (o) -> {
synchronized (indicesAccessLock) {
o.writeChar(loadedPropertyValue == null ? 0 : (char) loadedPropertyValue);
}
});
//System.err.println("Saving data Chr " + uid + ":" + loadedPropertyValue);
break;
case INTEGER:
indices.set(uid, Integer.BYTES, (o) -> {
synchronized (indicesAccessLock) {
o.writeInt(loadedPropertyValue == null ? 0 : (int) loadedPropertyValue);
}
});
//System.err.println("Saving data Int " + uid + ":" + loadedPropertyValue);
break;
case LONG:
indices.set(uid, Long.BYTES, (o) -> {
synchronized (indicesAccessLock) {
o.writeLong(loadedPropertyValue == null ? 0 : (long) loadedPropertyValue);
}
});
//System.err.println("Saving data Lng " + uid + ":" + loadedPropertyValue);
break;
case OBJECT:
Output baosOutput = new Output(new ByteArrayOutputStream());
kryo.writeClassAndObject(baosOutput, loadedPropertyValue);
//System.err.println("Saving data Obj " + uid + ":" + loadedPropertyValue);
if (loadedPropertyValue instanceof Class) {
System.out.println();
}
byte[] out = baosOutput.toBytes();
indices.set(uid, out.length, o -> {
synchronized (indicesAccessLock) {
o.write(out, 0, out.length);
}
});
break;
case UID_LIST:
if (loadedPropertyValue == null) {
indices.set(uid, 0, (o) -> {
});
} else {
LongArrayList list = (LongArrayList) loadedPropertyValue;
final int listSize = list.size();
Output baosListOutput = new Output(Long.BYTES * 100, Long.BYTES * (listSize > 100 ? listSize : 100));
baosListOutput.writeVarInt(listSize, true);
for (int i = 0; i < listSize; i++) {
baosListOutput.writeVarLong(list.getLong(i), true);
}
//System.err.println("Saving data LOb " + uid + ":" + loadedPropertyValue);
byte[] outList = baosListOutput.toBytes();
indices.set(uid, outList.length, o -> {
synchronized (indicesAccessLock) {
o.write(outList, 0, outList.length);
}
});
}
break;
case DATABASE_OBJECT:
//System.err.println("Saving data DBObj " + uid + ":" + loadedPropertyValue);
if (loadedPropertyValue == null) {
writeObjectInfoNull(uid);
} else {
((DBObject) loadedPropertyValue).writeToDisk(uid);
}
break;
}
}
}
public void registerClass(Class<?> clazz, int id) {
synchronized (indicesAccessLock) {
kryo.register(clazz, 100 + id);
}
}
public long allocateNullValue() {
synchronized (indicesAccessLock) {
return indices.add(0);
}
}
public long[] allocateNewUIDs(int quantity) {
synchronized (indicesAccessLock) {
long[] ids = new long[quantity];
for (int i = 0; i < quantity; i++) {
ids[i] = allocateNullValue();
}
return ids;
}
}
}

View File

@ -1,54 +0,0 @@
package org.warp.jcwdb.ann;
import org.warp.jcwdb.Cleanable;
import org.warp.jcwdb.Cleaner;
import java.io.IOException;
import java.util.function.Supplier;
public class DatabaseManager implements Cleanable {
public static final long MAX_LOADED_INDICES = 100000;
private final Cleaner cleaner;
private final DataLoader dataLoader;
private DBObject loadedRootObject = null;
private volatile boolean closed;
DatabaseManager(DataLoader dataLoader) {
this.dataLoader = dataLoader;
this.cleaner = new Cleaner(this);
this.cleaner.start();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
DatabaseManager.this.close();
} catch (Exception e) {
e.printStackTrace();
}
}));
}
public <T extends DBObject> T loadRoot(Class<T> rootType, SupplierWithIO<T> ifAbsent) throws IOException {
if (loadedRootObject != null) {
throw new RuntimeException("Root already set!");
}
T root = dataLoader.loadRoot(rootType, ifAbsent);
loadedRootObject = root;
return root;
}
public void close() throws IOException {
if (!closed) {
closed = true;
DatabaseManager.this.cleaner.stop();
if (loadedRootObject != null) {
loadedRootObject.writeToDisk(0);
}
dataLoader.close();
}
}
@Override
public long clean() {
return 0;//indices.clean();
}
}

View File

@ -1,55 +0,0 @@
package org.warp.jcwdb.ann;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Path;
import java.util.function.Supplier;
public class JCWDatabase {
private final DatabaseManager database;
private final DataLoader dataLoader;
private final DataInitializer dataInitializer;
public JCWDatabase(Path dataFile, Path metadataFile, boolean registrationRequired) throws IOException {
this.dataLoader = new DataLoader(this, dataFile, metadataFile, registrationRequired);
this.dataInitializer = new DataInitializer(dataLoader);
this.database = new DatabaseManager(dataLoader);
}
public <T extends DBObject> T loadRoot(Class<T> rootClass) throws IOException {
return loadRoot(rootClass, () -> {
try {
return rootClass.getConstructor(JCWDatabase.class).newInstance(this);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new IOException(e);
}
});
}
public <T extends DBObject> T loadRoot(Class<T> rootClass, SupplierWithIO<T> ifAbsent) throws IOException {
return database.loadRoot(rootClass, ifAbsent);
}
public void registerClass(Class<?> clazz, int id) {
if (id < 0) {
throw new IllegalArgumentException();
}
dataLoader.registerClass(clazz, id);
}
public void close() throws IOException {
database.close();
}
public DataLoader getDataLoader() {
return dataLoader;
}
public DataInitializer getDataInitializer() {
return dataInitializer;
}
public void initializeDBObject(DBObject dbObject) throws IOException {
dataInitializer.initializeDBObject(dbObject);
}
}

View File

@ -1,83 +0,0 @@
package org.warp.jcwdb.tests;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.warp.jcwdb.ann.*;
import org.warp.jcwdb.utils.TestUtils;
import java.io.IOException;
import static org.junit.Assert.*;
public class DBDBObjectListTests {
private TestUtils.WrappedDb db;
private RootWithList root;
@Before
public void setUp() throws Exception {
db = TestUtils.wrapDb().create((db) -> {
db.get().registerClass(TestUtils.class, 0);
db.get().registerClass(TestUtils.RootClass.class, 1);
db.get().registerClass(Class.class, 2);
System.out.println("Loading root");
root = db.get().loadRoot(RootWithList.class);
});
root.list = new DBDBObjectList<>(db.get(), TestUtils.RootClass.class);
for (int i = 0; i < 100; i++) {
TestUtils.RootClass rootClass = new TestUtils.RootClass(db.get());
db.setRootClassValues(rootClass);
root.list.add(rootClass);
}
db.closeAndReopen();
for (int i = 0; i < 100; i++) {
TestUtils.RootClass rootClass = new TestUtils.RootClass(db.get());
db.setRootClassValues(rootClass);
root.list.add(rootClass);
}
}
@Test
public void shouldMatchList() throws IOException {
checkEmptyList();
assertEquals(200, root.list.size());
for (int i = 0; i < 200; i++) {
db.testRootClassValues(root.list.get(i));
}
}
private void checkEmptyList() throws IOException {
DBObjectList<String> list = new DBObjectList<>(db.get());
assertEquals(null, list.getLast());
assertEquals(0, list.size());
assertTrue(list.isEmpty());
list.add("1");
assertEquals(1, list.size());
assertEquals("1", list.getLast());
assertFalse(list.isEmpty());
}
@After
public void tearDown() throws Exception {
db.delete();
}
public static class RootWithList extends DBObject {
@DBField(id = 0, type = DBDataType.DATABASE_OBJECT)
public DBDBObjectList<TestUtils.RootClass> list;
public RootWithList() {
super();
}
public RootWithList(JCWDatabase database) throws IOException {
super(database);
}
@Override
public void initialize() throws IOException {
list = new DBDBObjectList<>(database, TestUtils.RootClass.class);
}
}
}

View File

@ -1,85 +0,0 @@
package org.warp.jcwdb.tests;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.warp.jcwdb.ann.*;
import org.warp.jcwdb.utils.TestUtils;
import java.io.IOException;
public class DBMultipleDBObjects {
private TestUtils.WrappedDb db;
private RootTwoClasses root;
@Before
public void setUp() throws Exception {
db = TestUtils.wrapDb().create((db) -> {
root = db.get().loadRoot(RootTwoClasses.class);
});
root.class1 = new TestUtils.RootClass(db.get());
db.setRootClassValues(root.class1);
root.class2 = new TestUtils.RootClass(db.get());
db.setRootClassValues(root.class2);
root.setClass3(new TestUtils.RootClass(db.get()));
db.setRootClassValues(root.getClass3());
root.setClass4(new TestUtils.RootClass(db.get()));
db.setRootClassValues(root.getClass4());
db.closeAndReopen();
}
@Test
public void shouldMatchMultipleObjects() {
db.testRootClassValues(root.class1);
db.testRootClassValues(root.class2);
db.testRootClassValues(root.getClass3());
db.testRootClassValues(root.getClass4());
}
@After
public void tearDown() throws Exception {
db.delete();
}
public static class RootTwoClasses extends DBObject {
@DBField(id = 0, type = DBDataType.DATABASE_OBJECT)
public TestUtils.RootClass class1;
@DBField(id = 1, type = DBDataType.DATABASE_OBJECT)
public TestUtils.RootClass class2;
public RootTwoClasses() {
super();
}
public RootTwoClasses(JCWDatabase database) throws IOException {
super(database);
}
@DBPropertyGetter(id = 0, type = DBDataType.DATABASE_OBJECT)
public TestUtils.RootClass getClass3() {
return getProperty();
}
@DBPropertySetter(id = 0, type = DBDataType.DATABASE_OBJECT)
public void setClass3(TestUtils.RootClass value) {
setProperty(value);
}
@DBPropertyGetter(id = 1, type = DBDataType.DATABASE_OBJECT)
public TestUtils.RootClass getClass4() {
return getProperty();
}
@DBPropertySetter(id = 1, type = DBDataType.DATABASE_OBJECT)
public void setClass4(TestUtils.RootClass value) {
setProperty(value);
}
@Override
public void initialize() throws IOException {
}
}
}

View File

@ -1,66 +0,0 @@
package org.warp.jcwdb.tests;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.warp.jcwdb.utils.NestedClass;
import org.warp.jcwdb.utils.TestUtils;
import static org.junit.Assert.*;
public class DBNestedDBObjects {
private TestUtils.WrappedDb db;
private NestedClass root;
@Before
public void setUp() throws Exception {
db = TestUtils.wrapDb().create((db) -> {
root = db.get().loadRoot(NestedClass.class);
});
root.child = new NestedClass(db.get());
root.child.child = new NestedClass(db.get());
root.child.child.child = new NestedClass(db.get());
root.child.child.child.child = new NestedClass(db.get());
root.child.child.child.child.isFinal = true;
NestedClass nestedClass0 = new NestedClass(db.get());
nestedClass0.isFinal = true;
NestedClass nestedClass1 = new NestedClass(db.get());
nestedClass1.setValue(nestedClass0);
NestedClass nestedClass2 = new NestedClass(db.get());
nestedClass2.setValue(nestedClass1);
NestedClass nestedClass3 = new NestedClass(db.get());
nestedClass3.setValue(nestedClass2);
NestedClass nestedClass4 = new NestedClass(db.get());
nestedClass4.setValue(nestedClass3);
root.setValue(nestedClass4);
db.closeAndReopen();
}
@Test
public void shouldMatchNestedObjects() {
assertNotNull(root);
assertNotNull(root.child);
assertNotNull(root.child.child);
assertNotNull(root.child.child.child);
assertNotNull(root.child.child.child.child);
assertTrue(root.child.child.child.child.isFinal);
assertNotNull(root.getValue());
assertFalse(root.isFinal);
assertNotNull(root.getValue().getValue());
assertFalse(root.getValue().isFinal);
assertNotNull(root.getValue().getValue().getValue());
assertFalse(root.getValue().getValue().isFinal);
assertNotNull(root.getValue().getValue().getValue().getValue());
assertFalse(root.getValue().getValue().getValue().isFinal);
assertNotNull(root.getValue().getValue().getValue().getValue().getValue());
assertFalse(root.getValue().getValue().getValue().getValue().isFinal);
assertTrue(root.getValue().getValue().getValue().getValue().getValue().isFinal);
}
@After
public void tearDown() throws Exception {
db.delete();
}
}

View File

@ -1,49 +0,0 @@
package org.warp.jcwdb.tests;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.warp.jcwdb.ann.DBObject;
import org.warp.jcwdb.ann.DBObjectIndicesManager;
import org.warp.jcwdb.ann.JCWDatabase;
import org.warp.jcwdb.utils.TestUtils;
import java.io.IOException;
import static org.junit.Assert.assertTrue;
public class DBRootCreation {
private TestUtils.WrappedDb db;
@Before
public void setUp() throws Exception {
db = TestUtils.wrapDb().create();
}
@Test
public void shouldCreateRoot() throws IOException {
RootClass root = db.get().loadRoot(RootClass.class);
assertTrue(root.test());
}
@After
public void tearDown() throws Exception {
db.delete();
}
public static class RootClass extends DBObject {
public RootClass(JCWDatabase database) throws IOException {
super(database);
}
public boolean test() {
return true;
}
@Override
public void initialize() throws IOException {
}
}
}

View File

@ -1,30 +0,0 @@
package org.warp.jcwdb.tests;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.warp.jcwdb.utils.TestUtils;
public class DBRootFields {
private TestUtils.WrappedDb db;
private TestUtils.RootClass root;
@Before
public void setUp() throws Exception {
db = TestUtils.wrapDb().create((db) -> {
root = db.get().loadRoot(TestUtils.RootClass.class);
});
db.setRootClassFields(root);
db.closeAndReopen();
}
@Test
public void shouldMatchAllFields() {
db.testRootClassFields(root);
}
@After
public void tearDown() throws Exception {
db.delete();
}
}

View File

@ -1,30 +0,0 @@
package org.warp.jcwdb.tests;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.warp.jcwdb.utils.TestUtils;
public class DBRootProperties {
private TestUtils.WrappedDb db;
private TestUtils.RootClass root;
@Before
public void setUp() throws Exception {
db = TestUtils.wrapDb().create((db) -> {
root = db.get().loadRoot(TestUtils.RootClass.class);
});
db.setRootClassProperties(root);
db.closeAndReopen();
}
@Test
public void shouldMatchAllProperties() {
db.testRootClassProperties(root);
}
@After
public void tearDown() throws Exception {
db.delete();
}
}

View File

@ -4,8 +4,6 @@ import org.warp.cowdb.Database;
import org.warp.cowdb.EnhancedObject;
import org.warp.jcwdb.ann.DBDataType;
import org.warp.jcwdb.ann.DBField;
import org.warp.jcwdb.ann.DBObject;
import org.warp.jcwdb.ann.JCWDatabase;
import java.io.IOException;

View File

@ -218,7 +218,7 @@ public class NTestUtils {
assertEquals("Test", val);
}
private void shouldGetDBObject(SimplestClass val) {
private void shouldGetDBObject(NSimplestClass val) {
assertNotNull(val);
assertTrue(val.field1);
}

View File

@ -1,37 +0,0 @@
package org.warp.jcwdb.utils;
import org.warp.jcwdb.ann.*;
import java.io.IOException;
public class NestedClass extends DBObject {
@DBField(id = 0, type = DBDataType.BOOLEAN)
public boolean isFinal;
@DBField(id = 1, type = DBDataType.DATABASE_OBJECT)
public NestedClass child;
public NestedClass() {
}
public NestedClass(JCWDatabase database) throws IOException {
super(database);
}
@DBPropertySetter(id = 0, type = DBDataType.DATABASE_OBJECT)
public void setValue(NestedClass value) {
setProperty(value);
}
@DBPropertyGetter(id = 0, type = DBDataType.DATABASE_OBJECT)
public NestedClass getValue() {
return getProperty();
}
@Override
public void initialize() throws IOException {
}
}

View File

@ -1,24 +0,0 @@
package org.warp.jcwdb.utils;
import org.warp.jcwdb.ann.*;
import java.io.IOException;
public class SimplestClass extends DBObject {
@DBField(id = 0, type = DBDataType.BOOLEAN)
public boolean field1;
public SimplestClass() {
}
public SimplestClass(JCWDatabase database) throws IOException {
super(database);
}
@Override
public void initialize() throws IOException {
field1 = true;
}
}

View File

@ -1,381 +0,0 @@
package org.warp.jcwdb.utils;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.warp.jcwdb.ann.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.function.Consumer;
import static org.junit.Assert.*;
public class TestUtils {
public static WrappedDb wrapDb() {
return new WrappedDb();
}
public static class WrappedDb {
private JCWDatabase db;
private Path tempDir;
private RunnableWithIO r;
private WrappedDb() {
}
public WrappedDb create() throws IOException {
tempDir = Files.createTempDirectory("tests-");
db = openDatabase();
if (r != null) {
r.run();
}
return this;
}
public WrappedDb create(ConsumerWithIO<WrappedDb> r) throws IOException {
this.r = () -> r.accept(WrappedDb.this);
this.create();
return this;
}
private JCWDatabase openDatabase() throws IOException {
return new JCWDatabase(tempDir.resolve(Paths.get("data.db")), tempDir.resolve(Paths.get("indices.idx")), true);
}
public void delete() throws IOException {
db.close();
deleteDir(tempDir);
}
public JCWDatabase get() {
return db;
}
private void deleteDir(Path p) throws IOException {
Files.walk(p)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
public void closeAndReopen() throws IOException {
db.close();
db = openDatabase();
r.run();
}
public void setRootClassValues(RootClass root) throws IOException {
setRootClassFields(root);
setRootClassProperties(root);
}
public void setRootClassFields(RootClass root) throws IOException {
root.field1 = true;
root.field2 = 2;
root.field3 = 3;
root.field4 = 4;
root.field5 = 5;
root.field6 = 6;
root.field7 = "Test";
root.field8 = new LongArrayList();
root.field8.add(0);
root.field8.add(1);
root.field8.add(2);
root.field8.add(Long.MAX_VALUE/2);
root.field8.add(Long.MIN_VALUE/2);
root.field8.add(Long.MAX_VALUE);
root.field8.add(Long.MIN_VALUE);
root.field9 = new SimplestClass(db);
root.field9.field1 = true;
}
public void setRootClassProperties(RootClass root) throws IOException {
root.set1(true);
root.set2((byte)2);
root.set3((short)3);
root.set4((char)4);
root.set5(5);
root.set6(6);
root.set7("Test");
LongArrayList lArrayList = new LongArrayList();
lArrayList.add(0);
lArrayList.add(1);
lArrayList.add(2);
lArrayList.add(Long.MAX_VALUE/2);
lArrayList.add(Long.MIN_VALUE/2);
lArrayList.add(Long.MAX_VALUE);
lArrayList.add(Long.MIN_VALUE);
root.set8(lArrayList);
SimplestClass simplestClass9 = new SimplestClass(db);
simplestClass9.field1 = true;
root.set9(simplestClass9);
}
public void testRootClassValues(RootClass root) {
testRootClassFields(root);
testRootClassProperties(root);
}
public void testRootClassFields(RootClass root) {
shouldGetFieldBoolean(root);
shouldGetFieldByte(root);
shouldGetFieldShort(root);
shouldGetFieldCharacter(root);
shouldGetFieldInteger(root);
shouldGetFieldLong(root);
shouldGetFieldObject(root);
shouldGetFieldUID(root);
shouldGetFieldDBObject(root);
}
public void testRootClassProperties(RootClass root) {
shouldGetPropertyBoolean(root);
shouldGetPropertyByte(root);
shouldGetPropertyShort(root);
shouldGetPropertyCharacter(root);
shouldGetPropertyInteger(root);
shouldGetPropertyLong(root);
shouldGetPropertyObject(root);
shouldGetPropertyUID(root);
shouldGetPropertyDBObject(root);
}
private void shouldGetFieldBoolean(RootClass root) {
assertTrue(root.field1);
}
private void shouldGetPropertyBoolean(RootClass root) {
assertTrue(root.get1());
}
private void shouldGetFieldByte(RootClass root) {
assertEquals(2, root.field2);
}
private void shouldGetPropertyByte(RootClass root) {
assertEquals(2, root.get2());
}
private void shouldGetFieldShort(RootClass root) {
assertEquals(3, root.field3);
}
private void shouldGetPropertyShort(RootClass root) {
assertEquals(3, root.get3());
}
private void shouldGetFieldCharacter(RootClass root) {
assertEquals(4, root.field4);
}
private void shouldGetPropertyCharacter(RootClass root) {
assertEquals(4, root.get4());
}
private void shouldGetFieldInteger(RootClass root) {
assertEquals(5, root.field5);
}
private void shouldGetPropertyInteger(RootClass root) {
assertEquals(5, root.get5());
}
private void shouldGetFieldLong(RootClass root) {
assertEquals(6, root.field6);
}
private void shouldGetPropertyLong(RootClass root) {
assertEquals(6, root.get6());
}
private void shouldGetFieldObject(RootClass root) {
shouldGetObject(root.field7);
}
private void shouldGetPropertyObject(RootClass root) {
shouldGetObject(root.get7());
}
private void shouldGetFieldDBObject(RootClass root) {
assertTrue(root.field9.field1);
}
private void shouldGetPropertyDBObject(RootClass root) {
assertTrue(root.get9().field1);
}
private void shouldGetObject(String val) {
assertNotNull(val);
assertEquals("Test", val);
}
private void shouldGetDBObject(SimplestClass val) {
assertNotNull(val);
assertTrue(val.field1);
}
private void shouldGetFieldUID(RootClass root) {
shouldGetUID(root.field8);
}
private void shouldGetPropertyUID(RootClass root) {
shouldGetUID(root.get8());
}
private void shouldGetUID(LongArrayList val) {
assertNotNull(val);
assertEquals(7, val.size());
assertEquals(0, val.getLong(0));
assertEquals(val.getLong(5), Long.MAX_VALUE);
assertEquals(val.getLong(6), Long.MIN_VALUE);
}
public void onLoad(RunnableWithIO r) {
this.r = r;
}
}
public static class RootClass extends DBObject {
@DBField(id = 0, type = DBDataType.BOOLEAN)
public boolean field1;
@DBField(id = 1, type = DBDataType.BYTE)
public byte field2;
@DBField(id = 2, type = DBDataType.SHORT)
public short field3;
@DBField(id = 3, type = DBDataType.CHAR)
public char field4;
@DBField(id = 4, type = DBDataType.INTEGER)
public int field5;
@DBField(id = 5, type = DBDataType.LONG)
public long field6;
@DBField(id = 6, type = DBDataType.OBJECT)
public String field7;
@DBField(id = 7, type = DBDataType.UID_LIST)
public LongArrayList field8;
@DBField(id = 8, type = DBDataType.DATABASE_OBJECT)
public SimplestClass field9;
public RootClass() {
}
public RootClass(JCWDatabase database) throws IOException {
super(database);
}
@DBPropertyGetter(id = 0, type = DBDataType.BOOLEAN)
public boolean get1() {
return getProperty();
}
@DBPropertyGetter(id = 1, type = DBDataType.BYTE)
public byte get2() {
return getProperty();
}
@DBPropertyGetter(id = 2, type = DBDataType.SHORT)
public short get3() {
return getProperty();
}
@DBPropertyGetter(id = 3, type = DBDataType.CHAR)
public char get4() {
return getProperty();
}
@DBPropertyGetter(id = 4, type = DBDataType.INTEGER)
public int get5() {
return getProperty();
}
@DBPropertyGetter(id = 5, type = DBDataType.LONG)
public long get6() {
return getProperty();
}
@DBPropertyGetter(id = 6, type = DBDataType.OBJECT)
public String get7() {
return getProperty();
}
@DBPropertyGetter(id = 7, type = DBDataType.UID_LIST)
public LongArrayList get8() {
return getProperty();
}
@DBPropertyGetter(id = 8, type = DBDataType.DATABASE_OBJECT)
public SimplestClass get9() {
return getProperty();
}
@DBPropertySetter(id = 0, type = DBDataType.BOOLEAN)
public void set1(boolean val) {
setProperty(val);
}
@DBPropertySetter(id = 1, type = DBDataType.BYTE)
public void set2(byte val) {
setProperty(val);
}
@DBPropertySetter(id = 2, type = DBDataType.SHORT)
public void set3(short val) {
setProperty(val);
}
@DBPropertySetter(id = 3, type = DBDataType.CHAR)
public void set4(char val) {
setProperty(val);
}
@DBPropertySetter(id = 4, type = DBDataType.INTEGER)
public void set5(int val) {
setProperty(val);
}
@DBPropertySetter(id = 5, type = DBDataType.LONG)
public void set6(long val) {
setProperty(val);
}
@DBPropertySetter(id = 6, type = DBDataType.OBJECT)
public void set7(String val) {
setProperty(val);
}
@DBPropertySetter(id = 7, type = DBDataType.UID_LIST)
public void set8(LongArrayList val) {
setProperty(val);
}
@DBPropertySetter(id = 8, type = DBDataType.DATABASE_OBJECT)
public void set9(SimplestClass val) {
setProperty(val);
}
public boolean test() {
return true;
}
@Override
public void initialize() throws IOException {
}
}
}