From 7123228027d324233381564ee4a39fba1093d1ba Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Thu, 10 Jan 2019 23:24:48 +0100 Subject: [PATCH 1/5] Reimplemented database using Reflection --- pom.xml | 6 + .../org/warp/jcwdb/CacheIndexManager.java | 73 --- .../java/org/warp/jcwdb/DBDataOutput.java | 34 - .../org/warp/jcwdb/DBGenericObjectParser.java | 58 -- .../warp/jcwdb/DBLightArrayListParser.java | 45 -- .../org/warp/jcwdb/DBLightBigListParser.java | 44 -- .../java/org/warp/jcwdb/DBStandardTypes.java | 24 - .../java/org/warp/jcwdb/DBStringParser.java | 27 - .../java/org/warp/jcwdb/DBTypeParser.java | 7 - .../java/org/warp/jcwdb/DBTypeParserImpl.java | 9 - .../org/warp/jcwdb/DBTypedObjectParser.java | 5 - src/main/java/org/warp/jcwdb/DBWriter.java | 2 +- src/main/java/org/warp/jcwdb/Editable.java | 60 -- .../java/org/warp/jcwdb/EntryReference.java | 266 -------- .../java/org/warp/jcwdb/FileIndexManager.java | 117 ++-- .../java/org/warp/jcwdb/IndexDetails.java | 30 +- .../java/org/warp/jcwdb/IndexManager.java | 11 +- src/main/java/org/warp/jcwdb/JCWDatabase.java | 197 ------ .../java/org/warp/jcwdb/LightArrayList.java | 455 ------------- .../java/org/warp/jcwdb/LightBigList.java | 607 ------------------ src/main/java/org/warp/jcwdb/LightList.java | 29 - .../org/warp/jcwdb/MixedIndexDatabase.java | 92 --- .../java/org/warp/jcwdb/TypesManager.java | 59 -- .../java/org/warp/jcwdb/VariableWrapper.java | 2 - src/main/java/org/warp/jcwdb/ann/DBClass.java | 9 + .../java/org/warp/jcwdb/ann/DBDataType.java | 7 + src/main/java/org/warp/jcwdb/ann/DBField.java | 13 + .../java/org/warp/jcwdb/ann/DBObject.java | 106 +++ .../jcwdb/ann/DBObjectIndicesManager.java | 74 +++ .../org/warp/jcwdb/ann/DBPropertyGetter.java | 13 + .../org/warp/jcwdb/ann/DBPropertySetter.java | 13 + .../java/org/warp/jcwdb/ann/Database.java | 275 ++++++++ .../warp/jcwdb/ann/exampleimpl/Class1.java | 40 ++ .../org/warp/jcwdb/ann/exampleimpl/Main.java | 32 + .../org/warp/jcwdb/exampleimpl/Animal.java | 9 - .../java/org/warp/jcwdb/exampleimpl/App.java | 101 --- .../java/org/warp/jcwdb/exampleimpl/Cat.java | 12 - .../java/org/warp/jcwdb/exampleimpl/Dog.java | 12 - .../warp/jcwdb/exampleimpl/StrangeAnimal.java | 15 - 39 files changed, 642 insertions(+), 2348 deletions(-) delete mode 100644 src/main/java/org/warp/jcwdb/CacheIndexManager.java delete mode 100644 src/main/java/org/warp/jcwdb/DBDataOutput.java delete mode 100644 src/main/java/org/warp/jcwdb/DBGenericObjectParser.java delete mode 100644 src/main/java/org/warp/jcwdb/DBLightArrayListParser.java delete mode 100644 src/main/java/org/warp/jcwdb/DBLightBigListParser.java delete mode 100644 src/main/java/org/warp/jcwdb/DBStandardTypes.java delete mode 100644 src/main/java/org/warp/jcwdb/DBStringParser.java delete mode 100644 src/main/java/org/warp/jcwdb/DBTypeParser.java delete mode 100644 src/main/java/org/warp/jcwdb/DBTypeParserImpl.java delete mode 100644 src/main/java/org/warp/jcwdb/DBTypedObjectParser.java delete mode 100644 src/main/java/org/warp/jcwdb/Editable.java delete mode 100644 src/main/java/org/warp/jcwdb/EntryReference.java delete mode 100644 src/main/java/org/warp/jcwdb/JCWDatabase.java delete mode 100644 src/main/java/org/warp/jcwdb/LightArrayList.java delete mode 100644 src/main/java/org/warp/jcwdb/LightBigList.java delete mode 100644 src/main/java/org/warp/jcwdb/LightList.java delete mode 100644 src/main/java/org/warp/jcwdb/MixedIndexDatabase.java delete mode 100644 src/main/java/org/warp/jcwdb/TypesManager.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DBClass.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DBDataType.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DBField.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DBObject.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DBPropertyGetter.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DBPropertySetter.java create mode 100644 src/main/java/org/warp/jcwdb/ann/Database.java create mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java create mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java delete mode 100644 src/main/java/org/warp/jcwdb/exampleimpl/Animal.java delete mode 100644 src/main/java/org/warp/jcwdb/exampleimpl/App.java delete mode 100644 src/main/java/org/warp/jcwdb/exampleimpl/Cat.java delete mode 100644 src/main/java/org/warp/jcwdb/exampleimpl/Dog.java delete mode 100644 src/main/java/org/warp/jcwdb/exampleimpl/StrangeAnimal.java diff --git a/pom.xml b/pom.xml index ba64ed1..bc00df0 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,12 @@ zero-allocation-hashing 0.8 + + + org.apache.commons + commons-lang3 + 3.8.1 + diff --git a/src/main/java/org/warp/jcwdb/CacheIndexManager.java b/src/main/java/org/warp/jcwdb/CacheIndexManager.java deleted file mode 100644 index 6ab7543..0000000 --- a/src/main/java/org/warp/jcwdb/CacheIndexManager.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.warp.jcwdb; - -import java.io.IOException; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -public class CacheIndexManager implements IndexManager { - - public CacheIndexManager() { - } - - @Override - public T get(long index, DBReader reader) { - // TODO: implement - return null; - } - - @Override - public int getType(long index) { - // TODO: implement - return 0; - } - - @Override - public long getHash(long index) { - // TODO: implement - return 0; - } - - @Override - public long add(DBDataOutput writer) { - // TODO: implement - return 0; - } - - @Override - public FullIndexDetails addAndGetDetails(DBDataOutput writer) { - // TODO: implement - return null; - } - - @Override - public IndexDetails set(long index, DBDataOutput writer) { - // TODO: implement - return null; - } - - @Override - public void setFlushingAllowed(long index, boolean isUnloadingAllowed) { - // TODO: implement - } - - @Override - public void delete(long index) { - // TODO: implement - } - - @Override - public boolean has(long index) { - // TODO: implement - return false; - } - - @Override - public void close() { - // TODO: implement - } - - @Override - public long clean() { - return 0; - } -} diff --git a/src/main/java/org/warp/jcwdb/DBDataOutput.java b/src/main/java/org/warp/jcwdb/DBDataOutput.java deleted file mode 100644 index 6af18e9..0000000 --- a/src/main/java/org/warp/jcwdb/DBDataOutput.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.warp.jcwdb; - -public interface DBDataOutput { - int getSize(); - int getType(); - long calculateHash(); - DBWriter getWriter(); - - static DBDataOutput create(DBWriter writer, int type, int size, long hash) { - return new DBDataOutput() { - - @Override - public int getSize() { - return size; - } - - @Override - public int getType() { - return type; - } - - @Override - public long calculateHash() { - return hash; - } - - @Override - public DBWriter getWriter() { - return writer; - } - - }; - } -} diff --git a/src/main/java/org/warp/jcwdb/DBGenericObjectParser.java b/src/main/java/org/warp/jcwdb/DBGenericObjectParser.java deleted file mode 100644 index 5e6f9ae..0000000 --- a/src/main/java/org/warp/jcwdb/DBGenericObjectParser.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.warp.jcwdb; - -import java.io.ByteArrayOutputStream; - -import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.io.Output; - -import net.openhft.hashing.LongHashFunction; - -public class DBGenericObjectParser extends DBTypeParserImpl implements DBTypedObjectParser { - private static final LongHashFunction hashFunction = net.openhft.hashing.LongHashFunction.xx(); - private static final Kryo kryo = new Kryo(); - static { - kryo.setRegistrationRequired(false); - } - - private static final DBReader defaultReader = (i, size) -> { - return kryo.readClassAndObject(i); - }; - - public DBReader getReader() { - return defaultReader; - } - - public DBDataOutput getWriter(final Object value) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Output tmpO = new Output(baos); - kryo.writeClassAndObject(tmpO, value); - tmpO.flush(); - final byte[] bytes = baos.toByteArray(); - final long hash = hashFunction.hashBytes(bytes); - tmpO.close(); - return DBDataOutput.create((o) -> { - o.write(bytes); - }, DBStandardTypes.GENERIC_OBJECT, bytes.length, hash); - } - - @Override - public long calculateHash(Object value) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Output tmpO = new Output(baos); - kryo.writeClassAndObject(tmpO, value); - tmpO.flush(); - final byte[] bytes = baos.toByteArray(); - final long hash = hashFunction.hashBytes(bytes); - tmpO.close(); - return hash; - } - - - @Override - public void registerClass(Class clazz, int id) { - if (id >= Integer.MAX_VALUE - 100) { - throw new IndexOutOfBoundsException(); - } - kryo.register(clazz, id + 100); - } -} diff --git a/src/main/java/org/warp/jcwdb/DBLightArrayListParser.java b/src/main/java/org/warp/jcwdb/DBLightArrayListParser.java deleted file mode 100644 index 3608ca4..0000000 --- a/src/main/java/org/warp/jcwdb/DBLightArrayListParser.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.warp.jcwdb; - -import it.unimi.dsi.fastutil.longs.LongArrayList; - -public class DBLightArrayListParser extends DBTypeParserImpl> { - private final JCWDatabase db; - - public DBLightArrayListParser(JCWDatabase db) { - this.db = db; - } - - public DBReader> getReader() { - return (i, size) -> { - LongArrayList internalList = new LongArrayList(); - long max = size / Long.BYTES; - for (int item = 0; item < max; item++) { - long itm = i.readLong(); - internalList.add(itm); - } - return new LightArrayList(db, internalList); - }; - } - - public DBDataOutput> getWriter(final LightArrayList value) { - final int elementsCount = value.size(); - return DBDataOutput.create((o) -> { - LongArrayList list = value.internalList; - for (int i = 0; i < elementsCount; i++) { - o.writeLong(list.getLong(i)); - } - }, DBStandardTypes.LIGHT_LIST_ARRAY, elementsCount * Long.BYTES, calculateHash(value)); - } - - @Override - public long calculateHash(LightArrayList value) { - return value.internalList.hashCode(); - } - - @Override - public String toString() { - return "DBLightArrayListParser{" + - "db=" + db + - '}'; - } -} diff --git a/src/main/java/org/warp/jcwdb/DBLightBigListParser.java b/src/main/java/org/warp/jcwdb/DBLightBigListParser.java deleted file mode 100644 index c30eab3..0000000 --- a/src/main/java/org/warp/jcwdb/DBLightBigListParser.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.warp.jcwdb; - -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.longs.LongArrayList; - -public class DBLightBigListParser extends DBTypeParserImpl> { - private final JCWDatabase db; - - public DBLightBigListParser(JCWDatabase db) { - this.db = db; - } - - public DBReader> getReader() { - return (i, size) -> { - LongArrayList chunks = new LongArrayList(); - IntArrayList chunkSizes = new IntArrayList(); - long max = size / (Long.BYTES + Integer.BYTES); - for (int item = 0; item < max; item++) { - long itm = i.readLong(); - int itm2 = i.readInt(); - chunks.add(itm); - chunkSizes.add(itm2); - } - return new LightBigList<>(db, chunks, chunkSizes); - }; - } - - public DBDataOutput> getWriter(final LightBigList value) { - final int elementsCount = value.chunksCount(); - return DBDataOutput.create((o) -> { - LongArrayList list = value.chunks; - IntArrayList list2 = value.chunkSizes; - for (int i = 0; i < elementsCount; i++) { - o.writeLong(list.getLong(i)); - o.writeInt(list2.getInt(i)); - } - }, DBStandardTypes.LIGHT_LIST_BIG, elementsCount * (Long.BYTES + Integer.BYTES), calculateHash(value)); - } - - @Override - public long calculateHash(LightBigList value) { - return (((long)value.chunks.hashCode()) << 32) | (value.chunkSizes.hashCode() & 0xffffffffL); - } -} diff --git a/src/main/java/org/warp/jcwdb/DBStandardTypes.java b/src/main/java/org/warp/jcwdb/DBStandardTypes.java deleted file mode 100644 index eb71a70..0000000 --- a/src/main/java/org/warp/jcwdb/DBStandardTypes.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.warp.jcwdb; - -public class DBStandardTypes { - private static final int STD = 0xFFFFF000; - public static final int BOOLEAN = STD| 0x000; - public static final int BYTE = STD| 0x001; - public static final int SHORT = STD| 0x002; - public static final int CHAR = STD| 0x003; - public static final int INTEGER = STD| 0x004; - public static final int FLOAT = STD| 0x005; - public static final int DOUBLE = STD| 0x006; - public static final int STRING = STD| 0x007; - public static final int BYTE_ARRAY = STD| 0x008; - public static final int LIGHT_LIST_ARRAY = STD| 0x009; - public static final int LIGHT_LIST_BIG = STD| 0x00A; - public static final int GENERIC_OBJECT = STD| 0x00B; - - public static void registerStandardTypes(JCWDatabase db, TypesManager typesManager) { - typesManager.registerType(String.class, STRING, new DBStringParser()); - typesManager.registerType(LightArrayList.class, LIGHT_LIST_ARRAY, new DBLightArrayListParser(db)); - typesManager.registerType(LightBigList.class, LIGHT_LIST_BIG, new DBLightBigListParser(db)); - typesManager.registerTypeFallback(new DBGenericObjectParser()); - } -} \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/DBStringParser.java b/src/main/java/org/warp/jcwdb/DBStringParser.java deleted file mode 100644 index d7e8061..0000000 --- a/src/main/java/org/warp/jcwdb/DBStringParser.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.warp.jcwdb; - -import java.nio.charset.StandardCharsets; - -import net.openhft.hashing.LongHashFunction; - -public class DBStringParser extends DBTypeParserImpl { - private static final LongHashFunction hashFunction = net.openhft.hashing.LongHashFunction.xx(); - private static final DBReader defaultReader = (i, size) -> { - return new String(i.readBytes(size), StandardCharsets.UTF_16LE); - }; - - public DBReader getReader() { - return defaultReader; - } - - public DBDataOutput getWriter(final String value) { - return DBDataOutput.create((o) -> { - o.write(value.getBytes(StandardCharsets.UTF_16LE)); - }, DBStandardTypes.STRING, value.length() * 2, calculateHash(value)); - } - - @Override - public long calculateHash(String value) { - return hashFunction.hashBytes(value.getBytes(StandardCharsets.UTF_16LE)); - } -} diff --git a/src/main/java/org/warp/jcwdb/DBTypeParser.java b/src/main/java/org/warp/jcwdb/DBTypeParser.java deleted file mode 100644 index 3eecd54..0000000 --- a/src/main/java/org/warp/jcwdb/DBTypeParser.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.warp.jcwdb; - -public interface DBTypeParser extends Castable { - DBReader getReader(); - DBDataOutput getWriter(final T value); - long calculateHash(final T value); -} diff --git a/src/main/java/org/warp/jcwdb/DBTypeParserImpl.java b/src/main/java/org/warp/jcwdb/DBTypeParserImpl.java deleted file mode 100644 index daa7dcd..0000000 --- a/src/main/java/org/warp/jcwdb/DBTypeParserImpl.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.warp.jcwdb; - -public abstract class DBTypeParserImpl implements DBTypeParser { - @SuppressWarnings("unchecked") - @Override - public T cast() { - return (T) this; - } -} diff --git a/src/main/java/org/warp/jcwdb/DBTypedObjectParser.java b/src/main/java/org/warp/jcwdb/DBTypedObjectParser.java deleted file mode 100644 index 5aa4353..0000000 --- a/src/main/java/org/warp/jcwdb/DBTypedObjectParser.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.warp.jcwdb; - -public interface DBTypedObjectParser extends DBTypeParser { - public void registerClass(Class clazz, int type); -} diff --git a/src/main/java/org/warp/jcwdb/DBWriter.java b/src/main/java/org/warp/jcwdb/DBWriter.java index 5456c58..4e9275e 100644 --- a/src/main/java/org/warp/jcwdb/DBWriter.java +++ b/src/main/java/org/warp/jcwdb/DBWriter.java @@ -2,6 +2,6 @@ package org.warp.jcwdb; import com.esotericsoftware.kryo.io.Output; -public interface DBWriter { +public interface DBWriter { void write(Output o); } diff --git a/src/main/java/org/warp/jcwdb/Editable.java b/src/main/java/org/warp/jcwdb/Editable.java deleted file mode 100644 index 82008dc..0000000 --- a/src/main/java/org/warp/jcwdb/Editable.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.warp.jcwdb; - -import java.io.IOException; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; - -public interface Editable { - /** - * Reccomended way to edit the value - * - * @param editFunction - * @throws IOException - */ - void editValue(BiFunction editFunction); - - /** - * Reccomended way to edit the value - * @param editFunction - * @throws IOException - */ - void editValue(Function editFunction); - - /** - * Reccomended way to edit the value - * @param editFunction - * @throws IOException - */ - void editValue(BiConsumer editFunction); - - /** - * Reccomended way to edit the value - * @param editFunction - * @throws IOException - */ - void editValue(Consumer editFunction); - - /** - * Reccomended way to view the value - * @param viewFunction - * @throws IOException - */ - void viewValue(Consumer viewFunction); - - /** - * Substitute the old value with a new one - * @param val - * @throws IOException - */ - void setValue(T val); - - - /** - * DO NOT ATTEMPT TO MODIFY THE VALUE RETURNED - * @return - */ - T getValueReadOnlyUnsafe(); - -} diff --git a/src/main/java/org/warp/jcwdb/EntryReference.java b/src/main/java/org/warp/jcwdb/EntryReference.java deleted file mode 100644 index 2dc4a0b..0000000 --- a/src/main/java/org/warp/jcwdb/EntryReference.java +++ /dev/null @@ -1,266 +0,0 @@ -package org.warp.jcwdb; - -import java.io.IOException; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; - -/** - * You must have only a maximum of 1 reference for each index - * @param - */ -public class EntryReference implements Editable, Saveable, Castable { - private final JCWDatabase.EntryReferenceTools db; - private final long entryIndex; - private final DBTypeParser parser; - private T value; - private long cachedHash; - private volatile boolean isHashCached; - private volatile boolean loaded; - private volatile boolean closed; - private volatile boolean isFlushingAllowed; - private final Object hashCacheLock = new Object(); - private final Object accessLock = new Object(); - private final Object closeLock = new Object(); - - public EntryReference(JCWDatabase.EntryReferenceTools db, long entryId, long hash, DBTypeParser parser) { - this.loaded = false; - this.isHashCached = false; - this.db = db; - this.entryIndex = entryId; - this.parser = parser; - this.value = null; - } - - public EntryReference(JCWDatabase.EntryReferenceTools db, long entryId, long hash, DBTypeParser parser, T value) { - this.loaded = true; - this.isHashCached = true; - this.db = db; - this.entryIndex = entryId; - this.parser = parser; - this.cachedHash = hash; - this.value = value; - } - - public DBTypeParser getParser() { - return parser; - } - - public long getIndex() { - return entryIndex; - } - - public long calculateHash() { - synchronized(accessLock) { - load(); - synchronized(hashCacheLock) { - if (isHashCached) { - return cachedHash; - } - } - return parser.calculateHash(this.value); - } - } - - public boolean isClosed() { - return closed; - } - - /** - * Note that this method won't be called when closing without saving - */ - public void save() { - this.save(false); - } - - public void saveAndFlush() { - this.save(true); - } - - private void save(boolean flush) { - synchronized(accessLock) { - if (loaded && !closed) { - try { - if (value instanceof Saveable) { - if (flush) { - ((Saveable)value).saveAndFlush(); - } else { - ((Saveable)value).save(); - } - } - IndexDetails returnedDetails = this.db.write(entryIndex, parser.getWriter(value)); - synchronized(hashCacheLock) { - this.cachedHash = returnedDetails.getHash(); - this.isHashCached = true; - } - if (flush) { - if (!isFlushingAllowed) { - this.db.setFlushingAllowed(entryIndex, true); - this.isFlushingAllowed = true; - } - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - - /** - * Reccomended way to edit the value - * @param editFunction - * @throws IOException - */ - public void editValue(BiFunction editFunction) { - synchronized(accessLock) { - load(); - this.value = editFunction.apply(this.value, this); - this.save(true); - } - } - - /** - * Reccomended way to edit the value - * @param editFunction - * @throws IOException - */ - public void editValue(Function editFunction) { - synchronized(accessLock) { - load(); - this.value = editFunction.apply(this.value); - this.save(true); - } - } - - /** - * Reccomended way to edit the value - * @param editFunction - * @throws IOException - */ - public void editValue(BiConsumer editFunction) { - synchronized(accessLock) { - load(); - editFunction.accept(this.value, this); - this.save(true); - } - } - - /** - * Reccomended way to edit the value - * @param editFunction - * @throws IOException - */ - public void editValue(Consumer editFunction) { - synchronized(accessLock) { - load(); - editFunction.accept(this.value); - this.save(true); - } - } - - /** - * Reccomended way to edit the value - * DO NOT EDIT THE VALUE - * @param viewFunction - * @throws IOException - */ - public void viewValue(Consumer viewFunction) { - synchronized(accessLock) { - load(); - viewFunction.accept(this.value); - } - } - - /** - * Substitute the old value with a new one - * @param val - * @throws IOException - */ - public void setValue(T val) { - synchronized(accessLock) { - this.loaded = true; - this.value = val; - synchronized(hashCacheLock) { - this.isHashCached = false; - } - this.save(true); - } - } - - /** - * Use editValue instead. READ ONLY!! - * @return - */ - @Deprecated() - public T getValue() { - return getValueReadOnlyUnsafe(); - } - - /** - * DO NOT ATTEMPT TO MODIFY THE VALUE RETURNED - * @return - */ - public T getValueReadOnlyUnsafe() { - synchronized(accessLock) { - load(); - return this.value; - } - - } - - private void load() { - synchronized(accessLock) { - if (!loaded) { - try { - if (this.isFlushingAllowed) { - this.db.setFlushingAllowed(entryIndex, false); - this.isFlushingAllowed = false; - } - this.value = db.read(entryIndex, parser.getReader()); - this.loaded = true; - } catch (IOException e) { - throw (NullPointerException) new NullPointerException(e.getLocalizedMessage()).initCause(e); - } - } - } - } - - @SuppressWarnings("unchecked") - @Override - public U cast() { - return (U) this; - } - - protected void close() throws IOException { - if (closed) { - return; - } - synchronized (closeLock) { - if (closed) { - return; - } - - save(true); - - closed = true; - } - } - - public void closeWithoutSaving() { - if (closed) { - return; - } - synchronized (closeLock) { - if (closed) { - return; - } - - closed = true; - } - } - - public Object getAccessLock() { - return accessLock; - } -} diff --git a/src/main/java/org/warp/jcwdb/FileIndexManager.java b/src/main/java/org/warp/jcwdb/FileIndexManager.java index 59b3710..1aea0fd 100644 --- a/src/main/java/org/warp/jcwdb/FileIndexManager.java +++ b/src/main/java/org/warp/jcwdb/FileIndexManager.java @@ -4,6 +4,7 @@ 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.Database; import java.io.IOException; import java.nio.ByteBuffer; @@ -12,6 +13,7 @@ import java.nio.channels.SeekableByteChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.util.function.Consumer; public class FileIndexManager implements IndexManager { private final SeekableByteChannel dataFileChannel, metadataFileChannel; @@ -116,23 +118,12 @@ public class FileIndexManager implements IndexManager { } @Override - public int getType(long index) throws IOException { - return getIndexMetadata(index).getType(); - } - - @Override - public long getHash(long index) throws IOException { - return getIndexMetadata(index).getHash(); - } - - @Override - public IndexDetails set(long index, DBDataOutput data) throws IOException { + public IndexDetails set(long index, int size, DBWriter data) throws IOException { checkClosed(); - final int dataSize = data.getSize(); IndexDetails indexDetails = getIndexMetadataUnsafe(index); - if (indexDetails == null || indexDetails.getSize() < dataSize) { + if (indexDetails == null || indexDetails.getSize() < size) { // Allocate new space - IndexDetails newDetails = allocateAndWrite(index, data); + IndexDetails newDetails = allocateAndWrite(index, size, data); if (indexDetails != null) { // Mark free the old bytes fileAllocator.markFree(indexDetails.getOffset(), indexDetails.getSize()); @@ -140,14 +131,14 @@ public class FileIndexManager implements IndexManager { return newDetails; } else { // Check if size changed - if (dataSize < indexDetails.getSize()) { + if (size < indexDetails.getSize()) { // Mark free the unused bytes - fileAllocator.markFree(indexDetails.getOffset() + dataSize, dataSize); + fileAllocator.markFree(indexDetails.getOffset() + size, size); } // Update index details - indexDetails = editIndex(index, indexDetails, indexDetails.getOffset(), dataSize, indexDetails.getType(), data.calculateHash()); + indexDetails = editIndex(index, indexDetails, indexDetails.getOffset(), size); // Write data - writeExact(indexDetails, data); + writeExact(indexDetails, size, data); // Before returning, return IndexDetails return indexDetails; } @@ -164,58 +155,53 @@ public class FileIndexManager implements IndexManager { } @Override - public long add(DBDataOutput data) throws IOException { + public long add(int size) { checkClosed(); - final int size = data.getSize(); final long offset = fileAllocator.allocate(size); - final int type = data.getType(); - final long hash = data.calculateHash(); - final IndexDetails indexDetails = new IndexDetails(offset, size, type, hash); + final IndexDetails indexDetails = new IndexDetails(offset, size); final long index = createIndexMetadata(indexDetails); - writeExact(indexDetails, data); return index; } @Override - public FullIndexDetails addAndGetDetails(DBDataOutput data) throws IOException { + public long add(int size, DBWriter data) throws IOException { checkClosed(); - final int size = data.getSize(); final long offset = fileAllocator.allocate(size); - final int type = data.getType(); - final long hash = data.calculateHash(); - final IndexDetails indexDetails = new IndexDetails(offset, size, type, hash); + final IndexDetails indexDetails = new IndexDetails(offset, size); final long index = createIndexMetadata(indexDetails); - writeExact(indexDetails, data); + 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! - * - * @param indexDetails - * @param data - * @throws IOException */ - private void writeExact(final IndexDetails indexDetails, DBDataOutput data) throws IOException { - final int dataSize = data.getSize(); - if (indexDetails.getSize() != dataSize) { - throw new IOException("Unable to write " + dataSize + " in a space of " + indexDetails.getSize()); + 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(); - final Output o = new Output(Channels.newOutputStream(dataFileChannel.position(offset)), dataSize); - data.getWriter().write(o); + final Output o = new Output(Channels.newOutputStream(dataFileChannel.position(offset)), size); + data.write(o); o.flush(); } - private IndexDetails allocateAndWrite(final long index, DBDataOutput w) throws IOException { - final int size = w.getSize(); - final int type = w.getType(); - final long hash = w.calculateHash(); + private IndexDetails allocateAndWrite(final long index, int size, DBWriter w) throws IOException { final long offset = fileAllocator.allocate(size); - IndexDetails details = editIndex(index, offset, size, type, hash); - writeExact(details, w); + IndexDetails details = editIndex(index, offset, size); + writeExact(details, size, w); return details; } @@ -284,13 +270,11 @@ public class FileIndexManager implements IndexManager { * @param oldData Old index data to check * @param offset offset * @param size size - * @param type type - * @param hash hash * @return */ - private IndexDetails editIndex(long index, IndexDetails oldData, long offset, int size, int type, long hash) { - if (oldData.getOffset() != offset || oldData.getSize() != size || oldData.getType() != type || oldData.getHash() != hash) { - return editIndex(index, offset, size, type, hash); + 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; } @@ -301,12 +285,10 @@ public class FileIndexManager implements IndexManager { * @param index * @param offset * @param size - * @param type - * @param hash * @return */ - private IndexDetails editIndex(long index, long offset, int size, int type, long hash) { - IndexDetails indexDetails = new IndexDetails(offset, size, type, hash); + private IndexDetails editIndex(long index, long offset, int size) { + IndexDetails indexDetails = new IndexDetails(offset, size); editIndex(index, indexDetails); return indexDetails; } @@ -368,15 +350,10 @@ public class FileIndexManager implements IndexManager { currentMetadataFileChannel.read(metadataByteBuffer); metadataByteBuffer.rewind(); // If it's not deleted continue - if ((metadataByteBuffer.getInt() & IndexDetails.MASK_DELETED) == 0) { - final long offset = metadataByteBuffer.getLong(); -// final long sizeAndType = metadataByteBuffer.getLong(); -// final int size = (int)(sizeAndType >> 32); -// final int type = (int)sizeAndType; + 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(); - final int type = metadataByteBuffer.getInt(); - final long hash = metadataByteBuffer.getLong(); - indexDetails = new IndexDetails(offset, size, type, hash); + indexDetails = new IndexDetails(offset, size); } } return indexDetails; @@ -411,18 +388,12 @@ public class FileIndexManager implements IndexManager { } private void writeIndexDetails(SeekableByteChannel position, IndexDetails indexDetails) throws IOException { - synchronized (metadataByteBufferLock) {// FIXXXX cleaner3 + synchronized (metadataByteBufferLock) { final int size = indexDetails.getSize(); - final int type = indexDetails.getType(); final long offset = indexDetails.getOffset(); - final long hash = indexDetails.getHash(); metadataByteBuffer.rewind(); - metadataByteBuffer.putInt(0); metadataByteBuffer.putLong(offset); metadataByteBuffer.putInt(size); - metadataByteBuffer.putInt(type); - //metadataByteBuffer.putLong((long)size << 32 | type & 0xFFFFFFFFL); - metadataByteBuffer.putLong(hash); metadataByteBuffer.rewind(); position.write(metadataByteBuffer); } @@ -431,7 +402,7 @@ public class FileIndexManager implements IndexManager { private void eraseIndexDetails(SeekableByteChannel position) throws IOException { synchronized (maskByteBufferLock) { maskByteBuffer.rewind(); - maskByteBuffer.putInt(IndexDetails.MASK_DELETED); + maskByteBuffer.putLong(-1); // -1 = deleted maskByteBuffer.rewind(); position.write(maskByteBuffer); } @@ -513,12 +484,12 @@ public class FileIndexManager implements IndexManager { long removedIndices = 0; LongArrayList toUnload = new LongArrayList(); synchronized (indicesMapsAccessLock) { - if (loadedIndices.size() > JCWDatabase.MAX_LOADED_INDICES) { + if (loadedIndices.size() > Database.MAX_LOADED_INDICES) { long count = loadedIndices.size(); LongIterator it = loadedIndices.keySet().iterator(); while (it.hasNext()) { long loadedIndex = it.nextLong(); - if (count < JCWDatabase.MAX_LOADED_INDICES * 3l / 2l) { + if (count < Database.MAX_LOADED_INDICES * 3l / 2l) { break; } toUnload.add(loadedIndex); diff --git a/src/main/java/org/warp/jcwdb/IndexDetails.java b/src/main/java/org/warp/jcwdb/IndexDetails.java index ab4f1de..382eb49 100644 --- a/src/main/java/org/warp/jcwdb/IndexDetails.java +++ b/src/main/java/org/warp/jcwdb/IndexDetails.java @@ -6,30 +6,20 @@ public class IndexDetails { /** * The bitmask is used to determine if an index has been deleted */ - public static final int BITMASK_SIZE = Integer.BYTES; public static final int OFFSET_BYTES = Long.BYTES; public static final int DATA_SIZE_BYTES = Integer.BYTES; - public static final int TYPE_BYTES = Integer.BYTES; - public static final int HASH_BYTES = Long.BYTES; - public static final int TOTAL_BYTES = BITMASK_SIZE + OFFSET_BYTES + DATA_SIZE_BYTES + TYPE_BYTES + HASH_BYTES; - public static final int MASK_DELETED = 0b00000001; + public static final int TOTAL_BYTES = OFFSET_BYTES + DATA_SIZE_BYTES; private final long offset; private final int size; - private final int type; - private final long hash; - public IndexDetails(long offset, int size, int type, long hash) { + public IndexDetails(long offset, int size) { this.offset = offset; this.size = size; - this.type = type; - this.hash = hash; } public IndexDetails(IndexDetails indexDetails) { this.offset = indexDetails.offset; this.size = indexDetails.size; - this.type = indexDetails.type; - this.hash = indexDetails.hash; } public long getOffset() { @@ -40,22 +30,12 @@ public class IndexDetails { return size; } - public int getType() { - return type; - } - - public long getHash() { - return hash; - } - @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + (int) (hash ^ (hash >>> 32)); result = prime * result + (int) (offset ^ (offset >>> 32)); result = prime * result + size; - result = prime * result + type; return result; } @@ -68,20 +48,16 @@ public class IndexDetails { if (getClass() != obj.getClass()) return false; IndexDetails other = (IndexDetails) obj; - if (hash != other.hash) - return false; if (offset != other.offset) return false; if (size != other.size) return false; - if (type != other.type) - return false; return true; } @Override public String toString() { - return "IndexDetails [offset=" + offset + ", size=" + size + ", type=" + type + ", hash=" + hash + "]"; + return "IndexDetails [offset=" + offset + ", size=" + size + "]"; } diff --git a/src/main/java/org/warp/jcwdb/IndexManager.java b/src/main/java/org/warp/jcwdb/IndexManager.java index a48fbe3..21f4320 100644 --- a/src/main/java/org/warp/jcwdb/IndexManager.java +++ b/src/main/java/org/warp/jcwdb/IndexManager.java @@ -1,5 +1,7 @@ package org.warp.jcwdb; +import com.esotericsoftware.kryo.io.Output; + import java.io.IOException; import java.util.function.BiConsumer; import java.util.function.BiPredicate; @@ -7,11 +9,10 @@ import java.util.function.Consumer; public interface IndexManager extends Cleanable { T get(long index, DBReader reader) throws IOException; - int getType(long index) throws IOException; - long getHash(long index) throws IOException; - long add(DBDataOutput writer) throws IOException; - FullIndexDetails addAndGetDetails(DBDataOutput writer) throws IOException; - IndexDetails set(long index, DBDataOutput writer) 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 setFlushingAllowed(long index, boolean isUnloadingAllowed); void delete(long index) throws IOException; boolean has(long index); diff --git a/src/main/java/org/warp/jcwdb/JCWDatabase.java b/src/main/java/org/warp/jcwdb/JCWDatabase.java deleted file mode 100644 index c38b57e..0000000 --- a/src/main/java/org/warp/jcwdb/JCWDatabase.java +++ /dev/null @@ -1,197 +0,0 @@ -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); - } - -} diff --git a/src/main/java/org/warp/jcwdb/LightArrayList.java b/src/main/java/org/warp/jcwdb/LightArrayList.java deleted file mode 100644 index 83dcf93..0000000 --- a/src/main/java/org/warp/jcwdb/LightArrayList.java +++ /dev/null @@ -1,455 +0,0 @@ -package org.warp.jcwdb; - -import it.unimi.dsi.fastutil.longs.LongArrayList; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; - -import java.io.IOError; -import java.io.IOException; -import java.util.*; -import java.util.function.Consumer; -import java.util.function.Predicate; - -public class LightArrayList implements LightList { - - public final LongArrayList internalList; - private final transient JCWDatabase db; - - /** - * @param db Database reference - */ - public LightArrayList(JCWDatabase db) { - this.db = db; - this.internalList = new LongArrayList(); - } - - /** - * @param db Database reference - * @param elements Elements to add - */ - public LightArrayList(JCWDatabase db, LongArrayList elements) { - this.db = db; - this.internalList = new LongArrayList(elements); - } - - @Override - public int size() { - return internalList.size(); - } - - @Override - public boolean isEmpty() { - return internalList.isEmpty(); - } - - @Override - public boolean contains(Object o) { - if (o != null) { - for (long element : internalList) { - EntryReference ref = null; - try { - ref = db.get(element); - if (o.equals(ref.getValueReadOnlyUnsafe())) { - return true; - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } - return false; - } - - /** - * Use iteratorReferences() - */ - @Deprecated - @SuppressWarnings("unchecked") - @Override - public Iterator iterator() { - System.out.println("WARNING! YOU ARE USING iterator()! PLEASE USE ITERATORREFERENCES TO AVOID OUTOFMEMORY!"); - final ArrayList elements = new ArrayList<>(); - for (long element : internalList) { - try { - elements.add((T) db.get(element).getValueReadOnlyUnsafe()); - } catch (IOException e) { - e.printStackTrace(); - } - } - return elements.iterator(); - } - - @SuppressWarnings("unchecked") - @Override - public Iterator> iteratorReferences() { - final ArrayList> elements = new ArrayList<>(); - for (long element : internalList) { - try { - elements.add(db.get(element)); - } catch (IOException e) { - e.printStackTrace(); - } - } - return elements.iterator(); - } - - /** - * USE forEachReference INSTEAD, TO AVOID OUTOFMEMORY - * - * @param action - */ - @Deprecated - @Override - public void forEach(Consumer action) { - Objects.requireNonNull(action); - for (T t : this) { - action.accept(t); - } - } - - @Override - public void forEachReference(Consumer> action) { - Objects.requireNonNull(action); - for (long index : this.internalList) { - try { - action.accept(db.get(index)); - } catch (IOException e) { - throw new IOError(e); - } - } - } - - - @SuppressWarnings("unchecked") - @Override - public T[] toArray() { - final T[] elements = (T[]) new Object[internalList.size()]; - for (int i = 0; i < elements.length; i++) { - try { - T element = (T) db.get(internalList.getLong(i)).getValueReadOnlyUnsafe(); - elements[i] = element; - } catch (IOException e) { - e.printStackTrace(); - } - } - return elements; - } - - @SuppressWarnings("unchecked") - @Override - public T1[] toArray(T1[] a) { - final T1[] elements = (T1[]) new Objects[internalList.size()]; - for (int i = 0; i < elements.length; i++) { - try { - elements[i] = (T1) db.get(internalList.getLong(i)).getValueReadOnlyUnsafe(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return elements; - } - - @Override - public boolean add(T o) { - EntryReference ref = addEntry(o); - return ref != null; - } - - @Override - public EntryReference addEntry(T o) { - EntryReference ref = addToDatabase(o); - if (internalList.add(ref.getIndex())) { - return ref; - } else { - return null; - } - } - - @Override - public boolean remove(Object o) { - int removeIndex = indexOf(o); - if (removeIndex >= 0) { - internalList.removeLong(removeIndex); - return true; - } - return false; - } - - @Override - public boolean remove(EntryReference ref) { - int removeIndex = indexOfEntry(ref); - if (removeIndex >= 0) { - internalList.removeLong(removeIndex); - return true; - } - return false; - } - - @Override - public boolean containsAll(Collection c) { - for (Object o : c) { - int objIndex = indexOf(o); - if (objIndex < 0) { - return false; - } - } - return true; - } - - @SuppressWarnings("unchecked") - @Override - public boolean addAll(Collection c) { - boolean result = false; - for (Object o : c) { - result |= add((T) o); - } - return result; - } - - @SuppressWarnings("unchecked") - @Override - public boolean addAll(int index, Collection c) { - boolean result = false; - int delta = 0; - for (Object o : c) { - add(index + delta, (T) o); - result = true; - delta++; - } - return result; - } - - @SuppressWarnings("unchecked") - @Override - public boolean removeAll(Collection c) { - boolean result = false; - for (Object o : c) { - result |= remove((T) o); - } - return result; - } - - @Override - public boolean retainAll(Collection c) { - boolean result = false; - LongArrayList collectionHashes = new LongArrayList(); - ObjectArrayList collection = new ObjectArrayList<>(); - collection.addAll(c); - for (Object o : c) { - collectionHashes.add(db.calculateHash(o)); - } - for (int i = 0; i < internalList.size(); i++) { - long hash = internalList.getLong(i); - int positionInCollection = collectionHashes.indexOf(hash); - if (positionInCollection == -1) { - remove(collection.get(positionInCollection)); - result = true; - } - } - return result; - } - - @Override - public void clear() { - internalList.clear(); - } - - /** - * Use getReference or getReadOnlyValue - */ - @Deprecated - @Override - public T get(int index) { - return getReadOnlyValue(index); - } - - @SuppressWarnings("unchecked") - public T getReadOnlyValue(int index) { - try { - return (T) db.get(internalList.getLong(index)).getValueReadOnlyUnsafe(); - } catch (IOException e) { - e.printStackTrace(); - return null; - } - } - - @Override - public EntryReference getReference(int index) { - try { - return db.get(internalList.getLong(index)).cast(); - } catch (IOException e) { - e.printStackTrace(); - return null; - } - } - - @SuppressWarnings("unchecked") - @Override - public T set(int index, T element) { - EntryReference ref = addToDatabase(element); - long oldIndex = internalList.set(index, ref.getIndex()); - try { - ref.close(); - return ((EntryReference) (db.get(oldIndex))).getValueReadOnlyUnsafe(); - } catch (IOException e) { - throw (NullPointerException) new NullPointerException().initCause(e); - } - } - - @Override - public void add(int index, T element) { - EntryReference ref = addToDatabase(element); - internalList.add(index, ref.getIndex()); - } - - @SuppressWarnings("unchecked") - @Override - public T remove(int index) { - long oldIndex = internalList.removeLong(index); - try { - return ((EntryReference) (db.get(oldIndex))).getValueReadOnlyUnsafe(); - } catch (IOException e) { - throw (NullPointerException) new NullPointerException().initCause(e); - } - } - - @Override - public int indexOf(Object o) { - EntryReference ref = addToDatabase(o); - long objToRemoveHash = ref.calculateHash(); - LongArrayList hashes = new LongArrayList(); - - - for (int i = 0; i < hashes.size(); i++) { - long hash = hashes.getLong(i); - if (objToRemoveHash == hash) { - try { - if (ref.equals(db.get(internalList.getLong(i)))) { - return i; - } - } catch (IOException e) { - throw (NullPointerException) new NullPointerException().initCause(e); - } - } - } - return -1; - } - - @Override - public int indexOfEntry(EntryReference ref) { - for (int i = 0; i < internalList.size(); i++) { - long index = internalList.getLong(i); - try { - EntryReference ref2 = db.get(index); - if (ref.getValueReadOnlyUnsafe().equals(ref2.getValueReadOnlyUnsafe())) { - return i; - } - } catch (IOException e) { - throw (NullPointerException) new NullPointerException().initCause(e); - } - } - return -1; - } - - @Override - public void appendIndex(long elementIndex) { - internalList.add(elementIndex); - } - - @Override - public int lastIndexOf(Object o) { - EntryReference ref = addToDatabase(o).cast(); - return lastIndexOfEntry(ref); - } - - @Override - public int lastIndexOfEntry(EntryReference ref) { - long objToRemoveHash = ref.calculateHash(); - - int lastValue = -1; - - for (int i = 0; i < internalList.size(); i++) { - long index2 = internalList.getLong(i); - try { - EntryReference ref2 = db.get(index2); - if (objToRemoveHash == ref2.calculateHash()) { - if (ref.getValueReadOnlyUnsafe().equals(ref2.getValueReadOnlyUnsafe())) { - lastValue = i; - } - } - } catch (IOException e) { - throw (NullPointerException) new NullPointerException().initCause(e); - } - } - return lastValue; - } - - @Deprecated - @Override - public ListIterator listIterator() { - // TODO: implement - throw new RuntimeException("Not implemented!"); - } - - @Deprecated - @Override - public ListIterator listIterator(int index) { - // TODO: implement - throw new RuntimeException("Not implemented!"); - } - - @Deprecated - @Override - public List subList(int fromIndex, int toIndex) { - // TODO: implement - throw new RuntimeException("Not implemented!"); - } - - private EntryReference addToDatabase(U obj) { - EntryReference ref; - try { - ref = db.add(obj); - } catch (IOException e) { - throw (NullPointerException) new NullPointerException().initCause(e); - } - return ref; - } - - @Deprecated - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((internalList == null) ? 0 : internalList.hashCode()); - return result; - } - - @SuppressWarnings("unchecked") - @Override - public boolean removeIf(Predicate filter) { - Objects.requireNonNull(filter); - boolean removed = false; - for (int i = 0; i < internalList.size(); ) { - T obj; - try { - obj = ((EntryReference) (db.get(internalList.getLong(i)).cast())).getValueReadOnlyUnsafe(); - } catch (IOException e) { - throw (NullPointerException) new NullPointerException().initCause(e); - } - if (filter.test(obj)) { - internalList.removeLong(i); - removed = true; - } else { - i++; - } - } - return removed; - } - - @Override - public String toString() { - return "LightArrayList{" + - "internalList=" + internalList + - ", db=" + db + - '}'; - } -} diff --git a/src/main/java/org/warp/jcwdb/LightBigList.java b/src/main/java/org/warp/jcwdb/LightBigList.java deleted file mode 100644 index 915e6a4..0000000 --- a/src/main/java/org/warp/jcwdb/LightBigList.java +++ /dev/null @@ -1,607 +0,0 @@ -package org.warp.jcwdb; - -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.longs.LongArrayList; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; - -import java.io.IOException; -import java.util.*; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.function.Supplier; - -public class LightBigList implements LightList, Saveable { - - public static final int MAX_ELEMENTS_PER_CHUNK = 200000; - - public final LongArrayList chunks; - public final IntArrayList chunkSizes; - private final JCWDatabase db; - private LightArrayList cachedChunk; - private EntryReference> cachedChunkRef; - private long cachedChunkIndex = -1; - private int cachedChunkNumber = -1; - private final Object cachedChunkLock = new Object(); - - /** - * @param db Database reference - */ - public LightBigList(JCWDatabase db) { - this.db = db; - this.chunks = new LongArrayList(); - this.chunkSizes = new IntArrayList(); - } - - /** - * @param db Database reference - * @param elements Elements to add - */ - public LightBigList(JCWDatabase db, LongArrayList elements) { - this.db = db; - this.chunks = new LongArrayList(); - this.chunkSizes = new IntArrayList(); - elements.forEach((long element) -> { - this.appendIndex(element); - }); - } - - public LightBigList(JCWDatabase db, LongArrayList chunks, IntArrayList chunkSizes) { - this.db = db; - this.chunks = chunks; - this.chunkSizes = chunkSizes; - if (this.chunks.size() > 0) { - prepareAccessToChunk(0); - } - } - - private void prepareAccessToChunk(int chunkNumber) { - if (this.cachedChunkRef != null) { - this.cachedChunkRef.save(); - } - this.cachedChunkNumber = chunkNumber; - this.cachedChunkIndex = this.chunks.getLong(chunkNumber); - try { - this.cachedChunkRef = db.get(this.cachedChunkIndex); - } catch (IOException ex) { - throw (NullPointerException) new NullPointerException().initCause(ex); - } - this.cachedChunk = this.cachedChunkRef.getValueReadOnlyUnsafe(); - } - - /** - * Append an index to the first free chunk - * @param elementIndex - */ - public void appendIndex(long elementIndex) { - for (int i = 0; i < chunks.size(); i++) { - final int chunkNumber = i; - if (MAX_ELEMENTS_PER_CHUNK - chunkSizes.getInt(i) > 0) { - synchronized (cachedChunkLock) { - if (cachedChunkNumber != i) { - prepareAccessToChunk(i); - } - cachedChunk.appendIndex(elementIndex); - chunkSizes.set(chunkNumber, cachedChunk.size()); - return; - } - } - } - LightList newChunk = new LightArrayList<>(db); - newChunk.appendIndex(elementIndex); - long newChunkIndex; - try { - newChunkIndex = db.add(newChunk).getIndex(); - } catch (IOException ex) { - throw (NullPointerException) new NullPointerException().initCause(ex); - } - chunks.add(newChunkIndex); - chunkSizes.add(1); - } - - /** - * Get the elements count - * @return the size of the list - */ - @Override - public int size() { - int size = 0; - for (int chunkSize : this.chunkSizes) { - size += chunkSize; - } - return size; - } - - /** - * - * @return the count of chunks - */ - public int chunksCount() { - return this.chunkSizes.size(); - } - - /** - * Check if the list is empty - * @return true if the list is empty - */ - @Override - public boolean isEmpty() { - return this.size() <= 0; - } - - @Override - public boolean contains(Object o) { - if (o != null) { - for (long chunkIndex : chunks) { - try { - EntryReference> chunkRef = db.get(chunkIndex); - LightArrayList chunk = chunkRef.getValueReadOnlyUnsafe(); - if (chunk.contains(o)) { - return true; - } - } catch (IOException ex) { - throw (NullPointerException) new NullPointerException().initCause(ex); - } - } - } - return false; - } - - /** - * Use iteratorReferences() - */ - @Deprecated - @Override - public Iterator iterator() - { - throw new RuntimeException("iterator() isn't implemented!"); - } - - @Deprecated - @Override - public Iterator> iteratorReferences() { - throw new RuntimeException("iteratorReferences() isn't implemented!"); - } - - /** - * USE forEachReference INSTEAD, TO AVOID OUTOFMEMORY - * - * @param action - */ - @Deprecated - @Override - public void forEach(Consumer action) { - throw new RuntimeException("forEach() isn't implemented! Use forEachReferences() instead"); - } - - @Override - public void forEachReference(Consumer> action) { - Objects.requireNonNull(action); - // Iterate through all chunks - for (int i = 0; i < chunks.size(); i++) { - synchronized (cachedChunkLock) { - if (cachedChunkNumber != i) { - prepareAccessToChunk(i); - } - cachedChunk.forEachReference(action); - } - } - } - - /** - * toArray() isn't implemented! DO NOT USE IT. - * @return - */ - @SuppressWarnings("unchecked") - @Deprecated - @Override - public T[] toArray() { - if (true) { - throw new RuntimeException("toArray() isn't implemented!"); - } - T[] result = (T[]) new Object[this.size()]; - - long currentOffset = 0; - // Iterate through all chunks - for (int i = 0; i < chunks.size(); i++) { - final int currentChunkSize = chunkSizes.getInt(i); - final long chunkStartOffset = currentOffset; - currentOffset += currentChunkSize; - // Get chunk index - final long chunkIndex = chunks.getLong(i); - - try { - EntryReference> chunkRef = db.get(chunkIndex); - LightArrayList chunk = chunkRef.getValueReadOnlyUnsafe(); - for (int i1 = 0; i1 < chunk.size(); i1++) { - result[(int)(chunkStartOffset + i1)] = chunk.get(i); - } - } catch (IOException ex) { - throw (NullPointerException) new NullPointerException().initCause(ex); - } - } - return result; - } - - @SuppressWarnings("unchecked") - @Override - public T1[] toArray(T1[] a) { - throw new RuntimeException("toArray() isn't implemented!"); - } - - /** - * Use addEntry(o) - * @param o - * @return - */ - @Deprecated - @Override - public boolean add(T o) { - EntryReference ref = addEntry(o); - return ref != null; - } - - @Override - public EntryReference addEntry(T o) { - EntryReference ref = addToDatabase(o); - appendIndex(ref.getIndex()); - return ref; - } - - @Override - public boolean remove(Object o) { - final int removeOffset = indexOf(o); - return removeAt(removeOffset) != null; - } - - @Override - public boolean remove(EntryReference ref) { - final int removeOffset = indexOfEntry(ref); - return removeAt(removeOffset) != null; - } - - private T removeAt(int removeOffset) { - final VariableWrapper result = new VariableWrapper<>(null); - long currentOffset = 0; - if (removeOffset >= 0) { - // Iterate through all chunks - for (int i = 0; i < chunks.size(); i++) { - final int currentChunkSize = chunkSizes.getInt(i); - final long chunkStartOffset = currentOffset; - currentOffset += currentChunkSize; - // If the offset to remove is in the current chunk - if (currentOffset > removeOffset) { - // Get chunk index - final long chunkIndex = chunks.getLong(i); - // Get the offset relative to the current chunk - final int relativeOffset = (int) (removeOffset - chunkStartOffset); - - if (relativeOffset < 0) { - return null; - } - - try { - EntryReference> chunkRef = db.get(chunkIndex); - chunkRef.editValue((chunk) -> { - result.var = chunk.remove(relativeOffset); - }); - } catch (IOException ex) { - throw (NullPointerException) new NullPointerException().initCause(ex); - } - chunkSizes.set(removeOffset, currentChunkSize - 1); - break; - } - } - return result.var; - } - return null; - } - - @Override - public boolean containsAll(Collection c) { - for (Object o : c) { - int objIndex = indexOf(o); - if (objIndex < 0) { - return false; - } - } - return true; - } - - @SuppressWarnings("unchecked") - @Override - public boolean addAll(Collection c) { - boolean result = false; - for (Object o : c) { - result |= add((T) o); - } - return result; - } - - @SuppressWarnings("unchecked") - @Override - public boolean addAll(int index, Collection c) { - boolean result = false; - int delta = 0; - for (Object o : c) { - add(index + delta, (T) o); - result = true; - delta++; - } - return result; - } - - @SuppressWarnings("unchecked") - @Override - public boolean removeAll(Collection c) { - boolean result = false; - for (Object o : c) { - result |= remove((T) o); - } - return result; - } - - @Override - public boolean retainAll(Collection c) { - boolean result = false; - LongArrayList collectionHashes = new LongArrayList(); - ObjectArrayList collection = new ObjectArrayList<>(); - collection.addAll(c); - for (Object o : c) { - collectionHashes.add(db.calculateHash(o)); - } - for (int i = 0; i < chunks.size(); i++) { - long hash = chunks.getLong(i); - int positionInCollection = collectionHashes.indexOf(hash); - if (positionInCollection == -1) { - remove(collection.get(positionInCollection)); - result = true; - } - } - return result; - } - - @Override - public void clear() { - chunks.clear(); - chunkSizes.clear(); - } - - /** - * Use getReference or getReadOnlyValue - */ - @Deprecated - @Override - public T get(int index) { - return getReadOnlyValue(index); - } - - @SuppressWarnings("unchecked") - public T getReadOnlyValue(int index) { - try { - return (T) db.get(chunks.getLong(index)).getValueReadOnlyUnsafe(); - } catch (IOException e) { - e.printStackTrace(); - return null; - } - } - - @Override - public EntryReference getReference(int index) { - return getReferenceUnsafe(index, true); - } - - @Override - public EntryReference getReferenceOrInitialize(int index, Supplier initializer) { - EntryReference value = getReferenceUnsafe(index, false); - if (value != null) { - return value; - } else { - T initializedData = initializer.get(); - EntryReference initializedDataRef = addToDatabase(initializedData); - return set(index, initializedDataRef); - } - } - - private EntryReference getReferenceUnsafe(int index, boolean throwError) { - try { - return db.get(chunks.getLong(index)).cast(); - } catch (IOException e) { - if (throwError) e.printStackTrace(); - return null; - } - } - - @SuppressWarnings("unchecked") - @Override - public T set(int setOffset, final T element) { - return set(setOffset, addToDatabase(element)).getValueReadOnlyUnsafe(); - } - - @SuppressWarnings("unchecked") - @Override - public EntryReference set(int setOffset, final EntryReference element) { - long nextChunkOffset = 0; - VariableWrapper> wrapper = new VariableWrapper<>(null); - if (setOffset >= 0) { - // Iterate through all chunks - for (int i = 0; i < chunks.size(); i++) { - final int currentChunkSize = chunkSizes.getInt(i); - final long chunkStartOffset = nextChunkOffset; - nextChunkOffset += currentChunkSize; - // If the offset to remove is in the current chunk - if (nextChunkOffset > setOffset) { - // Get chunk index - final long chunkIndex = chunks.getLong(i); - // Get the offset relative to the current chunk - final int relativeOffset = (int) (setOffset - chunkStartOffset); - - if (relativeOffset < 0) { - throw new NullPointerException("Relative Offset < 0"); - } - - try { - EntryReference> chunkRef = db.get(chunkIndex); - chunkRef.editValue((chunk) -> { - wrapper.var = chunk.set(relativeOffset, element); - }); - } catch (IOException ex) { - throw (NullPointerException) new NullPointerException().initCause(ex); - } - break; - } - } - return wrapper.var; - } - return null; - } - - @Override - public void add(int index, T element) { - throw new RuntimeException("add() isn't implemented!"); - } - - @SuppressWarnings("unchecked") - @Override - public T remove(int index) { - return this.removeAt(index); - } - - @Override - public int indexOf(Object o) { - EntryReference ref = addToDatabase(o).cast(); - return indexOfEntry(ref); - } - - @Override - public int indexOfEntry(EntryReference ref) { - int currentOffset = 0; - // Iterate through all chunks - for (int i = 0; i < chunks.size(); i++) { - try { - final int currentChunkSize = chunkSizes.getInt(i); - // If the offset to remove is in the current chunk - - // Get chunk index - final long chunkIndex = chunks.getLong(i); - EntryReference> chunkRef = db.get(chunkIndex); - final int foundIndex = chunkRef.getValueReadOnlyUnsafe().indexOfEntry(ref); - if (foundIndex >= 0) { - return currentOffset + foundIndex; - } - currentOffset += currentChunkSize; - } catch (IOException ex) { - throw (NullPointerException) new NullPointerException().initCause(ex); - } - } - return -1; - } - - @Override - public int lastIndexOf(Object o) { - return lastIndexOfEntry(addToDatabase(o).cast()); - } - - @Override - public int lastIndexOfEntry(EntryReference ref) { - int currentOffset = 0; - // Iterate through all chunks - for (int i = chunks.size() - 1; i >= 0; i--) { - try { - final int currentChunkSize = chunkSizes.getInt(i); - // If the offset to remove is in the current chunk - - // Get chunk index - final long chunkIndex = chunks.getLong(i); - EntryReference> chunkRef = db.get(chunkIndex); - final int foundIndex = chunkRef.getValueReadOnlyUnsafe().lastIndexOfEntry(ref); - if (foundIndex >= 0) { - return currentOffset + foundIndex; - } - currentOffset += currentChunkSize; - } catch (IOException ex) { - throw (NullPointerException) new NullPointerException().initCause(ex); - } - } - return -1; - } - - @Deprecated - @Override - public ListIterator listIterator() { - // TODO: implement - throw new RuntimeException("Not implemented!"); - } - - @Deprecated - @Override - public ListIterator listIterator(int index) { - // TODO: implement - throw new RuntimeException("Not implemented!"); - } - - @Deprecated - @Override - public List subList(int fromIndex, int toIndex) { - // TODO: implement - throw new RuntimeException("Not implemented!"); - } - - private EntryReference addToDatabase(U obj) { - EntryReference ref; - try { - ref = db.add(obj); - } catch (IOException e) { - throw (NullPointerException) new NullPointerException().initCause(e); - } - return ref; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((chunks == null) ? 0 : chunks.hashCode()); - return result; - } - - @SuppressWarnings("unchecked") - @Override - public boolean removeIf(Predicate filter) { - Objects.requireNonNull(filter); - boolean result = false; - // Iterate through all chunks - for (int i = 0; i < chunks.size(); i++) { - synchronized (cachedChunkLock) { - if (cachedChunkNumber != i) { - prepareAccessToChunk(i); - } - if (cachedChunk.removeIf(filter)) { - result = true; - chunkSizes.set(cachedChunkNumber, cachedChunk.size()); - } - } - } - return result; - } - - @Override - public String toString() { - return "LightBigList{" + - "chunks=" + chunks + - ", chunkSizes=" + chunkSizes + - ", db=" + db + - '}'; - } - - @Override - public void save() { - if (this.cachedChunkRef != null) { - this.cachedChunkRef.save(); - } - } - - @Override - public void saveAndFlush() { - save(); - } -} diff --git a/src/main/java/org/warp/jcwdb/LightList.java b/src/main/java/org/warp/jcwdb/LightList.java deleted file mode 100644 index 1f9fc7d..0000000 --- a/src/main/java/org/warp/jcwdb/LightList.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.warp.jcwdb; - -import java.util.Iterator; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public interface LightList extends List { - - Iterator> iteratorReferences(); - - void forEachReference(Consumer> action); - - EntryReference addEntry(T o); - - EntryReference set(int setOffset, final EntryReference element); - - boolean remove(EntryReference ref); - - EntryReference getReference(int index); - - EntryReference getReferenceOrInitialize(int index, Supplier initializer); - - int indexOfEntry(EntryReference ref); - - int lastIndexOfEntry(EntryReference ref); - - void appendIndex(long elementIndex); -} diff --git a/src/main/java/org/warp/jcwdb/MixedIndexDatabase.java b/src/main/java/org/warp/jcwdb/MixedIndexDatabase.java deleted file mode 100644 index 4806a74..0000000 --- a/src/main/java/org/warp/jcwdb/MixedIndexDatabase.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.warp.jcwdb; - -import java.io.IOException; -import java.nio.file.Path; - -public class MixedIndexDatabase implements IndexManager { - private final FileIndexManager fileIndices; - private final CacheIndexManager cacheIndices; - - public MixedIndexDatabase(Path dataFile, Path metadataFile) throws IOException { - this.fileIndices = new FileIndexManager(dataFile, metadataFile); - this.cacheIndices = new CacheIndexManager(); - } - - @Override - public T get(long index, DBReader reader) throws IOException { - if (cacheIndices.has(index)) { - return cacheIndices.get(index, reader); - } else { - return fileIndices.get(index, reader); - } - } - - @Override - public int getType(long index) throws IOException { - if (cacheIndices.has(index)) { - return cacheIndices.getType(index); - } else { - return fileIndices.getType(index); - } - } - - @Override - public long getHash(long index) throws IOException { - if (cacheIndices.has(index)) { - return cacheIndices.getHash(index); - } else { - return fileIndices.getHash(index); - } - } - - @Override - public long add(DBDataOutput writer) throws IOException { - return fileIndices.add(writer); - } - - - @Override - public FullIndexDetails addAndGetDetails(DBDataOutput writer) throws IOException { - return fileIndices.addAndGetDetails(writer); - } - @Override - public IndexDetails set(long index, DBDataOutput writer) throws IOException { - if (cacheIndices.has(index)) { - return cacheIndices.set(index, writer); - } else { - return fileIndices.set(index, writer); - } - } - @Override - public void setFlushingAllowed(long index, boolean isFlushingAllowed) { - if (cacheIndices.has(index)) { - cacheIndices.setFlushingAllowed(index, isFlushingAllowed); - } else { - fileIndices.setFlushingAllowed(index, isFlushingAllowed); - } - } - - @Override - public void delete(long index) throws IOException { - cacheIndices.delete(index); - fileIndices.delete(index); - } - - @Override - public boolean has(long index) { - return cacheIndices.has(index) || fileIndices.has(index); - } - - @Override - public void close() throws IOException { - // TODO: move all cached indices to filesIndices before closing. - this.cacheIndices.close(); - this.fileIndices.close(); - } - - @Override - public long clean() { - return fileIndices.clean() - + cacheIndices.clean(); - } -} diff --git a/src/main/java/org/warp/jcwdb/TypesManager.java b/src/main/java/org/warp/jcwdb/TypesManager.java deleted file mode 100644 index 6e4abde..0000000 --- a/src/main/java/org/warp/jcwdb/TypesManager.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.warp.jcwdb; - -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; - -public class TypesManager { - private final Int2ObjectMap> types; - private final Object2ObjectMap, DBTypeParser> typesByClass; - private DBTypedObjectParser fallbackParser; - - public TypesManager(JCWDatabase db) { - types = new Int2ObjectOpenHashMap<>(); - typesByClass = new Object2ObjectOpenHashMap<>(); - DBStandardTypes.registerStandardTypes(db, this); - } - - public void registerType(Class clazz, int type, DBTypeParser parser) { - this.types.put(type, parser); - this.typesByClass.put(clazz, parser); - } - - /** - * Use this method with the most used classes to save disk space. - * @param clazz - * @param id - * @param - */ - public void registerGenericClass(Class clazz, int id) { - this.fallbackParser.registerClass(clazz, id); - } - - public void registerTypeFallback(DBTypedObjectParser parser) { - this.fallbackParser = parser; - } - - public DBTypeParser get(int type) { - if (types.containsKey(type) == false) { - if (fallbackParser == null) { - throw new NoParserFoundException("The type " + type + " can't be parsed."); - } else { - return fallbackParser.cast(); - } - } - return types.get(type).cast(); - } - - public DBTypeParser get(Class type) { - if (typesByClass.containsKey(type) == false) { - if (fallbackParser == null) { - throw new NoParserFoundException("The class " + type.getSimpleName() + " can't be parsed."); - } else { - return fallbackParser.cast(); - } - } - return typesByClass.get(type).cast(); - } -} diff --git a/src/main/java/org/warp/jcwdb/VariableWrapper.java b/src/main/java/org/warp/jcwdb/VariableWrapper.java index 0503674..df7da5b 100644 --- a/src/main/java/org/warp/jcwdb/VariableWrapper.java +++ b/src/main/java/org/warp/jcwdb/VariableWrapper.java @@ -1,7 +1,5 @@ package org.warp.jcwdb; -import java.nio.channels.SeekableByteChannel; - public class VariableWrapper { public T var; diff --git a/src/main/java/org/warp/jcwdb/ann/DBClass.java b/src/main/java/org/warp/jcwdb/ann/DBClass.java new file mode 100644 index 0000000..1829d2f --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DBClass.java @@ -0,0 +1,9 @@ +package org.warp.jcwdb.ann; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface DBClass { + int classTypeId(); +} diff --git a/src/main/java/org/warp/jcwdb/ann/DBDataType.java b/src/main/java/org/warp/jcwdb/ann/DBDataType.java new file mode 100644 index 0000000..8e2dab0 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DBDataType.java @@ -0,0 +1,7 @@ +package org.warp.jcwdb.ann; + +public enum DBDataType { + DATABASE_OBJECT, + OBJECT, + INTEGER +} \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/ann/DBField.java b/src/main/java/org/warp/jcwdb/ann/DBField.java new file mode 100644 index 0000000..0b51e0d --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DBField.java @@ -0,0 +1,13 @@ +package org.warp.jcwdb.ann; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface DBField { + int id(); + DBDataType type() default DBDataType.OBJECT; +} diff --git a/src/main/java/org/warp/jcwdb/ann/DBObject.java b/src/main/java/org/warp/jcwdb/ann/DBObject.java new file mode 100644 index 0000000..f6e58f1 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DBObject.java @@ -0,0 +1,106 @@ +package org.warp.jcwdb.ann; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public abstract class DBObject { + private final Database database; + private final long uid; + 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; + + public DBObject(Database database) { + this.database = database; + this.uid = database.newDBObject(this); + database.preloadDBObject(this); + } + + public DBObject(Database database, long uid) { + this.database = database; + this.uid = uid; + database.preloadDBObject(this); + } + + public 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 (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + void setLoadedProperty(int propertyId, T value) { + loadedPropertyValues[propertyId] = value; + } + + @SuppressWarnings("unchecked") + private T getProperty(int propertyId) { + if (!loadedProperties[propertyId]) { + try { + database.loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUIDs[propertyId]); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { + throw new RuntimeException(e); + } + } + return (T) loadedPropertyValues[propertyId]; + } + + public 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)); + try { + int propertyId = stackFrame.getDeclaringClass().getDeclaredMethod(stackFrame.getMethodName(), value.getClass()).getAnnotation(DBPropertySetter.class).id(); + setProperty(propertyId, value); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public void setProperty(int propertyId, T value) { + loadedPropertyValues[propertyId] = value; + loadedProperties[propertyId] = true; + } + + public void close() { + database.saveObject(this); + } + + public final void setFields(Field[] fields, DBDataType[] fieldTypes, long[] fieldUIDs) { + this.fields = fields; + this.fieldTypes = fieldTypes; + this.fieldUIDs = fieldUIDs; + } + + public final void setProperties(Method[] propertyGetters, Method[] propertySetters, DBDataType[] propertyTypes, long[] propertyUIDs) { + 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]; + } + + public final long getUID() { + return uid; + } + + public long[] getAllFieldUIDs() { + return fieldUIDs; + } + + public long[] getAllPropertyUIDs() { + return propertyUIDs; + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java b/src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java new file mode 100644 index 0000000..ca1f368 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java @@ -0,0 +1,74 @@ +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; + + public DBObjectIndicesManager(FileIndexManager indices) { + this.indices = indices; + } + + public long allocate(int fieldsCount, int propertiesCount) { + return indices.add(calculateObjectSize(fieldsCount, propertiesCount)); + } + + 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 < fields.length; i++) { + w.writeLong(properties[i]); + } + }); + } + + public DBObjectInfo get(long uid) throws IOException { + return indices.get(uid, (i, size) -> { + 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); + }); + } + + 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; + } + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/DBPropertyGetter.java b/src/main/java/org/warp/jcwdb/ann/DBPropertyGetter.java new file mode 100644 index 0000000..a628213 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DBPropertyGetter.java @@ -0,0 +1,13 @@ +package org.warp.jcwdb.ann; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface DBPropertyGetter { + int id(); + DBDataType type() default DBDataType.OBJECT; +} diff --git a/src/main/java/org/warp/jcwdb/ann/DBPropertySetter.java b/src/main/java/org/warp/jcwdb/ann/DBPropertySetter.java new file mode 100644 index 0000000..713ada4 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DBPropertySetter.java @@ -0,0 +1,13 @@ +package org.warp.jcwdb.ann; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface DBPropertySetter { + int id(); + DBDataType type() default DBDataType.OBJECT; +} diff --git a/src/main/java/org/warp/jcwdb/ann/Database.java b/src/main/java/org/warp/jcwdb/ann/Database.java new file mode 100644 index 0000000..a652706 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/Database.java @@ -0,0 +1,275 @@ +package org.warp.jcwdb.ann; + +import com.esotericsoftware.kryo.Kryo; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.commons.lang3.reflect.MethodUtils; +import org.warp.jcwdb.FileIndexManager; + +import java.io.IOError; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.LinkedList; + +public class Database { + + public static final long MAX_LOADED_INDICES = 100000; + private final DBObjectIndicesManager objectIndicesManager; + private final FileIndexManager indices; + private final LinkedList> loadedObjects = new LinkedList<>(); + private static final Kryo kryo = new Kryo(); + + public Database(Path dataFile, Path metadataFile) throws IOException { + this.indices = new FileIndexManager(dataFile, metadataFile); + this.objectIndicesManager = new DBObjectIndicesManager(this.indices); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + Database.this.close(); + } catch (Exception e) { + e.printStackTrace(); + } + })); + } + + private void close() throws IOException { + indices.close(); + for (WeakReference loadedObjectReference : loadedObjects) { + DBObject loadedObject = loadedObjectReference.get(); + if (loadedObject != null) { + loadedObject.close(); + } + } + } + + public void preloadDBObject(DBObject obj) { + DBObjectIndicesManager.DBObjectInfo UIDs = readUIDs(obj.getUID()); + + preloadDBObjectFields(obj, UIDs.getFields()); + preloadDBObjectProperties(obj, UIDs.getProperties()); + } + + private void preloadDBObjectFields(DBObject obj, long[] fieldUIDs) { + // 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(); + try { + loadField(obj, field, fieldType, fieldUIDs[fieldId]); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { + throw new RuntimeException(e); + } + fields[fieldId] = field; + orderedFieldTypes[fieldId] = fieldType; + } + // Set fields metadata + obj.setFields(fields, orderedFieldTypes, fieldUIDs); + } + + private void preloadDBObjectProperties(DBObject obj, long[] propertyUIDs) { + // Declare the variables needed to get the biggest property Id + Method[] unorderedPropertyGetters = getPropertyGetters(obj); + Method[] unorderedPropertySetters = getPropertySetters(obj); + + // 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]; + + // 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; + } + for (Method property : unorderedPropertySetters) { + DBPropertySetter propertyAnnotation = property.getAnnotation(DBPropertySetter.class); + int propertyId = propertyAnnotation.id(); + DBDataType propertyType = propertyAnnotation.type(); + propertyTypes[propertyId] = propertyType; + propertySetters[propertyId] = property; + } + // Set properties metadata + obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs); + } + + private Method[] getPropertyGetters(DBObject obj) { + return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertyGetter.class); + } + + private Method[] getPropertySetters(DBObject obj) { + return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertySetter.class); + } + + private Field[] getFields(DBObject obj) { + return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class); + } + + private int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) { + int biggestPropertyId = -1; + for (Method property : unorderedPropertyGetters) { + DBPropertyGetter fieldAnnotation = property.getAnnotation(DBPropertyGetter.class); + int propertyId = fieldAnnotation.id(); + if (propertyId > biggestPropertyId) { + biggestPropertyId = propertyId; + } + } + return biggestPropertyId; + } + + private int getBiggestPropertySetterId(Method[] unorderedPropertySetters) { + int biggestPropertyId = -1; + for (Method property : unorderedPropertySetters) { + DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class); + int propertyId = fieldAnnotation.id(); + if (propertyId > biggestPropertyId) { + biggestPropertyId = propertyId; + } + } + return biggestPropertyId; + } + + + private int getBiggestFieldId(Field[] unorderedFields) { + 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 NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + switch (propertyType) { + case DATABASE_OBJECT: + DBObject fieldDBObjectValue = (DBObject) property.getDeclaringClass().getConstructor(Database.class, long.class).newInstance(this, propertyUID); + obj.setLoadedProperty(propertyId, fieldDBObjectValue); + break; + case OBJECT: + Object fieldObjectValue = loadObject(propertyUID); + obj.setLoadedProperty(propertyId, fieldObjectValue); + break; + case INTEGER: + int fieldIntValue = loadInt(propertyUID); + obj.setLoadedProperty(propertyId, fieldIntValue); + break; + default: + throw new NullPointerException("Unknown Field Type"); + } + } + + public void loadField(DBObject obj, Field field, DBDataType fieldType, long fieldUID) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + switch (fieldType) { + case DATABASE_OBJECT: + DBObject fieldDBObjectValue = (DBObject) field.getDeclaringClass().getConstructor(Database.class, long.class).newInstance(this, fieldUID); + field.set(obj, fieldDBObjectValue); + break; + case OBJECT: + Object fieldObjectValue = loadObject(fieldUID); + field.set(obj, fieldObjectValue); + break; + case INTEGER: + int fieldIntValue = loadInt(fieldUID); + field.setInt(obj, fieldIntValue); + break; + default: + throw new NullPointerException("Unknown Field Type"); + } + } + + @SuppressWarnings("unchecked") + public T loadObject(long uid) { + try { + return (T) indices.get(uid, (i, size) -> size == 0 ? null : kryo.readClassAndObject(i)); + } catch (IOException ex) { + throw new IOError(ex); + } + } + + public int loadInt(long uid) { + try { + return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readInt()); + } catch (IOException ex) { + throw new IOError(ex); + } + } + + public void watchObject(DBObject obj) { + loadedObjects.add(new WeakReference<>(obj)); + } + + /** + * + * @param uid + * @return + */ + private DBObjectIndicesManager.DBObjectInfo readUIDs(long uid) { + try { + return objectIndicesManager.get(uid); + } catch (IOException e) { + throw new IOError(e); + } + } + + public void saveObject(DBObject obj) { + System.out.println("Saving object " + obj.getUID()); + try { + objectIndicesManager.set(obj.getUID(), obj.getAllFieldUIDs(), obj.getAllPropertyUIDs()); + } catch (IOException e) { + throw new IOError(e); + } + } + + public long newDBObject(DBObject obj) { + int fieldsCount = getBiggestFieldId(getFields(obj)) + 1; + int biggestGetter = getBiggestPropertyGetterId(getPropertyGetters(obj)); + int biggestSetter = getBiggestPropertySetterId(getPropertySetters(obj)); + int propertiesCount = (biggestGetter > biggestSetter ? biggestGetter : biggestSetter) + 1; + + long uid = objectIndicesManager.allocate(fieldsCount, propertiesCount); + long[] fields = new long[fieldsCount]; + for (int i = 0; i < fieldsCount; i++) { + fields[i] = indices.add(0); + } + long[] properties = new long[propertiesCount]; + for (int i = 0; i < propertiesCount; i++) { + properties[i] = indices.add(0); + } + try { + objectIndicesManager.set(uid, fields, properties); + } catch (IOException e) { + throw new IOError(e); + } + return uid; + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java new file mode 100644 index 0000000..5622816 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java @@ -0,0 +1,40 @@ +package org.warp.jcwdb.ann.exampleimpl; + +import org.warp.jcwdb.ann.*; + +@DBClass(classTypeId = 0) +public class Class1 extends DBObject { + public Class1(Database database) { + super(database); + } + + public Class1(Database database, long uid) { + super(database, uid); + } + + @DBField(id = 0, type = DBDataType.OBJECT) + public String value1; + + @DBField(id = 1, type = DBDataType.INTEGER) + public int value2; + + @DBPropertyGetter(id = 0, type = DBDataType.OBJECT) + public String getValue3() { + return getProperty(); + } + + @DBPropertySetter(id = 0, type = DBDataType.OBJECT) + public void setValue3(String value) { + setProperty(value); + } + + @DBPropertyGetter(id = 1, type = DBDataType.DATABASE_OBJECT) + public Class1 getValue4() { + return getProperty(); + } + + @DBPropertySetter(id = 1, type = DBDataType.DATABASE_OBJECT) + public void setValue4(Class1 value) { + setProperty(value); + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java new file mode 100644 index 0000000..259169f --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java @@ -0,0 +1,32 @@ +package org.warp.jcwdb.ann.exampleimpl; + +import org.warp.jcwdb.ann.Database; + +import java.io.IOException; +import java.nio.file.Paths; + +public class Main { + + public static void main(String[] args) throws IOException { + Database db = new Database(Paths.get("database_temp.db"), Paths.get("database_temp.idx")); + Class1 class1 = new Class1(db, 0); + class1.value1 = "ciao"; + class1.value2 = 3; + System.out.println("value3="+class1.getValue3()); + class1.setValue3("Ciao 3"); + System.out.println("value3="+class1.getValue3()); + + Class1 nested = new Class1(db); + class1.setValue4(nested); + nested.setValue3("Ciao nested 3"); + try { + class1.close(); + nested.close(); + System.out.println(class1.getValue4().getValue3()); + } catch (Exception ex) { + ex.printStackTrace(); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/exampleimpl/Animal.java b/src/main/java/org/warp/jcwdb/exampleimpl/Animal.java deleted file mode 100644 index 1f27eb9..0000000 --- a/src/main/java/org/warp/jcwdb/exampleimpl/Animal.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.warp.jcwdb.exampleimpl; - -public abstract class Animal { - protected int legsCount; - - public static boolean hasFourLegs(Animal a) { - return a.legsCount == 4; - } -} diff --git a/src/main/java/org/warp/jcwdb/exampleimpl/App.java b/src/main/java/org/warp/jcwdb/exampleimpl/App.java deleted file mode 100644 index 60f9a49..0000000 --- a/src/main/java/org/warp/jcwdb/exampleimpl/App.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.warp.jcwdb.exampleimpl; - -import org.warp.jcwdb.EntryReference; -import org.warp.jcwdb.JCWDatabase; -import org.warp.jcwdb.LightList; - -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectList; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.function.Predicate; - -public class App { - static long time3; - public static void main(String[] args) throws IOException { - if (args.length > 2 && Boolean.parseBoolean(args[2])) { - Files.delete(Paths.get(args[0])); - Files.delete(Paths.get(args[1])); - } - System.out.println("Loading database..."); - long time0 = System.currentTimeMillis(); - JCWDatabase db = new JCWDatabase(Paths.get(args[0]), Paths.get(args[1])); - db.registerClass(StrangeAnimal.class, 0); - try { - long time01 = System.currentTimeMillis(); - System.out.println("Time elapsed: " + (time01 - time0)); - System.out.println("Loading root..."); - EntryReference> rootRef = db.getRoot(Animal.class); - rootRef.editValue((root, saver) -> { - long time1 = System.currentTimeMillis(); - System.out.println("Time elapsed: " + (time1 - time01)); - System.out.println("Root size: " + root.size()); - System.out.println("Root:"); -// for (int i = 0; i < root.size(); i++) { -// System.out.println(" - " + root.get(i)); -// } - long prectime = System.currentTimeMillis(); - for (int i = 0; i < 2000000/* 2000000 */; i++) { - Animal animal = new StrangeAnimal(i % 40); - root.addEntry(animal); - if (i > 0 && i % 200000 == 0) { - long precprectime = prectime; - prectime = System.currentTimeMillis(); - System.out.println("Element " + i + " (" + (prectime - precprectime) + "ms)" + " Total Time: " + (prectime - time1)); - } - } - long time2 = System.currentTimeMillis(); - saver.save(); - System.out.println("Root size: " + root.size()); - System.out.println("Time elapsed: " + (time2 - time1)); - System.out.println("Used memory: " - + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB"); - long time2_0 = System.currentTimeMillis(); - System.out.println("Filtering strings..."); - long oldSize = root.size(); - root.removeIf(Animal::hasFourLegs); - long time2_1 = System.currentTimeMillis(); - System.out.println("RemoveIf(x) removed items: " + (oldSize - root.size())); - System.out.println("Time elapsed: " + (time2_1 - time2_0)); - ObjectList results = new ObjectArrayList<>(); - - System.out.println("Retrieving items..."); - root.forEachReference((valueReference) -> { - Animal value = valueReference.getValueReadOnlyUnsafe(); - if (Animal.hasFourLegs(value)) { - results.add(value); - } - //System.out.println("val:" + value); - }); - long time2_2 = System.currentTimeMillis(); - System.out.println("Matches: " + results.size()); - System.out.println("Time elapsed: " + (time2_2 - time2_1)); - System.out.println("Used memory: " - + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB"); - System.out.println("Used memory: " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB"); - System.out.println("Cleaning database (to reduce the amount of used memory and detect memory leaks)..."); - db.clean(); - time3 = System.currentTimeMillis(); - System.out.println("Used memory: " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024) + "MB"); - System.out.println("Time elapsed: " + (time3 - time2_2)); - System.out.println("Saving database..."); - System.out.println("Root size: " + root.size()); - }); - db.close(); - long time4 = System.currentTimeMillis(); - System.out.println("Time elapsed: " + (time4 - time3)); - } catch (Exception ex) { - ex.printStackTrace(); - } finally { - if (db.isOpen()) { - db.close(); - } - } - } - - public static Predicate not(Predicate t) { - return t.negate(); - } -} diff --git a/src/main/java/org/warp/jcwdb/exampleimpl/Cat.java b/src/main/java/org/warp/jcwdb/exampleimpl/Cat.java deleted file mode 100644 index d289170..0000000 --- a/src/main/java/org/warp/jcwdb/exampleimpl/Cat.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.warp.jcwdb.exampleimpl; - -public class Cat extends Animal { - public Cat() { - this.legsCount = 12; - } - - @Override - public String toString() { - return "Cat [legsCount=" + legsCount + "]"; - } -} diff --git a/src/main/java/org/warp/jcwdb/exampleimpl/Dog.java b/src/main/java/org/warp/jcwdb/exampleimpl/Dog.java deleted file mode 100644 index 78b7489..0000000 --- a/src/main/java/org/warp/jcwdb/exampleimpl/Dog.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.warp.jcwdb.exampleimpl; - -public class Dog extends Animal { - public Dog() { - this.legsCount = 4; - } - - @Override - public String toString() { - return "Dog [legsCount=" + legsCount + "]"; - } -} diff --git a/src/main/java/org/warp/jcwdb/exampleimpl/StrangeAnimal.java b/src/main/java/org/warp/jcwdb/exampleimpl/StrangeAnimal.java deleted file mode 100644 index 4e3d84b..0000000 --- a/src/main/java/org/warp/jcwdb/exampleimpl/StrangeAnimal.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.warp.jcwdb.exampleimpl; - -public class StrangeAnimal extends Animal { - public StrangeAnimal() { - super(); - } - public StrangeAnimal(int legs) { - super(); - this.legsCount = legs; - } - @Override - public String toString() { - return "StrangeAnimal [legsCount=" + legsCount + "]"; - } -} From 41b2bb77a29c2e29460a3dd79912982ec3a34169 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Fri, 18 Jan 2019 02:35:55 +0100 Subject: [PATCH 2/5] Finished new implementation with annotations --- pom.xml | 162 ++++--- .../java/org/warp/jcwdb/FileAllocator.java | 15 +- .../java/org/warp/jcwdb/FileIndexManager.java | 120 ++--- .../java/org/warp/jcwdb/IndexManager.java | 1 - src/main/java/org/warp/jcwdb/ann/DBClass.java | 9 - .../org/warp/jcwdb/ann/DBDBObjectList.java | 35 ++ .../java/org/warp/jcwdb/ann/DBDataType.java | 3 +- src/main/java/org/warp/jcwdb/ann/DBList.java | 171 +++++++ .../java/org/warp/jcwdb/ann/DBObject.java | 211 ++++++--- .../jcwdb/ann/DBObjectIndicesManager.java | 19 +- .../java/org/warp/jcwdb/ann/DBObjectList.java | 25 + .../java/org/warp/jcwdb/ann/Database.java | 275 ----------- .../org/warp/jcwdb/ann/DatabaseManager.java | 447 ++++++++++++++++++ .../java/org/warp/jcwdb/ann/JCWDatabase.java | 32 ++ .../warp/jcwdb/ann/exampleimpl/Class1.java | 36 +- .../warp/jcwdb/ann/exampleimpl/Class2.java | 40 ++ .../jcwdb/ann/exampleimpl/ClassWithList.java | 48 ++ .../warp/jcwdb/ann/exampleimpl/IntClass.java | 27 ++ .../org/warp/jcwdb/ann/exampleimpl/Main.java | 67 ++- .../ann/exampleimpl/MainSingleClass.java | 52 ++ .../jcwdb/ann/exampleimpl/SingleClass.java | 68 +++ src/test/java/org/warp/jcwdb/AppTest.java | 53 +++ 22 files changed, 1402 insertions(+), 514 deletions(-) delete mode 100644 src/main/java/org/warp/jcwdb/ann/DBClass.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DBList.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DBObjectList.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/Database.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DatabaseManager.java create mode 100644 src/main/java/org/warp/jcwdb/ann/JCWDatabase.java create mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/Class2.java create mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/ClassWithList.java create mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/IntClass.java create mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/MainSingleClass.java create mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/SingleClass.java diff --git a/pom.xml b/pom.xml index bc00df0..e46f8ab 100644 --- a/pom.xml +++ b/pom.xml @@ -1,86 +1,94 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - org.warp - jcwdb - 1.0-SNAPSHOT + org.warp + jcwdb + 1.1-SNAPSHOT - jcwdb - - http://www.example.com + jcwdb + + http://www.example.com - - UTF-8 - 11 - 11 - + + UTF-8 + 10 + 10 + - - - junit - junit - 4.11 - test - - - it.unimi.dsi - fastutil - 8.2.2 - - - com.esotericsoftware - kryo - 5.0.0-RC1 - - - net.openhft - zero-allocation-hashing - 0.8 - - - - org.apache.commons - commons-lang3 - 3.8.1 - - + + + sonatype-snapshots + sonatype snapshots repo + https://oss.sonatype.org/content/repositories/snapshots + + - - - - - maven-clean-plugin - 3.0.0 - - - - maven-resources-plugin - 3.0.2 - - - maven-compiler-plugin - 3.7.0 - - - maven-surefire-plugin - 2.22.1 - - - maven-jar-plugin - 3.0.2 - - - maven-install-plugin - 2.5.2 - - - maven-deploy-plugin - 2.8.2 - - - - + + + junit + junit + 4.11 + test + + + it.unimi.dsi + fastutil + 8.2.2 + + + com.esotericsoftware + kryo + 5.0.0-RC2-SNAPSHOT + + + net.openhft + zero-allocation-hashing + 0.8 + + + + org.apache.commons + commons-lang3 + 3.8.1 + + + + + + + + maven-clean-plugin + 3.0.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.7.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + diff --git a/src/main/java/org/warp/jcwdb/FileAllocator.java b/src/main/java/org/warp/jcwdb/FileAllocator.java index 88f04d5..8809afd 100644 --- a/src/main/java/org/warp/jcwdb/FileAllocator.java +++ b/src/main/java/org/warp/jcwdb/FileAllocator.java @@ -29,7 +29,7 @@ public class FileAllocator implements AutoCloseable { this.fileSize = this.dataFileChannel.size(); } - public FileAllocator(SeekableByteChannel dataFileChannel, long fileSize, Long2IntMap freeBytes) throws IOException { + public FileAllocator(SeekableByteChannel dataFileChannel, long fileSize, Long2IntMap freeBytes) { this.dataFileChannel = dataFileChannel; this.fileSize = fileSize; this.freeBytes.putAll(freeBytes); @@ -44,16 +44,20 @@ public class FileAllocator implements AutoCloseable { public long allocate(int size) { checkClosed(); synchronized (allocateLock) { - long offset = allocateIntoUnusedParts(size); - if (offset == -1) { - return allocateToEnd(size); - } else { + 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> sorted = freeBytes.entrySet().stream() .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())); @@ -104,6 +108,7 @@ public class FileAllocator implements AutoCloseable { */ 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); diff --git a/src/main/java/org/warp/jcwdb/FileIndexManager.java b/src/main/java/org/warp/jcwdb/FileIndexManager.java index 1aea0fd..3f52372 100644 --- a/src/main/java/org/warp/jcwdb/FileIndexManager.java +++ b/src/main/java/org/warp/jcwdb/FileIndexManager.java @@ -4,7 +4,7 @@ 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.Database; +import org.warp.jcwdb.ann.DatabaseManager; import java.io.IOException; import java.nio.ByteBuffer; @@ -13,14 +13,14 @@ import java.nio.channels.SeekableByteChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.util.function.Consumer; public class FileIndexManager implements IndexManager { + public static final boolean ALWAYS_ALLOCATE_NEW = false; 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(Integer.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(); @@ -36,19 +36,17 @@ public class FileIndexManager implements IndexManager { /** * Edit this using editIndex() */ - private final LongSet dirtyLoadedIndices, flushingAllowedIndices, removedIndices; + 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(); - flushingAllowedIndices = new LongOpenHashSet(); removedIndices = new LongOpenHashSet(); } else { loadedIndices = new Long2ObjectLinkedOpenHashMap<>(); dirtyLoadedIndices = new LongLinkedOpenHashSet(); - flushingAllowedIndices = new LongLinkedOpenHashSet(); removedIndices = new LongLinkedOpenHashSet(); } if (Files.notExists(dataFile)) { @@ -59,53 +57,57 @@ public class FileIndexManager implements IndexManager { } dataFileChannel = Files.newByteChannel(dataFile, StandardOpenOption.READ, StandardOpenOption.WRITE); metadataFileChannel = Files.newByteChannel(metadataFile, StandardOpenOption.READ, StandardOpenOption.WRITE); - fileAllocator = createFileAllocator(dataFileChannel, metadataFileChannel.position(0)); metadataFileChannelSize = metadataFileChannel.size(); + fileAllocator = createFileAllocator(dataFileChannel, metadataFileChannel.position(0)); firstAllocableIndex = getMetadataFileChannelSize() / (long) IndexDetails.TOTAL_BYTES; - if (firstAllocableIndex == 0) { - firstAllocableIndex = 1; - } } - private long getMetadataFileChannelSize() throws IOException { + private long getMetadataFileChannelSize() { return metadataFileChannelSize; } private FileAllocator createFileAllocator(final SeekableByteChannel dataFileChannel, final SeekableByteChannel metadataFileChannel) throws IOException { - Long2IntMap freeBytes = new Long2IntRBTreeMap(); - Long2IntMap usedBytes = new Long2IntRBTreeMap(); - long firstOffset = 0; - while (metadataFileChannel.position() + IndexDetails.TOTAL_BYTES <= getMetadataFileChannelSize()) { - IndexDetails indexDetails = readIndexDetailsAt(metadataFileChannel); - if (indexDetails != null) { - long offset = indexDetails.getOffset(); - usedBytes.put(offset, indexDetails.getSize()); - if (offset < firstOffset) { - firstOffset = offset; + 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 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(); + long previousEntryOffset = 0; + long previousEntrySize = 0; + ObjectIterator 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))); + if (previousEntryOffset + previousEntrySize < entryOffset) { + freeBytes.put(previousEntryOffset + previousEntrySize, (int) (entryOffset - (previousEntryOffset + previousEntrySize))); + } + + previousEntryOffset = entryOffset; + previousEntrySize = entrySize; } - previousEntryOffset = entryOffset; - previousEntrySize = entrySize; + final long fileSize = previousEntryOffset + previousEntrySize; + + return new FileAllocator(dataFileChannel, fileSize, freeBytes); } - - final long fileSize = previousEntryOffset + previousEntrySize; - - return new FileAllocator(dataFileChannel, fileSize, freeBytes); } @Override @@ -121,7 +123,7 @@ public class FileIndexManager implements IndexManager { public IndexDetails set(long index, int size, DBWriter data) throws IOException { checkClosed(); IndexDetails indexDetails = getIndexMetadataUnsafe(index); - if (indexDetails == null || indexDetails.getSize() < size) { + if (ALWAYS_ALLOCATE_NEW || indexDetails == null || indexDetails.getSize() < size) { // Allocate new space IndexDetails newDetails = allocateAndWrite(index, size, data); if (indexDetails != null) { @@ -144,16 +146,6 @@ public class FileIndexManager implements IndexManager { } } - @Override - public void setFlushingAllowed(long index, boolean isUnloadingAllowed) { - checkClosed(); - if (isUnloadingAllowed) { - flushingAllowedIndices.add(index); - } else { - flushingAllowedIndices.remove(index); - } - } - @Override public long add(int size) { checkClosed(); @@ -192,7 +184,6 @@ public class FileIndexManager implements IndexManager { throw new IOException("Unable to write " + size + " in a space of " + indexDetails.getSize()); } final long offset = indexDetails.getOffset(); - final Output o = new Output(Channels.newOutputStream(dataFileChannel.position(offset)), size); data.write(o); o.flush(); @@ -214,7 +205,6 @@ public class FileIndexManager implements IndexManager { } synchronized (indicesMapsAccessLock) { dirtyLoadedIndices.remove(index); - flushingAllowedIndices.remove(index); loadedIndices.remove(index); removedIndices.add(index); } @@ -225,7 +215,6 @@ public class FileIndexManager implements IndexManager { synchronized (indicesMapsAccessLock) { removedIndices.remove(index); dirtyLoadedIndices.remove(index); - flushingAllowedIndices.remove(index); loadedIndices.remove(index); } // Update indices metadata @@ -238,7 +227,6 @@ public class FileIndexManager implements IndexManager { if (dirtyLoadedIndices.contains(index)) { indexDetails = loadedIndices.get(index); dirtyLoadedIndices.remove(index); - flushingAllowedIndices.remove(index); } } if (isDirty) { @@ -302,7 +290,6 @@ public class FileIndexManager implements IndexManager { synchronized (indicesMapsAccessLock) { loadedIndices.put(index, details); dirtyLoadedIndices.add(index); - flushingAllowedIndices.remove(index); } } @@ -311,7 +298,6 @@ public class FileIndexManager implements IndexManager { long newIndex = firstAllocableIndex++; loadedIndices.put(newIndex, indexDetails); dirtyLoadedIndices.add(newIndex); - flushingAllowedIndices.remove(newIndex); removedIndices.remove(newIndex); return newIndex; } @@ -442,21 +428,17 @@ public class FileIndexManager implements IndexManager { long lastIndex = -2; synchronized (indicesMapsAccessLock) { for (long index : dirtyLoadedIndices) { - if (!flushingAllowedIndices.contains(index)) { - 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++; + 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(); - dirtyLoadedIndices.addAll(flushingAllowedIndices); - flushingAllowedIndices.clear(); } return flushedIndices; } @@ -484,12 +466,12 @@ public class FileIndexManager implements IndexManager { long removedIndices = 0; LongArrayList toUnload = new LongArrayList(); synchronized (indicesMapsAccessLock) { - if (loadedIndices.size() > Database.MAX_LOADED_INDICES) { + 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 < Database.MAX_LOADED_INDICES * 3l / 2l) { + if (count < DatabaseManager.MAX_LOADED_INDICES * 3l / 2l) { break; } toUnload.add(loadedIndex); diff --git a/src/main/java/org/warp/jcwdb/IndexManager.java b/src/main/java/org/warp/jcwdb/IndexManager.java index 21f4320..be8fc37 100644 --- a/src/main/java/org/warp/jcwdb/IndexManager.java +++ b/src/main/java/org/warp/jcwdb/IndexManager.java @@ -13,7 +13,6 @@ public interface IndexManager extends Cleanable { 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 setFlushingAllowed(long index, boolean isUnloadingAllowed); void delete(long index) throws IOException; boolean has(long index); void close() throws IOException; diff --git a/src/main/java/org/warp/jcwdb/ann/DBClass.java b/src/main/java/org/warp/jcwdb/ann/DBClass.java deleted file mode 100644 index 1829d2f..0000000 --- a/src/main/java/org/warp/jcwdb/ann/DBClass.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.warp.jcwdb.ann; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Retention(RetentionPolicy.RUNTIME) -public @interface DBClass { - int classTypeId(); -} diff --git a/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java b/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java new file mode 100644 index 0000000..d22a5ca --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java @@ -0,0 +1,35 @@ +package org.warp.jcwdb.ann; + +import java.io.IOException; +import java.util.LinkedList; + +public class DBDBObjectList extends DBList { + + @DBField(id = 2, type = DBDataType.OBJECT) + public Class type; + + public DBDBObjectList(JCWDatabase db, Class type) { + super(db, 10); + this.type = type; + } + + public DBDBObjectList(JCWDatabase db, Class type, int maxLoadedItems) { + super(db, maxLoadedItems); + this.type = type; + } + + public DBDBObjectList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + @Override + protected T loadObjectFromDatabase(long uid) throws IOException { + return database.loadDBObject(type, uid); + } + + @Override + protected long saveObjectToDatabase(long uid, T value) throws IOException { + database.writeObjectProperty(uid, DBDataType.DATABASE_OBJECT, value); + return uid; + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/DBDataType.java b/src/main/java/org/warp/jcwdb/ann/DBDataType.java index 8e2dab0..0ced52c 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBDataType.java +++ b/src/main/java/org/warp/jcwdb/ann/DBDataType.java @@ -3,5 +3,6 @@ package org.warp.jcwdb.ann; public enum DBDataType { DATABASE_OBJECT, OBJECT, - INTEGER + INTEGER, + UID_LIST } \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/ann/DBList.java b/src/main/java/org/warp/jcwdb/ann/DBList.java new file mode 100644 index 0000000..685798c --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DBList.java @@ -0,0 +1,171 @@ +package org.warp.jcwdb.ann; + +import it.unimi.dsi.fastutil.longs.LongArrayList; + +import java.io.IOError; +import java.io.IOException; +import java.util.LinkedList; +import java.util.StringJoiner; + +public abstract class DBList extends DBObject { + @DBField(id = 0, type = DBDataType.INTEGER) + private int maxLoadedItems; + + @DBField(id = 1, type = DBDataType.UID_LIST) + private LongArrayList itemIndices; + + public DBList(JCWDatabase db, int maxLoadedItems) { + super(db); + this.maxLoadedItems = maxLoadedItems; + this.itemIndices = new LongArrayList(); + } + + public DBList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + public int getMaxLoadedItems() { + return maxLoadedItems; + } + + public void add(T obj) { + try { + itemIndices.add(saveObjectToDatabase(database.allocateNullValue(), obj)); + } catch (IOException e) { + throw new IOError(e); + } + } + + public int size() { + return this.itemIndices.size(); + } + + private T loadObjectAt(int i) throws IOException { + return loadObjectFromDatabase(itemIndices.getLong(i)); + } + + protected abstract T loadObjectFromDatabase(long uid) throws IOException; + + protected abstract long saveObjectToDatabase(long uid, T value) throws IOException; + + public DBListIterator iterator() { + return new DBListIterator<>() { + private int position = itemIndices.size(); + private LinkedList cachedItems; + + private int objectToSavePosition; + private T objectToSave; + + @Override + public boolean hasNext() { + if (position > 0) { + return true; + } else { + try { + saveObservedObject(); + } catch (IOException e) { + throw new IOError(e); + } + return false; + } + } + + @Override + public T next() { + position--; + if (position < 0) { + throw new NullPointerException("Position < 0"); + } + if (cachedItems == null || cachedItems.size() == 0) { + try { + cachedItems = fillCache(position); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + try { + return switchObservedObject(position, cachedItems.removeFirst()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void remove() { + try { + saveObservedObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + itemIndices.removeLong(position); + } + + @Override + public void insert(T obj) { + try { + itemIndices.add(position, saveObjectToDatabase(database.allocateNullValue(), obj)); + } catch (IOException e) { + throw new IOError(e); + } + } + + @Override + public void set(T obj) { + try { + itemIndices.set(position, saveObjectToDatabase(database.allocateNullValue(), obj)); + } catch (IOException e) { + throw new IOError(e); + } + } + + private LinkedList fillCache(int position) throws IOException { + LinkedList cachedItems = new LinkedList<>(); + int firstObjectIndex = position; + int lastObjectIndex = firstObjectIndex - maxLoadedItems; + if (lastObjectIndex < 0) { + lastObjectIndex = 0; + } + for (int i = firstObjectIndex; i >= lastObjectIndex; i--) { + cachedItems.addLast(loadObjectAt(i)); + } + return cachedItems; + } + + private void saveObservedObject() throws IOException { + if (objectToSave != null) { + itemIndices.set(objectToSavePosition, saveObjectToDatabase(database.allocateNullValue(), objectToSave)); + objectToSave = null; + objectToSavePosition = 0; + } + } + + private T switchObservedObject(int position, T obj) throws IOException { + saveObservedObject(); + objectToSave = obj; + objectToSavePosition = position; + return obj; + } + }; + } + + public interface DBListIterator { + boolean hasNext(); + + T next(); + + void remove(); + + void insert(T obj); + + void set(T obj); + } + + @Override + public String toString() { + return new StringJoiner(", ", DBList.class.getSimpleName() + "[", "]") + .add("size=" + size()) + .add("maxLoadedItems=" + maxLoadedItems) + .add("itemIndices=" + (itemIndices.size() < 100 ? itemIndices : "["+itemIndices.size()+" items]")) + .toString(); + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/DBObject.java b/src/main/java/org/warp/jcwdb/ann/DBObject.java index f6e58f1..da5a3ae 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBObject.java +++ b/src/main/java/org/warp/jcwdb/ann/DBObject.java @@ -1,12 +1,16 @@ package org.warp.jcwdb.ann; +import org.apache.commons.lang3.reflect.FieldUtils; + +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.util.LinkedHashMap; +import java.util.Map; public abstract class DBObject { - private final Database database; - private final long uid; + protected final DatabaseManager database; private Field[] fields; private DBDataType[] fieldTypes; private long[] fieldUIDs; @@ -17,17 +21,103 @@ public abstract class DBObject { private long[] propertyUIDs; private boolean[] loadedProperties; private Object[] loadedPropertyValues; + private Map setterMethods; + private Map getterMethods; + private final Object fieldsAccessLock = new Object(); + private final Object propertiesAccessLock = new Object(); - public DBObject(Database database) { - this.database = database; - this.uid = database.newDBObject(this); - database.preloadDBObject(this); + public DBObject(JCWDatabase database) { + this.database = database.getDatabaseManager(); + try { + initializeDBObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } } - public DBObject(Database database, long uid) { - this.database = database; - this.uid = uid; - database.preloadDBObject(this); + private void initializeDBObject() throws IOException { + initializeDBObjectFields(); + initializeDBObjectProperties(); + } + + private void initializeDBObjectFields() throws IOException { + // Declare the variables needed to get the biggest field Id + Field[] unorderedFields = database.getFields(this); + // Find the biggest field Id + int biggestFieldId = database.getBiggestFieldId(unorderedFields); + + // Allocate new UIDs + fieldUIDs = database.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(); + database.loadField(this, field, fieldType, fieldUIDs[fieldId]); + fields[fieldId] = field; + orderedFieldTypes[fieldId] = fieldType; + } + // Set fields metadata + setFields(fields, orderedFieldTypes, fieldUIDs); + } + + private void initializeDBObjectProperties() { + // Declare the variables needed to get the biggest property Id + Method[] unorderedPropertyGetters = database.getPropertyGetters(this); + Method[] unorderedPropertySetters = database.getPropertySetters(this); + + // Find the biggest property Id + int biggestGetter = database.getBiggestPropertyGetterId(unorderedPropertyGetters); + int biggestSetter = database.getBiggestPropertySetterId(unorderedPropertySetters); + int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter; + + // Allocate new UIDs + propertyUIDs = database.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 setterMethods = new LinkedHashMap<>(); + Map 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 + setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs, setterMethods, getterMethods); + } + + public DBObject(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + this.database = database.getDatabaseManager(); + this.database.preloadDBObject(this, objectInfo); } public T getProperty() { @@ -36,71 +126,86 @@ public abstract class DBObject { try { int propertyId = stackFrame.getDeclaringClass().getDeclaredMethod(stackFrame.getMethodName()).getAnnotation(DBPropertyGetter.class).id(); return getProperty(propertyId); - } catch (NoSuchMethodException e) { + } catch (IOException | NoSuchMethodException e) { throw new RuntimeException(e); } } void setLoadedProperty(int propertyId, T value) { loadedPropertyValues[propertyId] = value; + loadedProperties[propertyId] = true; } @SuppressWarnings("unchecked") - private T getProperty(int propertyId) { - if (!loadedProperties[propertyId]) { - try { - database.loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUIDs[propertyId]); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { - throw new RuntimeException(e); + private T getProperty(int propertyId) throws IOException { + synchronized (propertiesAccessLock) { + if (!loadedProperties[propertyId]) { + long propertyUID = propertyUIDs[propertyId]; + database.loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID); } + return (T) loadedPropertyValues[propertyId]; } - return (T) loadedPropertyValues[propertyId]; } public 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)); - try { - int propertyId = stackFrame.getDeclaringClass().getDeclaredMethod(stackFrame.getMethodName(), value.getClass()).getAnnotation(DBPropertySetter.class).id(); - setProperty(propertyId, value); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); + DBPropertySetter propertyAnnotation = setterMethods.get(stackFrame.getMethodName()); + setProperty(propertyAnnotation.id(), propertyAnnotation.type(), value); + } + + public void setProperty(int propertyId, DBDataType propertyType, T value) { + synchronized (propertiesAccessLock) { + loadedPropertyValues[propertyId] = value; + loadedProperties[propertyId] = true; } } - public void setProperty(int propertyId, T value) { - loadedPropertyValues[propertyId] = value; - loadedProperties[propertyId] = true; - } - - public void close() { - database.saveObject(this); + public void writeToDisk(long uid) { + //System.err.println("Saving object " + uid + ":" + this); + try { + synchronized (propertiesAccessLock) { + synchronized (fieldsAccessLock) { + database.writeObjectInfo(uid, fieldUIDs, propertyUIDs); + } + } + synchronized (fieldsAccessLock) { + for (int i = 0; i < fieldUIDs.length; i++) { + try { + database.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.writeObjectProperty(propertyUIDs[i], propertyTypes[i], loadedPropertyValues[i]); + } + } + } catch (IOException e) { + throw new IOError(e); + } } public final void setFields(Field[] fields, DBDataType[] fieldTypes, long[] fieldUIDs) { - this.fields = fields; - this.fieldTypes = fieldTypes; - this.fieldUIDs = fieldUIDs; + synchronized (fieldsAccessLock) { + this.fields = fields; + this.fieldTypes = fieldTypes; + this.fieldUIDs = fieldUIDs; + } } - public final void setProperties(Method[] propertyGetters, Method[] propertySetters, DBDataType[] propertyTypes, long[] propertyUIDs) { - 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]; - } - - public final long getUID() { - return uid; - } - - public long[] getAllFieldUIDs() { - return fieldUIDs; - } - - public long[] getAllPropertyUIDs() { - return propertyUIDs; + public final void setProperties(Method[] propertyGetters, Method[] propertySetters, DBDataType[] propertyTypes, long[] propertyUIDs, Map setterMethods, Map 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; + } } } diff --git a/src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java b/src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java index ca1f368..4005946 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java +++ b/src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java @@ -8,12 +8,18 @@ import java.io.IOException; public class DBObjectIndicesManager { private final FileIndexManager indices; - public DBObjectIndicesManager(FileIndexManager indices) { + DBObjectIndicesManager(FileIndexManager indices) { this.indices = indices; } public long allocate(int fieldsCount, int propertiesCount) { - return indices.add(calculateObjectSize(fieldsCount, 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 { @@ -23,7 +29,7 @@ public class DBObjectIndicesManager { for (int i = 0; i < fields.length; i++) { w.writeLong(fields[i]); } - for (int i = 0; i < fields.length; i++) { + for (int i = 0; i < properties.length; i++) { w.writeLong(properties[i]); } }); @@ -31,6 +37,9 @@ public class DBObjectIndicesManager { 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)) { @@ -46,6 +55,10 @@ public class DBObjectIndicesManager { }); } + public boolean has(long uid) { + return indices.has(uid); + } + private int calculateObjectSize(long[] fields, long[] properties) { return calculateObjectSize(fields.length, properties.length); } diff --git a/src/main/java/org/warp/jcwdb/ann/DBObjectList.java b/src/main/java/org/warp/jcwdb/ann/DBObjectList.java new file mode 100644 index 0000000..1fa796e --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DBObjectList.java @@ -0,0 +1,25 @@ +package org.warp.jcwdb.ann; + +import java.io.IOException; + +public class DBObjectList extends DBList { + + public DBObjectList(JCWDatabase db, int maxLoadedItems) { + super(db, maxLoadedItems); + } + + public DBObjectList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + @Override + protected T loadObjectFromDatabase(long uid) throws IOException { + return database.loadObject(uid); + } + + @Override + protected long saveObjectToDatabase(long uid, T value) throws IOException { + database.writeObjectProperty(uid, DBDataType.OBJECT, value); + return uid; + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/Database.java b/src/main/java/org/warp/jcwdb/ann/Database.java deleted file mode 100644 index a652706..0000000 --- a/src/main/java/org/warp/jcwdb/ann/Database.java +++ /dev/null @@ -1,275 +0,0 @@ -package org.warp.jcwdb.ann; - -import com.esotericsoftware.kryo.Kryo; -import org.apache.commons.lang3.reflect.FieldUtils; -import org.apache.commons.lang3.reflect.MethodUtils; -import org.warp.jcwdb.FileIndexManager; - -import java.io.IOError; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.file.Path; -import java.util.LinkedList; - -public class Database { - - public static final long MAX_LOADED_INDICES = 100000; - private final DBObjectIndicesManager objectIndicesManager; - private final FileIndexManager indices; - private final LinkedList> loadedObjects = new LinkedList<>(); - private static final Kryo kryo = new Kryo(); - - public Database(Path dataFile, Path metadataFile) throws IOException { - this.indices = new FileIndexManager(dataFile, metadataFile); - this.objectIndicesManager = new DBObjectIndicesManager(this.indices); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - Database.this.close(); - } catch (Exception e) { - e.printStackTrace(); - } - })); - } - - private void close() throws IOException { - indices.close(); - for (WeakReference loadedObjectReference : loadedObjects) { - DBObject loadedObject = loadedObjectReference.get(); - if (loadedObject != null) { - loadedObject.close(); - } - } - } - - public void preloadDBObject(DBObject obj) { - DBObjectIndicesManager.DBObjectInfo UIDs = readUIDs(obj.getUID()); - - preloadDBObjectFields(obj, UIDs.getFields()); - preloadDBObjectProperties(obj, UIDs.getProperties()); - } - - private void preloadDBObjectFields(DBObject obj, long[] fieldUIDs) { - // 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(); - try { - loadField(obj, field, fieldType, fieldUIDs[fieldId]); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { - throw new RuntimeException(e); - } - fields[fieldId] = field; - orderedFieldTypes[fieldId] = fieldType; - } - // Set fields metadata - obj.setFields(fields, orderedFieldTypes, fieldUIDs); - } - - private void preloadDBObjectProperties(DBObject obj, long[] propertyUIDs) { - // Declare the variables needed to get the biggest property Id - Method[] unorderedPropertyGetters = getPropertyGetters(obj); - Method[] unorderedPropertySetters = getPropertySetters(obj); - - // 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]; - - // 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; - } - for (Method property : unorderedPropertySetters) { - DBPropertySetter propertyAnnotation = property.getAnnotation(DBPropertySetter.class); - int propertyId = propertyAnnotation.id(); - DBDataType propertyType = propertyAnnotation.type(); - propertyTypes[propertyId] = propertyType; - propertySetters[propertyId] = property; - } - // Set properties metadata - obj.setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs); - } - - private Method[] getPropertyGetters(DBObject obj) { - return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertyGetter.class); - } - - private Method[] getPropertySetters(DBObject obj) { - return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertySetter.class); - } - - private Field[] getFields(DBObject obj) { - return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class); - } - - private int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) { - int biggestPropertyId = -1; - for (Method property : unorderedPropertyGetters) { - DBPropertyGetter fieldAnnotation = property.getAnnotation(DBPropertyGetter.class); - int propertyId = fieldAnnotation.id(); - if (propertyId > biggestPropertyId) { - biggestPropertyId = propertyId; - } - } - return biggestPropertyId; - } - - private int getBiggestPropertySetterId(Method[] unorderedPropertySetters) { - int biggestPropertyId = -1; - for (Method property : unorderedPropertySetters) { - DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class); - int propertyId = fieldAnnotation.id(); - if (propertyId > biggestPropertyId) { - biggestPropertyId = propertyId; - } - } - return biggestPropertyId; - } - - - private int getBiggestFieldId(Field[] unorderedFields) { - 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 NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - switch (propertyType) { - case DATABASE_OBJECT: - DBObject fieldDBObjectValue = (DBObject) property.getDeclaringClass().getConstructor(Database.class, long.class).newInstance(this, propertyUID); - obj.setLoadedProperty(propertyId, fieldDBObjectValue); - break; - case OBJECT: - Object fieldObjectValue = loadObject(propertyUID); - obj.setLoadedProperty(propertyId, fieldObjectValue); - break; - case INTEGER: - int fieldIntValue = loadInt(propertyUID); - obj.setLoadedProperty(propertyId, fieldIntValue); - break; - default: - throw new NullPointerException("Unknown Field Type"); - } - } - - public void loadField(DBObject obj, Field field, DBDataType fieldType, long fieldUID) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - switch (fieldType) { - case DATABASE_OBJECT: - DBObject fieldDBObjectValue = (DBObject) field.getDeclaringClass().getConstructor(Database.class, long.class).newInstance(this, fieldUID); - field.set(obj, fieldDBObjectValue); - break; - case OBJECT: - Object fieldObjectValue = loadObject(fieldUID); - field.set(obj, fieldObjectValue); - break; - case INTEGER: - int fieldIntValue = loadInt(fieldUID); - field.setInt(obj, fieldIntValue); - break; - default: - throw new NullPointerException("Unknown Field Type"); - } - } - - @SuppressWarnings("unchecked") - public T loadObject(long uid) { - try { - return (T) indices.get(uid, (i, size) -> size == 0 ? null : kryo.readClassAndObject(i)); - } catch (IOException ex) { - throw new IOError(ex); - } - } - - public int loadInt(long uid) { - try { - return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readInt()); - } catch (IOException ex) { - throw new IOError(ex); - } - } - - public void watchObject(DBObject obj) { - loadedObjects.add(new WeakReference<>(obj)); - } - - /** - * - * @param uid - * @return - */ - private DBObjectIndicesManager.DBObjectInfo readUIDs(long uid) { - try { - return objectIndicesManager.get(uid); - } catch (IOException e) { - throw new IOError(e); - } - } - - public void saveObject(DBObject obj) { - System.out.println("Saving object " + obj.getUID()); - try { - objectIndicesManager.set(obj.getUID(), obj.getAllFieldUIDs(), obj.getAllPropertyUIDs()); - } catch (IOException e) { - throw new IOError(e); - } - } - - public long newDBObject(DBObject obj) { - int fieldsCount = getBiggestFieldId(getFields(obj)) + 1; - int biggestGetter = getBiggestPropertyGetterId(getPropertyGetters(obj)); - int biggestSetter = getBiggestPropertySetterId(getPropertySetters(obj)); - int propertiesCount = (biggestGetter > biggestSetter ? biggestGetter : biggestSetter) + 1; - - long uid = objectIndicesManager.allocate(fieldsCount, propertiesCount); - long[] fields = new long[fieldsCount]; - for (int i = 0; i < fieldsCount; i++) { - fields[i] = indices.add(0); - } - long[] properties = new long[propertiesCount]; - for (int i = 0; i < propertiesCount; i++) { - properties[i] = indices.add(0); - } - try { - objectIndicesManager.set(uid, fields, properties); - } catch (IOException e) { - throw new IOError(e); - } - return uid; - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java b/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java new file mode 100644 index 0000000..7a986bd --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java @@ -0,0 +1,447 @@ +package org.warp.jcwdb.ann; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Registration; +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.Cleanable; +import org.warp.jcwdb.Cleaner; +import org.warp.jcwdb.FileIndexManager; +import org.warp.jcwdb.ann.exampleimpl.ClassWithList; + +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.*; + +public class DatabaseManager implements Cleanable { + + public static final long MAX_LOADED_INDICES = 100000; + private final DBObjectIndicesManager objectIndicesManager; + private final FileIndexManager indices; + private final Cleaner cleaner; + private final JCWDatabase jcwDatabase; + private DBObject loadedRootObject = null; + private final Kryo kryo = new Kryo(); + private volatile boolean closed; + + DatabaseManager(JCWDatabase jcwDatabase, Path dataFile, Path metadataFile) throws IOException { + this.jcwDatabase = jcwDatabase; + kryo.setRegistrationRequired(true); + registerDefaultClasses(); + this.indices = new FileIndexManager(dataFile, metadataFile); + if (!indices.has(0)) { + allocateNullValue(); + } + this.objectIndicesManager = new DBObjectIndicesManager(this.indices); + this.cleaner = new Cleaner(this); + this.cleaner.start(); + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + DatabaseManager.this.close(); + } catch (Exception e) { + e.printStackTrace(); + } + })); + } + + 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++); + } + + @SuppressWarnings("unchecked") + public T loadRoot(Class rootType) throws IOException { + if (loadedRootObject != null) { + throw new RuntimeException("Root already set!"); + } + if (isDBObjectNull(0)) { + try { + T root = rootType.getConstructor(JCWDatabase.class).newInstance(this.jcwDatabase); + loadedRootObject = root; + return root; + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new IOError(e); + } + } else { + T root = (T) loadDBObject(rootType, 0); + loadedRootObject = root; + return root; + } + } + + public void close() throws IOException { + if (!closed) { + closed = true; + DatabaseManager.this.cleaner.stop(); + if (loadedRootObject != null) { + loadedRootObject.writeToDisk(0); + } + indices.close(); + } + } + + public void preloadDBObject(DBObject obj, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + preloadDBObjectFields(obj, objectInfo.getFields()); + preloadDBObjectProperties(obj, objectInfo.getProperties()); + } + + private void preloadDBObjectFields(DBObject obj, long[] fieldUIDs) throws IOException { + // 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) { + // Declare the variables needed to get the biggest property Id + Method[] unorderedPropertyGetters = getPropertyGetters(obj); + Method[] unorderedPropertySetters = getPropertySetters(obj); + + // 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 setterMethods = new LinkedHashMap<>(); + Map 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); + } + + Method[] getPropertyGetters(DBObject obj) { + return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertyGetter.class); + } + + Method[] getPropertySetters(DBObject obj) { + return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertySetter.class); + } + + protected Field[] getFields(DBObject obj) { + return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class); + } + + int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) { + 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) { + 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) { + int biggestFieldId = -1; + for (Field field : unorderedFields) { + DBField fieldAnnotation = field.getAnnotation(DBField.class); + int propertyId = fieldAnnotation.id(); + if (propertyId > biggestFieldId) { + biggestFieldId = propertyId; + } + } + return biggestFieldId; + } + + @SuppressWarnings("unchecked") + public void loadProperty(DBObject obj, int propertyId, Method property, DBDataType propertyType, long propertyUID) throws IOException { + switch (propertyType) { + case DATABASE_OBJECT: + DBObject fieldDBObjectValue = loadDBObject((Class) property.getReturnType(), propertyUID); + //System.err.println("Loading prop. DBObj " + propertyUID + ":" + fieldDBObjectValue); + obj.setLoadedProperty(propertyId, fieldDBObjectValue); + break; + case OBJECT: + Object fieldObjectValue = loadObject(propertyUID); + //System.err.println("Loading prop. Obj " + propertyUID + ":" + fieldObjectValue); + obj.setLoadedProperty(propertyId, fieldObjectValue); + break; + case UID_LIST: + LongArrayList fieldListObjectValue = loadListObject(propertyUID); + //System.err.println("Loading prop. LOb " + propertyUID + ":" + fieldListObjectValue); + obj.setLoadedProperty(propertyId, fieldListObjectValue); + break; + case INTEGER: + int fieldIntValue = loadInt(propertyUID); + //System.err.println("Loading prop. Int " + propertyUID + ":" + fieldIntValue); + obj.setLoadedProperty(propertyId, fieldIntValue); + break; + default: + throw new NullPointerException("Unknown Field Type"); + } + } + + @SuppressWarnings("unchecked") + public void loadField(DBObject obj, Field field, DBDataType fieldType, long fieldUID) throws IOException { + try { + switch (fieldType) { + case DATABASE_OBJECT: + DBObject fieldDBObjectValue = loadDBObject((Class) field.getType(), fieldUID); + //System.err.println("Loading field DBObj " + fieldUID + ":" + fieldDBObjectValue); + FieldUtils.writeField(field, obj, fieldDBObjectValue, true); + break; + case OBJECT: + Object fieldObjectValue = loadObject(fieldUID); + //System.err.println("Loading field Obj " + fieldUID + ":" + fieldObjectValue); + FieldUtils.writeField(field, obj, fieldObjectValue, true); + break; + case UID_LIST: + LongArrayList fieldListObjectValue = loadListObject(fieldUID); + //System.err.println("Loading field LOb " + fieldUID + ":" + fieldObjectValue); + FieldUtils.writeField(field, obj, fieldListObjectValue, true); + break; + case INTEGER: + int fieldIntValue = loadInt(fieldUID); + //System.err.println("Loading field Int " + fieldUID + ":" + fieldIntValue); + FieldUtils.writeField(field, obj, fieldIntValue, true); + break; + default: + throw new NullPointerException("Unknown Field Type"); + } + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public T loadDBObject(Class type, long propertyUID) throws IOException { + try { + DBObjectIndicesManager.DBObjectInfo objectInfo = readUIDs(propertyUID); + if (objectInfo == null) return null; + return type.getDeclaredConstructor(JCWDatabase.class, DBObjectIndicesManager.DBObjectInfo.class).newInstance(jcwDatabase, objectInfo); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new IOException(e); + } + } + + private boolean isDBObjectNull(long uid) { + try { + return !objectIndicesManager.has(uid) || objectIndicesManager.get(uid) == null; + } catch (IOException ex) { + throw new IOError(ex); + } + } + + @SuppressWarnings("unchecked") + public T loadObject(long uid) throws IOException { + return indices.get(uid, (i, size) -> size == 0 ? null : (T) kryo.readClassAndObject(i)); + } + + @SuppressWarnings("unchecked") + private LongArrayList loadListObject(long uid) throws IOException { + return indices.get(uid, (i, size) -> { + if (size == 0) return null; + LongArrayList list = new LongArrayList(); + int listSize = i.readVarInt(true); + for (int li = 0; li < listSize; li++) { + list.add(i.readVarLong(true)); + } + return list; + }); + } + + public int loadInt(long uid) throws IOException { + return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readInt()); + } + + /** + * + * @param uid + * @return + */ + public DBObjectIndicesManager.DBObjectInfo readUIDs(long uid) { + try { + return objectIndicesManager.get(uid); + } catch (IOException e) { + throw new IOError(e); + } + } + + public boolean exists(long uid) { + return objectIndicesManager.has(uid); + } + + public void writeObjectInfo(long uid, long[] fieldUIDs, long[] propertyUIDs) throws IOException { + //System.err.println("Saving obj. " + uid); + this.objectIndicesManager.set(uid, fieldUIDs, propertyUIDs); + } + + private void writeObjectInfoNull(long uid) throws IOException { + this.objectIndicesManager.setNull(uid); + } + + public void writeObjectProperty(long uid, DBDataType propertyType, T loadedPropertyValue) throws IOException { + switch (propertyType) { + case INTEGER: + indices.set(uid, Integer.BYTES, (o) -> o.writeInt((int) loadedPropertyValue)); + //System.err.println("Saving prop. Int " + uid + ":" + loadedPropertyValue); + break; + case OBJECT: + Output baosOutput = new Output(new ByteArrayOutputStream()); + kryo.writeClassAndObject(baosOutput, loadedPropertyValue); + //System.err.println("Saving prop. Obj " + uid + ":" + loadedPropertyValue); + byte[] out = baosOutput.toBytes(); + indices.set(uid, out.length, o -> o.write(out, 0, out.length)); + break; + case UID_LIST: + LongArrayList list = (LongArrayList) loadedPropertyValue; + final int listSize = list.size(); + Output baosListOutput = new Output(Long.BYTES * 100, Long.BYTES * listSize); + baosListOutput.writeVarInt(listSize, true); + for (int i = 0; i < listSize; i++) { + baosListOutput.writeVarLong(list.getLong(i), true); + } + //System.err.println("Saving prop. LOb " + uid + ":" + loadedPropertyValue); + byte[] outList = baosListOutput.toBytes(); + indices.set(uid, outList.length, o -> o.write(outList, 0, outList.length)); + break; + case DATABASE_OBJECT: + //System.err.println("Saving prop. DBObj " + uid + ":" + loadedPropertyValue); + if (loadedPropertyValue == null) { + writeObjectInfoNull(uid); + } else { + ((DBObject) loadedPropertyValue).writeToDisk(uid); + } + break; + } + } + + public void registerClass(Class clazz, int id) { + kryo.register(clazz, 100 + id); + } + + public long allocateNullValue() { + return indices.add(0); + } + + @Override + public long clean() { + return 0;//indices.clean(); + } + + public long[] allocateNewUIDs(int quantity) { + long[] ids = new long[quantity]; + for (int i = 0; i < quantity; i++) { + ids[i] = allocateNullValue(); + } + return ids; + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java b/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java new file mode 100644 index 0000000..0d25ca2 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java @@ -0,0 +1,32 @@ +package org.warp.jcwdb.ann; + +import java.io.IOException; +import java.nio.file.Path; + +public class JCWDatabase { + private final DatabaseManager database; + + public JCWDatabase(Path dataFile, Path metadataFile) throws IOException { + this.database = new DatabaseManager(this, dataFile, metadataFile); + } + + public T loadRoot(Class rootClass) { + try { + return database.loadRoot(rootClass); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + DatabaseManager getDatabaseManager() { + return database; + } + + public void registerClass(Class clazz, int id) { + database.registerClass(clazz, id); + } + + public void close() throws IOException { + database.close(); + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java index 5622816..3687822 100644 --- a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java +++ b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java @@ -2,14 +2,17 @@ package org.warp.jcwdb.ann.exampleimpl; import org.warp.jcwdb.ann.*; -@DBClass(classTypeId = 0) +import java.io.IOException; +import java.util.StringJoiner; + public class Class1 extends DBObject { - public Class1(Database database) { + + public Class1(JCWDatabase database) { super(database); } - public Class1(Database database, long uid) { - super(database, uid); + public Class1(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); } @DBField(id = 0, type = DBDataType.OBJECT) @@ -18,6 +21,9 @@ public class Class1 extends DBObject { @DBField(id = 1, type = DBDataType.INTEGER) public int value2; + @DBField(id = 2, type = DBDataType.INTEGER) + public int value3; + @DBPropertyGetter(id = 0, type = DBDataType.OBJECT) public String getValue3() { return getProperty(); @@ -37,4 +43,26 @@ public class Class1 extends DBObject { public void setValue4(Class1 value) { setProperty(value); } + + @DBPropertyGetter(id = 2, type = DBDataType.OBJECT) + public String getValueStr() { + return getProperty(); + } + + @DBPropertySetter(id = 2, type = DBDataType.OBJECT) + public void setValueStr(String value) { + setProperty(value); + } + + @Override + public String toString() { + return new StringJoiner(", ", Class1.class.getSimpleName() + "[", "]") + .add("value1='" + value1 + "'") + .add("value2=" + value2) + .add("value3=" + value3) + .add("getValue3=" + getValue3()) + .add("getValue4=" + getValue4()) + .add("getValueStr=" + getValueStr()) + .toString(); + } } diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class2.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class2.java new file mode 100644 index 0000000..4b92731 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class2.java @@ -0,0 +1,40 @@ +package org.warp.jcwdb.ann.exampleimpl; + +import org.warp.jcwdb.ann.*; + +import java.io.IOException; +import java.util.StringJoiner; + +public class Class2 extends Class1 { + + //@DBField(id = 3, type = DBDataType.DATABASE_OBJECT) + //public DBDBObjectList value4; + + @DBField(id = 3, type = DBDataType.DATABASE_OBJECT) + public Class1 value4; + + @DBField(id = 4, type = DBDataType.INTEGER) + public int value5; + + public Class2(JCWDatabase database) { + super(database); + } + + public Class2(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + @Override + public String toString() { + return new StringJoiner(", ", Class1.class.getSimpleName() + "[", "]") + .add("value1='" + value1 + "'") + .add("value2=" + value2) + .add("value3=" + value3) + .add("value4=" + value4) + .add("value5=" + value5) + .add("getValue3=" + getValue3()) + .add("getValue4=" + getValue4()) + .add("getValueStr=" + getValueStr()) + .toString(); + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/ClassWithList.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/ClassWithList.java new file mode 100644 index 0000000..6e76677 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/exampleimpl/ClassWithList.java @@ -0,0 +1,48 @@ +package org.warp.jcwdb.ann.exampleimpl; + +import org.warp.jcwdb.ann.*; + +import java.io.IOError; +import java.io.IOException; +import java.util.StringJoiner; + +public class ClassWithList extends DBObject { + + public ClassWithList(JCWDatabase database) { + super(database); + } + + public ClassWithList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + @DBField(id = 0, type = DBDataType.DATABASE_OBJECT) + public DBDBObjectList valueList; + + + @DBPropertySetter(id = 0, type = DBDataType.DATABASE_OBJECT) + public void setList(DBDBObjectList value) { + setProperty(value); + } + + @DBPropertyGetter(id = 0, type = DBDataType.DATABASE_OBJECT) + public DBDBObjectList getList() { + return getProperty(); + } + + @Override + public String toString() { + DBDBObjectList list; + boolean listErrored = false; + try { + list = getList(); + } catch (IOError | Exception ex) { + list = null; + listErrored = true; + } + return new StringJoiner(", ", ClassWithList.class.getSimpleName() + "[", "]") + .add("valueList=" + valueList) + .add("getList=" + (listErrored ? "{error}" : (list == null ? list : list.size() < 100 ? list : "[" + list.size() + " items])"))) + .toString(); + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/IntClass.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/IntClass.java new file mode 100644 index 0000000..cf19761 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/exampleimpl/IntClass.java @@ -0,0 +1,27 @@ +package org.warp.jcwdb.ann.exampleimpl; + +import org.warp.jcwdb.ann.*; + +import java.io.IOException; +import java.util.StringJoiner; + +public class IntClass extends DBObject { + + public IntClass(JCWDatabase database) { + super(database); + } + + public IntClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + @DBField(id = 0, type = DBDataType.INTEGER) + public int value; + + @Override + public String toString() { + return new StringJoiner(", ", IntClass.class.getSimpleName() + "[", "]") + .add("value=" + value) + .toString(); + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java index 259169f..2e07a5b 100644 --- a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java +++ b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java @@ -1,6 +1,7 @@ package org.warp.jcwdb.ann.exampleimpl; -import org.warp.jcwdb.ann.Database; +import org.warp.jcwdb.ann.DBDBObjectList; +import org.warp.jcwdb.ann.JCWDatabase; import java.io.IOException; import java.nio.file.Paths; @@ -8,25 +9,57 @@ import java.nio.file.Paths; public class Main { public static void main(String[] args) throws IOException { - Database db = new Database(Paths.get("database_temp.db"), Paths.get("database_temp.idx")); - Class1 class1 = new Class1(db, 0); - class1.value1 = "ciao"; + long t0 = System.currentTimeMillis(); + JCWDatabase db = new JCWDatabase(Paths.get("N:\\TimedTemp\\database_temp.db"), Paths.get("N:\\TimedTemp\\database_temp.idx")); + db.registerClass(Class1.class, 0); + db.registerClass(Class2.class, 1); + Class2 class1 = db.loadRoot(Class2.class); + long t1 = System.currentTimeMillis(); + System.err.println("Loading took " + (t1-t0)/1000d + " seconds"); + t0 = System.currentTimeMillis(); + System.err.println("[MAIN] class1="+class1); + class1.value1 = "ciaoooooooooooooooooooooo"; class1.value2 = 3; - System.out.println("value3="+class1.getValue3()); + class1.value5 = 5; + System.err.println("[MAIN] value3="+class1.getValue3()); class1.setValue3("Ciao 3"); - System.out.println("value3="+class1.getValue3()); + System.err.println("[MAIN] value3="+class1.getValue3()); - Class1 nested = new Class1(db); - class1.setValue4(nested); - nested.setValue3("Ciao nested 3"); - try { - class1.close(); - nested.close(); - System.out.println(class1.getValue4().getValue3()); - } catch (Exception ex) { - ex.printStackTrace(); - } catch (Throwable throwable) { - throwable.printStackTrace(); + System.err.println("[MAIN] propString="+class1.getValueStr()); + class1.setValueStr("Ciao String"); + System.err.println("[MAIN] propString="+class1.getValueStr()); + + System.err.println("[MAIN] getValue4="+class1.getValue4()); + t1 = System.currentTimeMillis(); + System.err.println("Post-loading took " + (t1-t0)/1000d + " seconds"); + t0 = System.currentTimeMillis(); + for (int i = 0; i < 200; i++) { + Class1 nested; + if ((nested = class1.getValue4()) == null) { + //System.err.println("[MAIN] Created nested class"); + class1.setValue4(nested = new Class1(db)); + } + nested.getValue3(); + //System.err.println("[MAIN] value4="+class1.getValue4()); + //System.err.println("[MAIN] nested value3="+nested.getValue3()); + nested.setValue3("Ciao nested 3"); + //System.err.println("[MAIN] nested value3=" + class1.getValue4().getValue3()); } + t1 = System.currentTimeMillis(); + System.err.println("Took " + (t1-t0)/1000d + " seconds"); + + + /* + if (class1.value4 == null) { + class1.value4 = new DBDBObjectList<>(db, 100, Class1.class); + } + for (int i = 0; i < 15; i++) { + Class1 c1 = new Class1(db); + c1.value1 = "" + i; + c1.value2 = i; + c1.setValueStr("" + i); + class1.value4.add(c1); + }*/ + class1.value4 = new Class1(db); } } \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/MainSingleClass.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/MainSingleClass.java new file mode 100644 index 0000000..0cd1961 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/exampleimpl/MainSingleClass.java @@ -0,0 +1,52 @@ +package org.warp.jcwdb.ann.exampleimpl; + +import org.warp.jcwdb.ann.DBDBObjectList; +import org.warp.jcwdb.ann.DBList; +import org.warp.jcwdb.ann.JCWDatabase; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class MainSingleClass { + + public static void main(String[] args) throws IOException { + long t0 = System.currentTimeMillis(); + Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_t.db")); + Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_t.idx")); + for (int i = 0; i < 18; i++) { + doIt(); + } + } + + public static void doIt() throws IOException { + System.err.println("doIt"); + JCWDatabase db = new JCWDatabase(Paths.get("N:\\TimedTemp\\database_t.db"), Paths.get("N:\\TimedTemp\\database_t.idx")); + db.registerClass(ClassWithList.class, 0); + db.registerClass(IntClass.class, 1); + ClassWithList classWithList = db.loadRoot(ClassWithList.class); + System.err.println("[MAIN init] classWithList="+classWithList); + + if (classWithList.valueList == null) { + System.out.println("Get list was null"); + classWithList.valueList = new DBDBObjectList<>(db, IntClass.class, 10000); + } + DBDBObjectList list = classWithList.valueList; + + for (int i = 0; i < 1000000; i++) { + IntClass intClass = new IntClass(db); + intClass.value = i+0xFF00; + //System.err.println("[WRITE]" + intClass.value); + list.add(intClass); + } + + DBList.DBListIterator it = list.iterator(); + while (it.hasNext()) { + IntClass intClass = it.next(); + //System.err.println("[READ]" + intClass.value); + } + + System.err.println("[MAIN end.] singleClass="+classWithList); + db.close(); + } +} \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/SingleClass.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/SingleClass.java new file mode 100644 index 0000000..f35e5da --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/exampleimpl/SingleClass.java @@ -0,0 +1,68 @@ +package org.warp.jcwdb.ann.exampleimpl; + +import org.warp.jcwdb.ann.*; + +import java.io.IOException; +import java.util.StringJoiner; + +public class SingleClass extends DBObject { + + @DBField(id = 0, type = DBDataType.INTEGER) + public int valueInt; + + @DBField(id = 1, type = DBDataType.OBJECT) + public String valueObj; + + @DBField(id = 2, type = DBDataType.DATABASE_OBJECT) + public IntClass valueDB; + + @DBPropertySetter(id = 0, type = DBDataType.INTEGER) + public void setInt(int value) { + setProperty(value); + } + + @DBPropertySetter(id = 1, type = DBDataType.OBJECT) + public void setObj(String value) { + setProperty(value); + } + + @DBPropertySetter(id = 2, type = DBDataType.DATABASE_OBJECT) + public void setDB(IntClass value) { + setProperty(value); + } + + @DBPropertyGetter(id = 0, type = DBDataType.INTEGER) + public int getInt() { + return getProperty(); + } + + @DBPropertyGetter(id = 1, type = DBDataType.OBJECT) + public String getObj() { + return getProperty(); + } + + @DBPropertyGetter(id = 2, type = DBDataType.DATABASE_OBJECT) + public IntClass getDB() { + return getProperty(); + } + + public SingleClass(JCWDatabase database) { + super(database); + } + + public SingleClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + @Override + public String toString() { + return new StringJoiner(", ", SingleClass.class.getSimpleName() + "[", "]") + .add("valueInt=" + valueInt) + .add("valueObj='" + valueObj + "'") + .add("valueDB=" + valueDB) + .add("getInt=" + getInt()) + .add("getObj='" + getObj() + "'") + .add("getDB=" + getDB()) + .toString(); + } +} diff --git a/src/test/java/org/warp/jcwdb/AppTest.java b/src/test/java/org/warp/jcwdb/AppTest.java index 662c139..7e58457 100644 --- a/src/test/java/org/warp/jcwdb/AppTest.java +++ b/src/test/java/org/warp/jcwdb/AppTest.java @@ -1,6 +1,13 @@ package org.warp.jcwdb; import org.junit.Test; +import org.warp.jcwdb.ann.JCWDatabase; +import org.warp.jcwdb.ann.exampleimpl.IntClass; +import org.warp.jcwdb.ann.exampleimpl.SingleClass; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import static org.junit.Assert.assertTrue; @@ -15,4 +22,50 @@ public class AppTest { public void shouldAnswerWithTrue() { assertTrue(true); } + + @Test + public void simpleClassTest() throws IOException { + long t0 = System.currentTimeMillis(); + Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_s.db")); + Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_s.idx")); + for (int i = 0; i < 3; i++) { + doSingleClassTest(); + } + } + + public static void doSingleClassTest() throws IOException { + System.err.println("doSingleClassTest"); + JCWDatabase db = new JCWDatabase(Paths.get("N:\\TimedTemp\\database_s.db"), Paths.get("N:\\TimedTemp\\database_s.idx")); + db.registerClass(SingleClass.class, 0); + db.registerClass(IntClass.class, 1); + SingleClass singleClass = db.loadRoot(SingleClass.class); + System.err.println("[MAIN init] singleClass="+singleClass); + + singleClass.getInt(); + singleClass.setInt((int) (Math.random() * 100)); + + singleClass.getObj(); + singleClass.setObj(String.valueOf(Math.random() * 100)); + + if (singleClass.getDB() == null) { + System.out.println("Get db was null"); + singleClass.setDB(new IntClass(db)); + } + IntClass intClass = singleClass.getDB(); + intClass.value = (int) (Math.random() * 100); + + singleClass.valueInt = (int) (Math.random() * 100); + + singleClass.valueObj = String.valueOf(Math.random() * 100); + + if (singleClass.valueDB == null) { + System.out.println("Value db was null"); + singleClass.valueDB = new IntClass(db); + } + singleClass.valueDB.value = (int) (Math.random() * 100); + + + System.err.println("[MAIN end.] singleClass="+singleClass); + db.close(); + } } From 740db59b6271bca2587985f70f87be3d4414a70a Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Tue, 22 Jan 2019 12:33:58 +0100 Subject: [PATCH 3/5] Updated --- pom.xml | 12 +- .../java/org/warp/jcwdb/ann/DBArrayList.java | 110 +++++ .../org/warp/jcwdb/ann/DBDBObjectList.java | 25 +- .../java/org/warp/jcwdb/ann/DBDataType.java | 5 + src/main/java/org/warp/jcwdb/ann/DBList.java | 171 -------- .../java/org/warp/jcwdb/ann/DBObject.java | 43 +- .../java/org/warp/jcwdb/ann/DBObjectList.java | 16 +- .../org/warp/jcwdb/ann/DatabaseManager.java | 199 +++++---- .../java/org/warp/jcwdb/ann/JCWDatabase.java | 7 +- .../warp/jcwdb/ann/exampleimpl/Class1.java | 68 ---- .../warp/jcwdb/ann/exampleimpl/Class2.java | 40 -- .../jcwdb/ann/exampleimpl/ClassWithList.java | 48 --- .../warp/jcwdb/ann/exampleimpl/IntClass.java | 27 -- .../org/warp/jcwdb/ann/exampleimpl/Main.java | 65 --- .../ann/exampleimpl/MainSingleClass.java | 52 --- .../jcwdb/ann/exampleimpl/SingleClass.java | 68 ---- src/test/java/org/warp/jcwdb/AppTest.java | 71 ---- .../org/warp/jcwdb/FileAllocatorTest.java | 51 --- .../warp/jcwdb/tests/DBDBObjectListTests.java | 77 ++++ .../warp/jcwdb/tests/DBMultipleDBObjects.java | 80 ++++ .../warp/jcwdb/tests/DBNestedDBObjects.java | 69 ++++ .../org/warp/jcwdb/tests/DBRootCreation.java | 48 +++ .../org/warp/jcwdb/tests/DBRootFields.java | 30 ++ .../warp/jcwdb/tests/DBRootProperties.java | 30 ++ .../org/warp/jcwdb/utils/NestedClass.java | 32 ++ .../org/warp/jcwdb/utils/SimplestClass.java | 20 + .../java/org/warp/jcwdb/utils/TestUtils.java | 377 ++++++++++++++++++ 27 files changed, 1059 insertions(+), 782 deletions(-) create mode 100644 src/main/java/org/warp/jcwdb/ann/DBArrayList.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/DBList.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/Class2.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/ClassWithList.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/IntClass.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/MainSingleClass.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/exampleimpl/SingleClass.java delete mode 100644 src/test/java/org/warp/jcwdb/AppTest.java delete mode 100644 src/test/java/org/warp/jcwdb/FileAllocatorTest.java create mode 100644 src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java create mode 100644 src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java create mode 100644 src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java create mode 100644 src/test/java/org/warp/jcwdb/tests/DBRootCreation.java create mode 100644 src/test/java/org/warp/jcwdb/tests/DBRootFields.java create mode 100644 src/test/java/org/warp/jcwdb/tests/DBRootProperties.java create mode 100644 src/test/java/org/warp/jcwdb/utils/NestedClass.java create mode 100644 src/test/java/org/warp/jcwdb/utils/SimplestClass.java create mode 100644 src/test/java/org/warp/jcwdb/utils/TestUtils.java diff --git a/pom.xml b/pom.xml index e46f8ab..6451ac9 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.warp jcwdb - 1.1-SNAPSHOT + 1.2-SNAPSHOT jcwdb @@ -41,7 +41,7 @@ com.esotericsoftware kryo - 5.0.0-RC2-SNAPSHOT + 5.0.0-RC1 net.openhft @@ -52,7 +52,13 @@ org.apache.commons commons-lang3 - 3.8.1 + 3.5 + + + org.jetbrains + annotations + RELEASE + compile diff --git a/src/main/java/org/warp/jcwdb/ann/DBArrayList.java b/src/main/java/org/warp/jcwdb/ann/DBArrayList.java new file mode 100644 index 0000000..366024f --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DBArrayList.java @@ -0,0 +1,110 @@ +package org.warp.jcwdb.ann; + +import it.unimi.dsi.fastutil.longs.LongArrayList; + +import java.io.IOError; +import java.io.IOException; +import java.util.StringJoiner; + +public abstract class DBArrayList extends DBObject { + + private final Object indicesAccessLock = new Object(); + + @DBField(id = 0, type = DBDataType.UID_LIST) + private LongArrayList indices; + + public DBArrayList(JCWDatabase database) { + super(database); + indices = new LongArrayList(); + } + + public DBArrayList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + public T get(int index) { + 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 = databaseManager.allocateNullValue(); + synchronized (indicesAccessLock) { + indices.add(uid); + try { + writeItemToDisk(uid, value); + } catch (IOException e) { + throw new IOError(e); + } + } + } + + public void update(int index, T value) { + synchronized (indicesAccessLock) { + set(index, value); + } + } + + public void set(int index, T value) { + long uid = databaseManager.allocateNullValue(); + 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 = databaseManager.allocateNullValue(); + synchronized (indicesAccessLock) { + indices.add(index, uid); + try { + writeItemToDisk(uid, value); + } catch (IOException e) { + throw new IOError(e); + } + } + } + + public T getLast() { + synchronized (indicesAccessLock) { + if (indices.size() > 0) { + return get(indices.size() - 1); + } else { + return null; + } + } + } + + public boolean isEmpty() { + synchronized (indicesAccessLock) { + return indices.size() <= 0; + } + } + + public int size() { + synchronized (indicesAccessLock) { + return indices.size(); + } + } + + protected abstract T loadItem(long uid) throws IOException; + + protected abstract void writeItemToDisk(long uid, T item) throws IOException; + + @Override + public String toString() { + return new StringJoiner(", ", DBArrayList.class.getSimpleName() + "[", "]") + .add(indices.size() + " items") + .toString(); + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java b/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java index d22a5ca..71d7dac 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java +++ b/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java @@ -1,20 +1,14 @@ package org.warp.jcwdb.ann; import java.io.IOException; -import java.util.LinkedList; -public class DBDBObjectList extends DBList { +public class DBDBObjectList extends DBArrayList { - @DBField(id = 2, type = DBDataType.OBJECT) - public Class type; + @DBField(id = 1, type = DBDataType.OBJECT) + private Class type; - public DBDBObjectList(JCWDatabase db, Class type) { - super(db, 10); - this.type = type; - } - - public DBDBObjectList(JCWDatabase db, Class type, int maxLoadedItems) { - super(db, maxLoadedItems); + public DBDBObjectList(JCWDatabase database, Class type) { + super(database); this.type = type; } @@ -23,13 +17,12 @@ public class DBDBObjectList extends DBList { } @Override - protected T loadObjectFromDatabase(long uid) throws IOException { - return database.loadDBObject(type, uid); + protected T loadItem(long uid) throws IOException { + return databaseManager.loadDBObject(type, uid); } @Override - protected long saveObjectToDatabase(long uid, T value) throws IOException { - database.writeObjectProperty(uid, DBDataType.DATABASE_OBJECT, value); - return uid; + protected void writeItemToDisk(long uid, T item) throws IOException { + databaseManager.writeObjectProperty(uid, DBDataType.DATABASE_OBJECT, item); } } diff --git a/src/main/java/org/warp/jcwdb/ann/DBDataType.java b/src/main/java/org/warp/jcwdb/ann/DBDataType.java index 0ced52c..9e8907b 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBDataType.java +++ b/src/main/java/org/warp/jcwdb/ann/DBDataType.java @@ -3,6 +3,11 @@ package org.warp.jcwdb.ann; public enum DBDataType { DATABASE_OBJECT, OBJECT, + BOOLEAN, + BYTE, + SHORT, + CHAR, INTEGER, + LONG, UID_LIST } \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/ann/DBList.java b/src/main/java/org/warp/jcwdb/ann/DBList.java deleted file mode 100644 index 685798c..0000000 --- a/src/main/java/org/warp/jcwdb/ann/DBList.java +++ /dev/null @@ -1,171 +0,0 @@ -package org.warp.jcwdb.ann; - -import it.unimi.dsi.fastutil.longs.LongArrayList; - -import java.io.IOError; -import java.io.IOException; -import java.util.LinkedList; -import java.util.StringJoiner; - -public abstract class DBList extends DBObject { - @DBField(id = 0, type = DBDataType.INTEGER) - private int maxLoadedItems; - - @DBField(id = 1, type = DBDataType.UID_LIST) - private LongArrayList itemIndices; - - public DBList(JCWDatabase db, int maxLoadedItems) { - super(db); - this.maxLoadedItems = maxLoadedItems; - this.itemIndices = new LongArrayList(); - } - - public DBList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); - } - - public int getMaxLoadedItems() { - return maxLoadedItems; - } - - public void add(T obj) { - try { - itemIndices.add(saveObjectToDatabase(database.allocateNullValue(), obj)); - } catch (IOException e) { - throw new IOError(e); - } - } - - public int size() { - return this.itemIndices.size(); - } - - private T loadObjectAt(int i) throws IOException { - return loadObjectFromDatabase(itemIndices.getLong(i)); - } - - protected abstract T loadObjectFromDatabase(long uid) throws IOException; - - protected abstract long saveObjectToDatabase(long uid, T value) throws IOException; - - public DBListIterator iterator() { - return new DBListIterator<>() { - private int position = itemIndices.size(); - private LinkedList cachedItems; - - private int objectToSavePosition; - private T objectToSave; - - @Override - public boolean hasNext() { - if (position > 0) { - return true; - } else { - try { - saveObservedObject(); - } catch (IOException e) { - throw new IOError(e); - } - return false; - } - } - - @Override - public T next() { - position--; - if (position < 0) { - throw new NullPointerException("Position < 0"); - } - if (cachedItems == null || cachedItems.size() == 0) { - try { - cachedItems = fillCache(position); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - try { - return switchObservedObject(position, cachedItems.removeFirst()); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public void remove() { - try { - saveObservedObject(); - } catch (IOException e) { - throw new RuntimeException(e); - } - itemIndices.removeLong(position); - } - - @Override - public void insert(T obj) { - try { - itemIndices.add(position, saveObjectToDatabase(database.allocateNullValue(), obj)); - } catch (IOException e) { - throw new IOError(e); - } - } - - @Override - public void set(T obj) { - try { - itemIndices.set(position, saveObjectToDatabase(database.allocateNullValue(), obj)); - } catch (IOException e) { - throw new IOError(e); - } - } - - private LinkedList fillCache(int position) throws IOException { - LinkedList cachedItems = new LinkedList<>(); - int firstObjectIndex = position; - int lastObjectIndex = firstObjectIndex - maxLoadedItems; - if (lastObjectIndex < 0) { - lastObjectIndex = 0; - } - for (int i = firstObjectIndex; i >= lastObjectIndex; i--) { - cachedItems.addLast(loadObjectAt(i)); - } - return cachedItems; - } - - private void saveObservedObject() throws IOException { - if (objectToSave != null) { - itemIndices.set(objectToSavePosition, saveObjectToDatabase(database.allocateNullValue(), objectToSave)); - objectToSave = null; - objectToSavePosition = 0; - } - } - - private T switchObservedObject(int position, T obj) throws IOException { - saveObservedObject(); - objectToSave = obj; - objectToSavePosition = position; - return obj; - } - }; - } - - public interface DBListIterator { - boolean hasNext(); - - T next(); - - void remove(); - - void insert(T obj); - - void set(T obj); - } - - @Override - public String toString() { - return new StringJoiner(", ", DBList.class.getSimpleName() + "[", "]") - .add("size=" + size()) - .add("maxLoadedItems=" + maxLoadedItems) - .add("itemIndices=" + (itemIndices.size() < 100 ? itemIndices : "["+itemIndices.size()+" items]")) - .toString(); - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/DBObject.java b/src/main/java/org/warp/jcwdb/ann/DBObject.java index da5a3ae..e70b17a 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBObject.java +++ b/src/main/java/org/warp/jcwdb/ann/DBObject.java @@ -10,7 +10,8 @@ import java.util.LinkedHashMap; import java.util.Map; public abstract class DBObject { - protected final DatabaseManager database; + protected final JCWDatabase database; + protected final DatabaseManager databaseManager; private Field[] fields; private DBDataType[] fieldTypes; private long[] fieldUIDs; @@ -27,7 +28,8 @@ public abstract class DBObject { private final Object propertiesAccessLock = new Object(); public DBObject(JCWDatabase database) { - this.database = database.getDatabaseManager(); + this.database = database; + this.databaseManager = database.getDatabaseManager(); try { initializeDBObject(); } catch (IOException e) { @@ -35,6 +37,12 @@ public abstract class DBObject { } } + public DBObject(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + this.database = database; + this.databaseManager = database.getDatabaseManager(); + this.databaseManager.preloadDBObject(this, objectInfo); + } + private void initializeDBObject() throws IOException { initializeDBObjectFields(); initializeDBObjectProperties(); @@ -42,12 +50,12 @@ public abstract class DBObject { private void initializeDBObjectFields() throws IOException { // Declare the variables needed to get the biggest field Id - Field[] unorderedFields = database.getFields(this); + Field[] unorderedFields = databaseManager.getFields(this); // Find the biggest field Id - int biggestFieldId = database.getBiggestFieldId(unorderedFields); + int biggestFieldId = databaseManager.getBiggestFieldId(unorderedFields); // Allocate new UIDs - fieldUIDs = database.allocateNewUIDs(biggestFieldId + 1); + fieldUIDs = databaseManager.allocateNewUIDs(biggestFieldId + 1); // Declare the other variables Field[] fields = new Field[biggestFieldId + 1]; @@ -58,7 +66,7 @@ public abstract class DBObject { DBField fieldAnnotation = field.getAnnotation(DBField.class); int fieldId = fieldAnnotation.id(); DBDataType fieldType = fieldAnnotation.type(); - database.loadField(this, field, fieldType, fieldUIDs[fieldId]); + databaseManager.loadField(this, field, fieldType, fieldUIDs[fieldId]); fields[fieldId] = field; orderedFieldTypes[fieldId] = fieldType; } @@ -68,16 +76,16 @@ public abstract class DBObject { private void initializeDBObjectProperties() { // Declare the variables needed to get the biggest property Id - Method[] unorderedPropertyGetters = database.getPropertyGetters(this); - Method[] unorderedPropertySetters = database.getPropertySetters(this); + Method[] unorderedPropertyGetters = databaseManager.getPropertyGetters(this); + Method[] unorderedPropertySetters = databaseManager.getPropertySetters(this); // Find the biggest property Id - int biggestGetter = database.getBiggestPropertyGetterId(unorderedPropertyGetters); - int biggestSetter = database.getBiggestPropertySetterId(unorderedPropertySetters); + int biggestGetter = databaseManager.getBiggestPropertyGetterId(unorderedPropertyGetters); + int biggestSetter = databaseManager.getBiggestPropertySetterId(unorderedPropertySetters); int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter; // Allocate new UIDs - propertyUIDs = database.allocateNewUIDs(biggestPropertyId + 1); + propertyUIDs = databaseManager.allocateNewUIDs(biggestPropertyId + 1); for (Method property : unorderedPropertySetters) { DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class); @@ -115,11 +123,6 @@ public abstract class DBObject { setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs, setterMethods, getterMethods); } - public DBObject(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - this.database = database.getDatabaseManager(); - this.database.preloadDBObject(this, objectInfo); - } - public T getProperty() { StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); StackWalker.StackFrame stackFrame = walker.walk(f -> f.skip(1).findFirst().orElse(null)); @@ -141,7 +144,7 @@ public abstract class DBObject { synchronized (propertiesAccessLock) { if (!loadedProperties[propertyId]) { long propertyUID = propertyUIDs[propertyId]; - database.loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID); + databaseManager.loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID); } return (T) loadedPropertyValues[propertyId]; } @@ -166,13 +169,13 @@ public abstract class DBObject { try { synchronized (propertiesAccessLock) { synchronized (fieldsAccessLock) { - database.writeObjectInfo(uid, fieldUIDs, propertyUIDs); + databaseManager.writeObjectInfo(uid, fieldUIDs, propertyUIDs); } } synchronized (fieldsAccessLock) { for (int i = 0; i < fieldUIDs.length; i++) { try { - database.writeObjectProperty(fieldUIDs[i], fieldTypes[i], fields[i].get(this)); + databaseManager.writeObjectProperty(fieldUIDs[i], fieldTypes[i], fields[i].get(this)); } catch (IllegalAccessException e) { throw new IOError(e); } @@ -180,7 +183,7 @@ public abstract class DBObject { } synchronized (propertiesAccessLock) { for (int i = 0; i < propertyUIDs.length; i++) { - database.writeObjectProperty(propertyUIDs[i], propertyTypes[i], loadedPropertyValues[i]); + databaseManager.writeObjectProperty(propertyUIDs[i], propertyTypes[i], loadedPropertyValues[i]); } } } catch (IOException e) { diff --git a/src/main/java/org/warp/jcwdb/ann/DBObjectList.java b/src/main/java/org/warp/jcwdb/ann/DBObjectList.java index 1fa796e..44a41a0 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBObjectList.java +++ b/src/main/java/org/warp/jcwdb/ann/DBObjectList.java @@ -2,10 +2,9 @@ package org.warp.jcwdb.ann; import java.io.IOException; -public class DBObjectList extends DBList { - - public DBObjectList(JCWDatabase db, int maxLoadedItems) { - super(db, maxLoadedItems); +public class DBObjectList extends DBArrayList { + public DBObjectList(JCWDatabase database) { + super(database); } public DBObjectList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { @@ -13,13 +12,12 @@ public class DBObjectList extends DBList { } @Override - protected T loadObjectFromDatabase(long uid) throws IOException { - return database.loadObject(uid); + public T loadItem(long uid) throws IOException { + return databaseManager.loadObject(uid); } @Override - protected long saveObjectToDatabase(long uid, T value) throws IOException { - database.writeObjectProperty(uid, DBDataType.OBJECT, value); - return uid; + public void writeItemToDisk(long uid, T item) throws IOException { + databaseManager.writeObjectProperty(uid, DBDataType.OBJECT, item); } } diff --git a/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java b/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java index 7a986bd..84a0cb4 100644 --- a/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java +++ b/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java @@ -1,7 +1,6 @@ package org.warp.jcwdb.ann; import com.esotericsoftware.kryo.Kryo; -import com.esotericsoftware.kryo.Registration; import com.esotericsoftware.kryo.io.Output; import it.unimi.dsi.fastutil.booleans.BooleanArrayList; import it.unimi.dsi.fastutil.bytes.ByteArrayList; @@ -14,7 +13,6 @@ import org.apache.commons.lang3.reflect.MethodUtils; import org.warp.jcwdb.Cleanable; import org.warp.jcwdb.Cleaner; import org.warp.jcwdb.FileIndexManager; -import org.warp.jcwdb.ann.exampleimpl.ClassWithList; import java.io.ByteArrayOutputStream; import java.io.IOError; @@ -24,6 +22,8 @@ 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 DatabaseManager implements Cleanable { @@ -36,9 +36,9 @@ public class DatabaseManager implements Cleanable { private final Kryo kryo = new Kryo(); private volatile boolean closed; - DatabaseManager(JCWDatabase jcwDatabase, Path dataFile, Path metadataFile) throws IOException { + DatabaseManager(JCWDatabase jcwDatabase, Path dataFile, Path metadataFile, boolean registrationRequired) throws IOException { this.jcwDatabase = jcwDatabase; - kryo.setRegistrationRequired(true); + kryo.setRegistrationRequired(registrationRequired); registerDefaultClasses(); this.indices = new FileIndexManager(dataFile, metadataFile); if (!indices.has(0)) { @@ -106,6 +106,9 @@ public class DatabaseManager implements Cleanable { registerClass(CharArrayList.class, id++); registerClass(IntArrayList.class, id++); registerClass(LongArrayList.class, id++); + registerClass(TreeSet.class, id++); + registerClass(SortedSet.class, id++); + registerClass(SortedMap.class, id++); } @SuppressWarnings("unchecked") @@ -262,63 +265,70 @@ public class DatabaseManager implements Cleanable { return biggestFieldId; } - @SuppressWarnings("unchecked") public void loadProperty(DBObject obj, int propertyId, Method property, DBDataType propertyType, long propertyUID) throws IOException { - switch (propertyType) { - case DATABASE_OBJECT: - DBObject fieldDBObjectValue = loadDBObject((Class) property.getReturnType(), propertyUID); - //System.err.println("Loading prop. DBObj " + propertyUID + ":" + fieldDBObjectValue); - obj.setLoadedProperty(propertyId, fieldDBObjectValue); - break; - case OBJECT: - Object fieldObjectValue = loadObject(propertyUID); - //System.err.println("Loading prop. Obj " + propertyUID + ":" + fieldObjectValue); - obj.setLoadedProperty(propertyId, fieldObjectValue); - break; - case UID_LIST: - LongArrayList fieldListObjectValue = loadListObject(propertyUID); - //System.err.println("Loading prop. LOb " + propertyUID + ":" + fieldListObjectValue); - obj.setLoadedProperty(propertyId, fieldListObjectValue); - break; - case INTEGER: - int fieldIntValue = loadInt(propertyUID); - //System.err.println("Loading prop. Int " + propertyUID + ":" + fieldIntValue); - obj.setLoadedProperty(propertyId, fieldIntValue); - break; - default: - throw new NullPointerException("Unknown Field Type"); - } + loadData(propertyType, propertyUID, property::getReturnType, (data) -> obj.setLoadedProperty(propertyId, data)); + } + + public void loadField(DBObject obj, Field field, DBDataType fieldType, long fieldUID) throws IOException { + loadData(fieldType, fieldUID, field::getType, (data) -> { + try { + FieldUtils.writeField(field, obj, data, true); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }); } @SuppressWarnings("unchecked") - public void loadField(DBObject obj, Field field, DBDataType fieldType, long fieldUID) throws IOException { - try { - switch (fieldType) { - case DATABASE_OBJECT: - DBObject fieldDBObjectValue = loadDBObject((Class) field.getType(), fieldUID); - //System.err.println("Loading field DBObj " + fieldUID + ":" + fieldDBObjectValue); - FieldUtils.writeField(field, obj, fieldDBObjectValue, true); - break; - case OBJECT: - Object fieldObjectValue = loadObject(fieldUID); - //System.err.println("Loading field Obj " + fieldUID + ":" + fieldObjectValue); - FieldUtils.writeField(field, obj, fieldObjectValue, true); - break; - case UID_LIST: - LongArrayList fieldListObjectValue = loadListObject(fieldUID); - //System.err.println("Loading field LOb " + fieldUID + ":" + fieldObjectValue); - FieldUtils.writeField(field, obj, fieldListObjectValue, true); - break; - case INTEGER: - int fieldIntValue = loadInt(fieldUID); - //System.err.println("Loading field Int " + fieldUID + ":" + fieldIntValue); - FieldUtils.writeField(field, obj, fieldIntValue, true); - break; - default: - throw new NullPointerException("Unknown Field Type"); - } - } catch (IllegalAccessException e) { - throw new RuntimeException(e); + private void loadData(DBDataType propertyType, long dataUID, Supplier> returnType, Consumer result) throws IOException { + switch (propertyType) { + case DATABASE_OBJECT: + DBObject fieldDBObjectValue = loadDBObject((Class) 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"); } } @@ -345,7 +355,6 @@ public class DatabaseManager implements Cleanable { return indices.get(uid, (i, size) -> size == 0 ? null : (T) kryo.readClassAndObject(i)); } - @SuppressWarnings("unchecked") private LongArrayList loadListObject(long uid) throws IOException { return indices.get(uid, (i, size) -> { if (size == 0) return null; @@ -358,10 +367,31 @@ public class DatabaseManager implements Cleanable { }); } + public boolean loadBoolean(long uid) throws IOException { + return indices.get(uid, (i, size) -> size != 0 && i.readBoolean()); + } + + public byte loadByte(long uid) throws IOException { + return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readByte()); + } + + public short loadShort(long uid) throws IOException { + return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readShort()); + } + + public char loadChar(long uid) throws IOException { + return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readChar()); + } + + public int loadInt(long uid) throws IOException { return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readInt()); } + public long loadLong(long uid) throws IOException { + return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readLong()); + } + /** * * @param uid @@ -390,31 +420,58 @@ public class DatabaseManager implements Cleanable { public void writeObjectProperty(long uid, DBDataType propertyType, T loadedPropertyValue) throws IOException { switch (propertyType) { + case BOOLEAN: + indices.set(uid, 1, (o) -> o.writeBoolean(loadedPropertyValue == null ? false : (boolean) loadedPropertyValue)); + //System.err.println("Saving data Boo " + uid + ":" + loadedPropertyValue); + break; + case BYTE: + indices.set(uid, Byte.BYTES, (o) -> o.writeByte(loadedPropertyValue == null ? 0 : (byte) loadedPropertyValue)); + //System.err.println("Saving data Byt " + uid + ":" + loadedPropertyValue); + break; + case SHORT: + indices.set(uid, Short.BYTES, (o) -> o.writeShort(loadedPropertyValue == null ? 0 : (short) loadedPropertyValue)); + //System.err.println("Saving data Shr " + uid + ":" + loadedPropertyValue); + break; + case CHAR: + indices.set(uid, Character.BYTES, (o) -> o.writeChar(loadedPropertyValue == null ? 0 : (char) loadedPropertyValue)); + //System.err.println("Saving data Chr " + uid + ":" + loadedPropertyValue); + break; case INTEGER: - indices.set(uid, Integer.BYTES, (o) -> o.writeInt((int) loadedPropertyValue)); - //System.err.println("Saving prop. Int " + uid + ":" + loadedPropertyValue); + indices.set(uid, Integer.BYTES, (o) -> o.writeInt(loadedPropertyValue == null ? 0 : (int) loadedPropertyValue)); + //System.err.println("Saving data Int " + uid + ":" + loadedPropertyValue); + break; + case LONG: + indices.set(uid, Long.BYTES, (o) -> 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 prop. Obj " + uid + ":" + 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 -> o.write(out, 0, out.length)); break; case UID_LIST: - LongArrayList list = (LongArrayList) loadedPropertyValue; - final int listSize = list.size(); - Output baosListOutput = new Output(Long.BYTES * 100, Long.BYTES * listSize); - baosListOutput.writeVarInt(listSize, true); - for (int i = 0; i < listSize; i++) { - baosListOutput.writeVarLong(list.getLong(i), true); + 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 -> o.write(outList, 0, outList.length)); } - //System.err.println("Saving prop. LOb " + uid + ":" + loadedPropertyValue); - byte[] outList = baosListOutput.toBytes(); - indices.set(uid, outList.length, o -> o.write(outList, 0, outList.length)); break; case DATABASE_OBJECT: - //System.err.println("Saving prop. DBObj " + uid + ":" + loadedPropertyValue); + //System.err.println("Saving data DBObj " + uid + ":" + loadedPropertyValue); if (loadedPropertyValue == null) { writeObjectInfoNull(uid); } else { diff --git a/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java b/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java index 0d25ca2..4e0854d 100644 --- a/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java +++ b/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java @@ -6,8 +6,8 @@ import java.nio.file.Path; public class JCWDatabase { private final DatabaseManager database; - public JCWDatabase(Path dataFile, Path metadataFile) throws IOException { - this.database = new DatabaseManager(this, dataFile, metadataFile); + public JCWDatabase(Path dataFile, Path metadataFile, boolean registrationRequired) throws IOException { + this.database = new DatabaseManager(this, dataFile, metadataFile, registrationRequired); } public T loadRoot(Class rootClass) { @@ -23,6 +23,9 @@ public class JCWDatabase { } public void registerClass(Class clazz, int id) { + if (id < 0) { + throw new IllegalArgumentException(); + } database.registerClass(clazz, id); } diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java deleted file mode 100644 index 3687822..0000000 --- a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class1.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.warp.jcwdb.ann.exampleimpl; - -import org.warp.jcwdb.ann.*; - -import java.io.IOException; -import java.util.StringJoiner; - -public class Class1 extends DBObject { - - public Class1(JCWDatabase database) { - super(database); - } - - public Class1(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); - } - - @DBField(id = 0, type = DBDataType.OBJECT) - public String value1; - - @DBField(id = 1, type = DBDataType.INTEGER) - public int value2; - - @DBField(id = 2, type = DBDataType.INTEGER) - public int value3; - - @DBPropertyGetter(id = 0, type = DBDataType.OBJECT) - public String getValue3() { - return getProperty(); - } - - @DBPropertySetter(id = 0, type = DBDataType.OBJECT) - public void setValue3(String value) { - setProperty(value); - } - - @DBPropertyGetter(id = 1, type = DBDataType.DATABASE_OBJECT) - public Class1 getValue4() { - return getProperty(); - } - - @DBPropertySetter(id = 1, type = DBDataType.DATABASE_OBJECT) - public void setValue4(Class1 value) { - setProperty(value); - } - - @DBPropertyGetter(id = 2, type = DBDataType.OBJECT) - public String getValueStr() { - return getProperty(); - } - - @DBPropertySetter(id = 2, type = DBDataType.OBJECT) - public void setValueStr(String value) { - setProperty(value); - } - - @Override - public String toString() { - return new StringJoiner(", ", Class1.class.getSimpleName() + "[", "]") - .add("value1='" + value1 + "'") - .add("value2=" + value2) - .add("value3=" + value3) - .add("getValue3=" + getValue3()) - .add("getValue4=" + getValue4()) - .add("getValueStr=" + getValueStr()) - .toString(); - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class2.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class2.java deleted file mode 100644 index 4b92731..0000000 --- a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Class2.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.warp.jcwdb.ann.exampleimpl; - -import org.warp.jcwdb.ann.*; - -import java.io.IOException; -import java.util.StringJoiner; - -public class Class2 extends Class1 { - - //@DBField(id = 3, type = DBDataType.DATABASE_OBJECT) - //public DBDBObjectList value4; - - @DBField(id = 3, type = DBDataType.DATABASE_OBJECT) - public Class1 value4; - - @DBField(id = 4, type = DBDataType.INTEGER) - public int value5; - - public Class2(JCWDatabase database) { - super(database); - } - - public Class2(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); - } - - @Override - public String toString() { - return new StringJoiner(", ", Class1.class.getSimpleName() + "[", "]") - .add("value1='" + value1 + "'") - .add("value2=" + value2) - .add("value3=" + value3) - .add("value4=" + value4) - .add("value5=" + value5) - .add("getValue3=" + getValue3()) - .add("getValue4=" + getValue4()) - .add("getValueStr=" + getValueStr()) - .toString(); - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/ClassWithList.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/ClassWithList.java deleted file mode 100644 index 6e76677..0000000 --- a/src/main/java/org/warp/jcwdb/ann/exampleimpl/ClassWithList.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.warp.jcwdb.ann.exampleimpl; - -import org.warp.jcwdb.ann.*; - -import java.io.IOError; -import java.io.IOException; -import java.util.StringJoiner; - -public class ClassWithList extends DBObject { - - public ClassWithList(JCWDatabase database) { - super(database); - } - - public ClassWithList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); - } - - @DBField(id = 0, type = DBDataType.DATABASE_OBJECT) - public DBDBObjectList valueList; - - - @DBPropertySetter(id = 0, type = DBDataType.DATABASE_OBJECT) - public void setList(DBDBObjectList value) { - setProperty(value); - } - - @DBPropertyGetter(id = 0, type = DBDataType.DATABASE_OBJECT) - public DBDBObjectList getList() { - return getProperty(); - } - - @Override - public String toString() { - DBDBObjectList list; - boolean listErrored = false; - try { - list = getList(); - } catch (IOError | Exception ex) { - list = null; - listErrored = true; - } - return new StringJoiner(", ", ClassWithList.class.getSimpleName() + "[", "]") - .add("valueList=" + valueList) - .add("getList=" + (listErrored ? "{error}" : (list == null ? list : list.size() < 100 ? list : "[" + list.size() + " items])"))) - .toString(); - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/IntClass.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/IntClass.java deleted file mode 100644 index cf19761..0000000 --- a/src/main/java/org/warp/jcwdb/ann/exampleimpl/IntClass.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.warp.jcwdb.ann.exampleimpl; - -import org.warp.jcwdb.ann.*; - -import java.io.IOException; -import java.util.StringJoiner; - -public class IntClass extends DBObject { - - public IntClass(JCWDatabase database) { - super(database); - } - - public IntClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); - } - - @DBField(id = 0, type = DBDataType.INTEGER) - public int value; - - @Override - public String toString() { - return new StringJoiner(", ", IntClass.class.getSimpleName() + "[", "]") - .add("value=" + value) - .toString(); - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java deleted file mode 100644 index 2e07a5b..0000000 --- a/src/main/java/org/warp/jcwdb/ann/exampleimpl/Main.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.warp.jcwdb.ann.exampleimpl; - -import org.warp.jcwdb.ann.DBDBObjectList; -import org.warp.jcwdb.ann.JCWDatabase; - -import java.io.IOException; -import java.nio.file.Paths; - -public class Main { - - public static void main(String[] args) throws IOException { - long t0 = System.currentTimeMillis(); - JCWDatabase db = new JCWDatabase(Paths.get("N:\\TimedTemp\\database_temp.db"), Paths.get("N:\\TimedTemp\\database_temp.idx")); - db.registerClass(Class1.class, 0); - db.registerClass(Class2.class, 1); - Class2 class1 = db.loadRoot(Class2.class); - long t1 = System.currentTimeMillis(); - System.err.println("Loading took " + (t1-t0)/1000d + " seconds"); - t0 = System.currentTimeMillis(); - System.err.println("[MAIN] class1="+class1); - class1.value1 = "ciaoooooooooooooooooooooo"; - class1.value2 = 3; - class1.value5 = 5; - System.err.println("[MAIN] value3="+class1.getValue3()); - class1.setValue3("Ciao 3"); - System.err.println("[MAIN] value3="+class1.getValue3()); - - System.err.println("[MAIN] propString="+class1.getValueStr()); - class1.setValueStr("Ciao String"); - System.err.println("[MAIN] propString="+class1.getValueStr()); - - System.err.println("[MAIN] getValue4="+class1.getValue4()); - t1 = System.currentTimeMillis(); - System.err.println("Post-loading took " + (t1-t0)/1000d + " seconds"); - t0 = System.currentTimeMillis(); - for (int i = 0; i < 200; i++) { - Class1 nested; - if ((nested = class1.getValue4()) == null) { - //System.err.println("[MAIN] Created nested class"); - class1.setValue4(nested = new Class1(db)); - } - nested.getValue3(); - //System.err.println("[MAIN] value4="+class1.getValue4()); - //System.err.println("[MAIN] nested value3="+nested.getValue3()); - nested.setValue3("Ciao nested 3"); - //System.err.println("[MAIN] nested value3=" + class1.getValue4().getValue3()); - } - t1 = System.currentTimeMillis(); - System.err.println("Took " + (t1-t0)/1000d + " seconds"); - - - /* - if (class1.value4 == null) { - class1.value4 = new DBDBObjectList<>(db, 100, Class1.class); - } - for (int i = 0; i < 15; i++) { - Class1 c1 = new Class1(db); - c1.value1 = "" + i; - c1.value2 = i; - c1.setValueStr("" + i); - class1.value4.add(c1); - }*/ - class1.value4 = new Class1(db); - } -} \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/MainSingleClass.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/MainSingleClass.java deleted file mode 100644 index 0cd1961..0000000 --- a/src/main/java/org/warp/jcwdb/ann/exampleimpl/MainSingleClass.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.warp.jcwdb.ann.exampleimpl; - -import org.warp.jcwdb.ann.DBDBObjectList; -import org.warp.jcwdb.ann.DBList; -import org.warp.jcwdb.ann.JCWDatabase; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; - -public class MainSingleClass { - - public static void main(String[] args) throws IOException { - long t0 = System.currentTimeMillis(); - Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_t.db")); - Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_t.idx")); - for (int i = 0; i < 18; i++) { - doIt(); - } - } - - public static void doIt() throws IOException { - System.err.println("doIt"); - JCWDatabase db = new JCWDatabase(Paths.get("N:\\TimedTemp\\database_t.db"), Paths.get("N:\\TimedTemp\\database_t.idx")); - db.registerClass(ClassWithList.class, 0); - db.registerClass(IntClass.class, 1); - ClassWithList classWithList = db.loadRoot(ClassWithList.class); - System.err.println("[MAIN init] classWithList="+classWithList); - - if (classWithList.valueList == null) { - System.out.println("Get list was null"); - classWithList.valueList = new DBDBObjectList<>(db, IntClass.class, 10000); - } - DBDBObjectList list = classWithList.valueList; - - for (int i = 0; i < 1000000; i++) { - IntClass intClass = new IntClass(db); - intClass.value = i+0xFF00; - //System.err.println("[WRITE]" + intClass.value); - list.add(intClass); - } - - DBList.DBListIterator it = list.iterator(); - while (it.hasNext()) { - IntClass intClass = it.next(); - //System.err.println("[READ]" + intClass.value); - } - - System.err.println("[MAIN end.] singleClass="+classWithList); - db.close(); - } -} \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/ann/exampleimpl/SingleClass.java b/src/main/java/org/warp/jcwdb/ann/exampleimpl/SingleClass.java deleted file mode 100644 index f35e5da..0000000 --- a/src/main/java/org/warp/jcwdb/ann/exampleimpl/SingleClass.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.warp.jcwdb.ann.exampleimpl; - -import org.warp.jcwdb.ann.*; - -import java.io.IOException; -import java.util.StringJoiner; - -public class SingleClass extends DBObject { - - @DBField(id = 0, type = DBDataType.INTEGER) - public int valueInt; - - @DBField(id = 1, type = DBDataType.OBJECT) - public String valueObj; - - @DBField(id = 2, type = DBDataType.DATABASE_OBJECT) - public IntClass valueDB; - - @DBPropertySetter(id = 0, type = DBDataType.INTEGER) - public void setInt(int value) { - setProperty(value); - } - - @DBPropertySetter(id = 1, type = DBDataType.OBJECT) - public void setObj(String value) { - setProperty(value); - } - - @DBPropertySetter(id = 2, type = DBDataType.DATABASE_OBJECT) - public void setDB(IntClass value) { - setProperty(value); - } - - @DBPropertyGetter(id = 0, type = DBDataType.INTEGER) - public int getInt() { - return getProperty(); - } - - @DBPropertyGetter(id = 1, type = DBDataType.OBJECT) - public String getObj() { - return getProperty(); - } - - @DBPropertyGetter(id = 2, type = DBDataType.DATABASE_OBJECT) - public IntClass getDB() { - return getProperty(); - } - - public SingleClass(JCWDatabase database) { - super(database); - } - - public SingleClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); - } - - @Override - public String toString() { - return new StringJoiner(", ", SingleClass.class.getSimpleName() + "[", "]") - .add("valueInt=" + valueInt) - .add("valueObj='" + valueObj + "'") - .add("valueDB=" + valueDB) - .add("getInt=" + getInt()) - .add("getObj='" + getObj() + "'") - .add("getDB=" + getDB()) - .toString(); - } -} diff --git a/src/test/java/org/warp/jcwdb/AppTest.java b/src/test/java/org/warp/jcwdb/AppTest.java deleted file mode 100644 index 7e58457..0000000 --- a/src/test/java/org/warp/jcwdb/AppTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.warp.jcwdb; - -import org.junit.Test; -import org.warp.jcwdb.ann.JCWDatabase; -import org.warp.jcwdb.ann.exampleimpl.IntClass; -import org.warp.jcwdb.ann.exampleimpl.SingleClass; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; - -import static org.junit.Assert.assertTrue; - -/** - * Unit test for simple App. - */ -public class AppTest { - /** - * Rigorous Test :-) - */ - @Test - public void shouldAnswerWithTrue() { - assertTrue(true); - } - - @Test - public void simpleClassTest() throws IOException { - long t0 = System.currentTimeMillis(); - Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_s.db")); - Files.deleteIfExists(Paths.get("N:\\TimedTemp\\database_s.idx")); - for (int i = 0; i < 3; i++) { - doSingleClassTest(); - } - } - - public static void doSingleClassTest() throws IOException { - System.err.println("doSingleClassTest"); - JCWDatabase db = new JCWDatabase(Paths.get("N:\\TimedTemp\\database_s.db"), Paths.get("N:\\TimedTemp\\database_s.idx")); - db.registerClass(SingleClass.class, 0); - db.registerClass(IntClass.class, 1); - SingleClass singleClass = db.loadRoot(SingleClass.class); - System.err.println("[MAIN init] singleClass="+singleClass); - - singleClass.getInt(); - singleClass.setInt((int) (Math.random() * 100)); - - singleClass.getObj(); - singleClass.setObj(String.valueOf(Math.random() * 100)); - - if (singleClass.getDB() == null) { - System.out.println("Get db was null"); - singleClass.setDB(new IntClass(db)); - } - IntClass intClass = singleClass.getDB(); - intClass.value = (int) (Math.random() * 100); - - singleClass.valueInt = (int) (Math.random() * 100); - - singleClass.valueObj = String.valueOf(Math.random() * 100); - - if (singleClass.valueDB == null) { - System.out.println("Value db was null"); - singleClass.valueDB = new IntClass(db); - } - singleClass.valueDB.value = (int) (Math.random() * 100); - - - System.err.println("[MAIN end.] singleClass="+singleClass); - db.close(); - } -} diff --git a/src/test/java/org/warp/jcwdb/FileAllocatorTest.java b/src/test/java/org/warp/jcwdb/FileAllocatorTest.java deleted file mode 100644 index 91e4792..0000000 --- a/src/test/java/org/warp/jcwdb/FileAllocatorTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.warp.jcwdb; - -import org.junit.Test; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.SeekableByteChannel; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public class FileAllocatorTest { - - @Test - public void shouldAllocateAtZero() throws IOException { - Path tempFile = Files.createTempFile("", ""); - SeekableByteChannel byteCh = Files.newByteChannel(tempFile); - FileAllocator allocator = new FileAllocator(byteCh); - long offset1 = allocator.allocate(512); - assertEquals(0, offset1); - } - - @Test - public void shouldAllocateAt512() throws IOException { - Path tempFile = Files.createTempFile("", ""); - SeekableByteChannel byteCh = Files.newByteChannel(tempFile, StandardOpenOption.WRITE); - byteCh.write(ByteBuffer.wrap(new byte[512])); - FileAllocator allocator = new FileAllocator(byteCh); - long offset1 = allocator.allocate(512); - assertEquals(512, offset1); - } - - @Test - public void shouldAllocateUnusedSpace() throws IOException { - Path tempFile = Files.createTempFile("", ""); - SeekableByteChannel byteCh = Files.newByteChannel(tempFile, StandardOpenOption.WRITE); - FileAllocator allocator = new FileAllocator(byteCh); - long offset1 = allocator.allocate(512); - allocator.markFree(offset1, 512); - long offset2 = allocator.allocate(128); - long offset3 = allocator.allocate(512-128); - long offset4 = allocator.allocate(128); - assertEquals(0, offset2); - assertEquals(128, offset3); - assertEquals(512, offset4); - } - -} diff --git a/src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java b/src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java new file mode 100644 index 0000000..60eaaa3 --- /dev/null +++ b/src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java @@ -0,0 +1,77 @@ +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); + 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() { + checkEmptyList(); + assertEquals(200, root.list.size()); + for (int i = 0; i < 200; i++) { + db.testRootClassValues(root.list.get(i)); + } + } + + private void checkEmptyList() { + DBObjectList 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 { + + public RootWithList(JCWDatabase database) { + super(database); + } + + public RootWithList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + @DBField(id = 0, type = DBDataType.DATABASE_OBJECT) + public DBDBObjectList list; + } +} diff --git a/src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java b/src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java new file mode 100644 index 0000000..c0e0aa1 --- /dev/null +++ b/src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java @@ -0,0 +1,80 @@ +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 { + + public RootTwoClasses(JCWDatabase database) { + super(database); + } + + public RootTwoClasses(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + @DBField(id = 0, type = DBDataType.DATABASE_OBJECT) + public TestUtils.RootClass class1; + + @DBField(id = 1, type = DBDataType.DATABASE_OBJECT) + public TestUtils.RootClass class2; + + @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); + } + } +} diff --git a/src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java b/src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java new file mode 100644 index 0000000..73160df --- /dev/null +++ b/src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java @@ -0,0 +1,69 @@ +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.NestedClass; +import org.warp.jcwdb.utils.TestUtils; + +import java.io.IOException; + +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(); + } +} diff --git a/src/test/java/org/warp/jcwdb/tests/DBRootCreation.java b/src/test/java/org/warp/jcwdb/tests/DBRootCreation.java new file mode 100644 index 0000000..ff962d0 --- /dev/null +++ b/src/test/java/org/warp/jcwdb/tests/DBRootCreation.java @@ -0,0 +1,48 @@ +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.*; + +public class DBRootCreation { + private TestUtils.WrappedDb db; + + @Before + public void setUp() throws Exception { + db = TestUtils.wrapDb().create(); + } + + @Test + public void shouldCreateRoot() { + 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) { + super(database); + } + + public RootClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + public boolean test() { + return true; + } + } +} diff --git a/src/test/java/org/warp/jcwdb/tests/DBRootFields.java b/src/test/java/org/warp/jcwdb/tests/DBRootFields.java new file mode 100644 index 0000000..0073e08 --- /dev/null +++ b/src/test/java/org/warp/jcwdb/tests/DBRootFields.java @@ -0,0 +1,30 @@ +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(); + } +} diff --git a/src/test/java/org/warp/jcwdb/tests/DBRootProperties.java b/src/test/java/org/warp/jcwdb/tests/DBRootProperties.java new file mode 100644 index 0000000..a144ecd --- /dev/null +++ b/src/test/java/org/warp/jcwdb/tests/DBRootProperties.java @@ -0,0 +1,30 @@ +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(); + } +} diff --git a/src/test/java/org/warp/jcwdb/utils/NestedClass.java b/src/test/java/org/warp/jcwdb/utils/NestedClass.java new file mode 100644 index 0000000..7a53bb3 --- /dev/null +++ b/src/test/java/org/warp/jcwdb/utils/NestedClass.java @@ -0,0 +1,32 @@ +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; + + @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(); + } + + public NestedClass(JCWDatabase database) { + super(database); + } + + public NestedClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } +} diff --git a/src/test/java/org/warp/jcwdb/utils/SimplestClass.java b/src/test/java/org/warp/jcwdb/utils/SimplestClass.java new file mode 100644 index 0000000..e73e55d --- /dev/null +++ b/src/test/java/org/warp/jcwdb/utils/SimplestClass.java @@ -0,0 +1,20 @@ +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(JCWDatabase database) { + super(database); + field1 = true; + } + + public SimplestClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } +} diff --git a/src/test/java/org/warp/jcwdb/utils/TestUtils.java b/src/test/java/org/warp/jcwdb/utils/TestUtils.java new file mode 100644 index 0000000..5f8c632 --- /dev/null +++ b/src/test/java/org/warp/jcwdb/utils/TestUtils.java @@ -0,0 +1,377 @@ +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.*; +import static org.junit.Assert.assertEquals; + +public class TestUtils { + public static WrappedDb wrapDb() { + return new WrappedDb(); + } + + public static class WrappedDb { + + private JCWDatabase db; + private Path tempDir; + private Runnable r; + + private WrappedDb() { + + } + + public WrappedDb create() throws IOException { + tempDir = Files.createTempDirectory("tests-"); + db = openDatabase(); + if (r != null) { + r.run(); + } + return this; + } + + public WrappedDb create(Consumer 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) { + setRootClassFields(root); + setRootClassProperties(root); + } + + public void setRootClassFields(RootClass root) { + 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) { + 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(Runnable 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; + + @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 RootClass(JCWDatabase database) { + super(database); + } + + public RootClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { + super(database, objectInfo); + } + + public boolean test() { + return true; + } + } +} From a772ac543187ddc7fe433f49a088fdd35d0e28af Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Thu, 31 Jan 2019 20:05:23 +0100 Subject: [PATCH 4/5] First real copy-on-write implementation --- pom.xml | 2 +- src/main/java/org/warp/cowdb/BlockInfo.java | 44 + .../cowdb/ByteBufferBackedInputStream.java | 33 + src/main/java/org/warp/cowdb/Database.java | 839 ++++++++++++++++++ .../java/org/warp/cowdb/EnhancedObject.java | 101 +++ .../warp/cowdb/EnhancedObjectFullInfo.java | 47 + src/main/java/org/warp/cowdb/IBlocksIO.java | 28 + .../java/org/warp/cowdb/IBlocksMetadata.java | 44 + .../java/org/warp/cowdb/IDataInitializer.java | 7 + src/main/java/org/warp/cowdb/IDatabase.java | 10 + src/main/java/org/warp/cowdb/IFileIO.java | 35 + src/main/java/org/warp/cowdb/IObjectsIO.java | 109 +++ .../java/org/warp/cowdb/IReferencesIO.java | 37 + .../org/warp/cowdb/IReferencesMetadata.java | 31 + src/main/java/org/warp/jcwdb/Cleaner.java | 6 - .../java/org/warp/jcwdb/FileAllocator.java | 5 +- .../java/org/warp/jcwdb/FileIndexManager.java | 9 +- .../java/org/warp/jcwdb/IndexDetails.java | 2 - .../java/org/warp/jcwdb/IndexManager.java | 5 - src/main/java/org/warp/jcwdb/Saveable.java | 2 - .../org/warp/jcwdb/ann/ConsumerWithIO.java | 15 + .../java/org/warp/jcwdb/ann/DBArrayList.java | 20 +- .../org/warp/jcwdb/ann/DBDBObjectList.java | 14 +- .../java/org/warp/jcwdb/ann/DBDataType.java | 3 +- .../java/org/warp/jcwdb/ann/DBObject.java | 119 +-- .../java/org/warp/jcwdb/ann/DBObjectList.java | 13 +- .../org/warp/jcwdb/ann/DataInitializer.java | 96 ++ .../java/org/warp/jcwdb/ann/DataLoader.java | 645 ++++++++++++++ .../org/warp/jcwdb/ann/DatabaseManager.java | 466 +--------- .../java/org/warp/jcwdb/ann/JCWDatabase.java | 40 +- .../org/warp/jcwdb/ann/RunnableWithIO.java | 19 + .../org/warp/jcwdb/ann/SupplierWithIO.java | 8 + .../warp/jcwdb/tests/DBDBObjectListTests.java | 22 +- .../warp/jcwdb/tests/DBMultipleDBObjects.java | 21 +- .../warp/jcwdb/tests/DBNestedDBObjects.java | 3 - .../org/warp/jcwdb/tests/DBRootCreation.java | 15 +- .../tests/NDBMultipleEnhancedObjects.java | 90 ++ .../org/warp/jcwdb/utils/ConsumerWithIO.java | 32 + .../org/warp/jcwdb/utils/NSimplestClass.java | 29 + .../java/org/warp/jcwdb/utils/NTestUtils.java | 383 ++++++++ .../org/warp/jcwdb/utils/NestedClass.java | 15 +- .../org/warp/jcwdb/utils/SimplestClass.java | 14 +- .../java/org/warp/jcwdb/utils/TestUtils.java | 34 +- 43 files changed, 2851 insertions(+), 661 deletions(-) create mode 100644 src/main/java/org/warp/cowdb/BlockInfo.java create mode 100644 src/main/java/org/warp/cowdb/ByteBufferBackedInputStream.java create mode 100644 src/main/java/org/warp/cowdb/Database.java create mode 100644 src/main/java/org/warp/cowdb/EnhancedObject.java create mode 100644 src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java create mode 100644 src/main/java/org/warp/cowdb/IBlocksIO.java create mode 100644 src/main/java/org/warp/cowdb/IBlocksMetadata.java create mode 100644 src/main/java/org/warp/cowdb/IDataInitializer.java create mode 100644 src/main/java/org/warp/cowdb/IDatabase.java create mode 100644 src/main/java/org/warp/cowdb/IFileIO.java create mode 100644 src/main/java/org/warp/cowdb/IObjectsIO.java create mode 100644 src/main/java/org/warp/cowdb/IReferencesIO.java create mode 100644 src/main/java/org/warp/cowdb/IReferencesMetadata.java create mode 100644 src/main/java/org/warp/jcwdb/ann/ConsumerWithIO.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DataInitializer.java create mode 100644 src/main/java/org/warp/jcwdb/ann/DataLoader.java create mode 100644 src/main/java/org/warp/jcwdb/ann/RunnableWithIO.java create mode 100644 src/main/java/org/warp/jcwdb/ann/SupplierWithIO.java create mode 100644 src/test/java/org/warp/jcwdb/tests/NDBMultipleEnhancedObjects.java create mode 100644 src/test/java/org/warp/jcwdb/utils/ConsumerWithIO.java create mode 100644 src/test/java/org/warp/jcwdb/utils/NSimplestClass.java create mode 100644 src/test/java/org/warp/jcwdb/utils/NTestUtils.java diff --git a/pom.xml b/pom.xml index 6451ac9..378d8aa 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.warp jcwdb - 1.2-SNAPSHOT + 1.3 jcwdb diff --git a/src/main/java/org/warp/cowdb/BlockInfo.java b/src/main/java/org/warp/cowdb/BlockInfo.java new file mode 100644 index 0000000..02b301b --- /dev/null +++ b/src/main/java/org/warp/cowdb/BlockInfo.java @@ -0,0 +1,44 @@ +package org.warp.cowdb; + +import java.util.Objects; +import java.util.StringJoiner; + +public class BlockInfo { + private final long index; + private final int size; + + public BlockInfo(long index, int size) { + this.index = index; + this.size = size; + } + + public long getIndex() { + return index; + } + + public int getSize() { + return size; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BlockInfo blockInfo = (BlockInfo) o; + return index == blockInfo.index && + size == blockInfo.size; + } + + @Override + public int hashCode() { + return Objects.hash(index, size); + } + + @Override + public String toString() { + return new StringJoiner(", ", BlockInfo.class.getSimpleName() + "[", "]") + .add("index=" + index) + .add("size=" + size) + .toString(); + } +} diff --git a/src/main/java/org/warp/cowdb/ByteBufferBackedInputStream.java b/src/main/java/org/warp/cowdb/ByteBufferBackedInputStream.java new file mode 100644 index 0000000..2c339a0 --- /dev/null +++ b/src/main/java/org/warp/cowdb/ByteBufferBackedInputStream.java @@ -0,0 +1,33 @@ +package org.warp.cowdb; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +public class ByteBufferBackedInputStream extends InputStream { + + ByteBuffer buf; + + public ByteBufferBackedInputStream(ByteBuffer buf) { + this.buf = buf; + } + + public int read() throws IOException { + if (!buf.hasRemaining()) { + return -1; + } + return buf.get() & 0xFF; + } + + public int read(byte[] bytes, int off, int len) + throws IOException { + if (!buf.hasRemaining()) { + return -1; + } + + len = Math.min(len, buf.remaining()); + buf.get(bytes, off, len); + return len; + + } +} diff --git a/src/main/java/org/warp/cowdb/Database.java b/src/main/java/org/warp/cowdb/Database.java new file mode 100644 index 0000000..97ba2dc --- /dev/null +++ b/src/main/java/org/warp/cowdb/Database.java @@ -0,0 +1,839 @@ +package org.warp.cowdb; + +import com.esotericsoftware.kryo.Kryo; +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.longs.LongArrayList; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.warp.jcwdb.ann.*; + +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.ByteBuffer; +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.function.Supplier; + +import static org.warp.cowdb.IBlocksMetadata.EMPTY_BLOCK_ID; +import static org.warp.cowdb.IBlocksMetadata.EMPTY_BLOCK_INFO; + +public class Database implements IDatabase { + + private final DatabaseFileIO fileIO; + private final DatabaseBlocksIO blocksIO; + private final DatabaseBlocksMetadata blocksMetadata; + private final DatabaseReferencesIO referencesIO; + private final DatabaseReferencesMetadata referencesMetadata; + private final DatabaseObjectsIO objectsIO; + private final DatabaseDataInitializer dataInitializer; + private EnhancedObject loadedRootObject; + + public Database(Path dataFile, Path blocksMetaFile, Path referencesMetaFile) throws IOException { + if (Files.notExists(dataFile)) { + Files.createFile(dataFile); + } + if (Files.notExists(blocksMetaFile)) { + Files.createFile(blocksMetaFile); + } + if (Files.notExists(referencesMetaFile)) { + Files.createFile(referencesMetaFile); + } + this.fileIO = new DatabaseFileIO(dataFile); + this.blocksMetadata = new DatabaseBlocksMetadata(blocksMetaFile); + this.blocksIO = new DatabaseBlocksIO(fileIO, blocksMetadata); + this.referencesMetadata = new DatabaseReferencesMetadata(referencesMetaFile); + this.referencesIO = new DatabaseReferencesIO(blocksIO, referencesMetadata); + this.objectsIO = new DatabaseObjectsIO(this, referencesIO); + this.dataInitializer = new DatabaseDataInitializer(objectsIO); + } + + @Override + public IDataInitializer getDataInitializer() { + return dataInitializer; + } + + @Override + public IObjectsIO getObjectsIO() { + return objectsIO; + } + + @Override + public void close() throws IOException { + this.objectsIO.setEnhancedObject(0, loadedRootObject); + this.referencesMetadata.close(); + this.blocksMetadata.close(); + this.fileIO.close(); + } + + public T loadRoot(Class type) throws IOException { + return loadRoot(type, () -> { + T obj = objectsIO.instantiateEnhancedObject(type); + dataInitializer.initializeDBObject(obj); + return obj; + }); + } + + public T loadRoot(Class type, SupplierWithIO ifAbsent) throws IOException { + if (loadedRootObject != null) { + throw new RuntimeException("Root already set!"); + } + T root; + if (referencesMetadata.firstFreeReference > 0) { + root = objectsIO.loadEnhancedObject(0, type); + } else { + if (objectsIO.newNullObject() != 0) { + throw new IOException("Can't allocate root!"); + } else { + root = ifAbsent.getWithIO(); + objectsIO.setEnhancedObject(0, root); + } + } + loadedRootObject = root; + return root; + } + + public static class DatabaseDataInitializer implements IDataInitializer { + + private final DatabaseObjectsIO objectsIO; + + public DatabaseDataInitializer(DatabaseObjectsIO objectsIO) { + this.objectsIO = objectsIO; + } + + @Override + public void initializeDBObject(EnhancedObject obj) throws IOException { + initializeDBObjectFields(obj); + initializeDBObjectProperties(obj); + obj.initialize(); + } + + private void initializeDBObjectFields(EnhancedObject obj) throws IOException { + // Declare the variables needed to get the biggest field Id + Field[] unorderedFields = objectsIO.getFields(obj); + // Find the biggest field Id + int biggestFieldId = objectsIO.getBiggestFieldId(unorderedFields); + + // Allocate new UIDs + long[] fieldUIDs = objectsIO.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(); + objectsIO.loadField(obj, field, fieldType, fieldUIDs[fieldId]); + fields[fieldId] = field; + orderedFieldTypes[fieldId] = fieldType; + } + // Set fields metadata + obj.setFields(fields, orderedFieldTypes, fieldUIDs); + } + + private void initializeDBObjectProperties(EnhancedObject obj) throws IOException { + // 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 = objectsIO.getBiggestPropertyGetterId(unorderedPropertyGetters); + int biggestSetter = objectsIO.getBiggestPropertySetterId(unorderedPropertySetters); + int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter; + + // Allocate new UIDs + long[] propertyUIDs = objectsIO.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 setterMethods = new LinkedHashMap<>(); + Map 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); + } + } + + public static class DatabaseObjectsIO implements IObjectsIO { + + private final IDatabase database; + private final DatabaseReferencesIO referencesIO; + + private Kryo kryo = new Kryo(); + + private DatabaseObjectsIO(IDatabase database, DatabaseReferencesIO referencesIO) { + this.database = database; + this.referencesIO = referencesIO; + } + + @Override + public T loadEnhancedObject(long reference, Class objectType) throws IOException { + ByteBuffer buffer = referencesIO.readFromReference(reference); + if (buffer.limit() == 0) { + return null; + } + int fieldsCount = buffer.getInt(); + int methodsCount = buffer.getInt(); + long[] fieldRefs = new long[fieldsCount]; + long[] methodRefs = new long[methodsCount]; + for (int i = 0; i < fieldsCount; i++) { + fieldRefs[i] = buffer.getLong(); + } + for (int i = 0; i < methodsCount; i++) { + methodRefs[i] = buffer.getLong(); + } + return preloadEnhancedObject(objectType, fieldRefs, methodRefs); + } + + + @SuppressWarnings("unchecked") + private Object loadData(DBDataType propertyType, long dataReference, Supplier> returnType) throws IOException { + switch (propertyType) { + case DATABASE_OBJECT: + return loadEnhancedObject(dataReference, (Class) returnType.get()); + case OBJECT: + return loadObject(dataReference); + case REFERENCES_LIST: + return loadReferencesList(dataReference); + case BOOLEAN: + return loadBoolean(dataReference); + case BYTE: + return loadByte(dataReference); + case SHORT: + return loadShort(dataReference); + case CHAR: + return loadChar(dataReference); + case INTEGER: + return loadInt(dataReference); + case LONG: + return loadLong(dataReference); + default: + throw new NullPointerException("Unknown data type"); + } + } + + private void setData(long reference, DBDataType propertyType, T loadedPropertyValue) throws IOException { + switch (propertyType) { + case BOOLEAN: + setBoolean(reference, loadedPropertyValue != null && (boolean) loadedPropertyValue); + break; + case BYTE: + setByte(reference, loadedPropertyValue == null ? 0 : (byte) loadedPropertyValue); + break; + case SHORT: + setShort(reference, loadedPropertyValue == null ? 0 : (short) loadedPropertyValue); + break; + case CHAR: + setChar(reference, loadedPropertyValue == null ? 0 : (char) loadedPropertyValue); + break; + case INTEGER: + setInt(reference, loadedPropertyValue == null ? 0 : (int) loadedPropertyValue); + break; + case LONG: + setLong(reference, loadedPropertyValue == null ? 0 : (long) loadedPropertyValue); + break; + case OBJECT: + setObject(reference, loadedPropertyValue); + break; + case REFERENCES_LIST: + setReferencesList(reference, (LongArrayList) loadedPropertyValue); + break; + case DATABASE_OBJECT: + setEnhancedObject(reference, (EnhancedObject) loadedPropertyValue); + break; + } + } + + @Override + public void setEnhancedObject(long reference, T value) throws IOException { + if (value != null) { + EnhancedObjectFullInfo objectFullInfo = value.getAllInfo(); + int totalSize = Integer.BYTES * 2 + objectFullInfo.getFieldReferences().length * Long.BYTES + objectFullInfo.getPropertyReferences().length * Long.BYTES; + ByteBuffer buffer = ByteBuffer.allocate(totalSize); + buffer.putInt(objectFullInfo.getFieldReferences().length); + buffer.putInt(objectFullInfo.getPropertyReferences().length); + for (int i = 0; i < objectFullInfo.getFieldReferences().length; i++) { + buffer.putLong(objectFullInfo.getFieldReferences()[i]); + } + for (int i = 0; i < objectFullInfo.getPropertyReferences().length; i++) { + buffer.putLong(objectFullInfo.getPropertyReferences()[i]); + } + for (int i = 0; i < objectFullInfo.getFieldReferences().length; i++) { + try { + setData(objectFullInfo.getFieldReferences()[i], objectFullInfo.getFieldTypes()[i], objectFullInfo.getFields()[i].get(value)); + } catch (IllegalAccessException e) { + throw new IOError(e); + } + } + for (int i = 0; i < objectFullInfo.getPropertyReferences().length; i++) { + setData(objectFullInfo.getPropertyReferences()[i], objectFullInfo.getPropertyTypes()[i], objectFullInfo.getLoadedPropertyValues()[i]); + } + buffer.flip(); + referencesIO.writeToReference(reference, totalSize, buffer); + } else { + referencesIO.writeToReference(reference, 0, null); + } + } + + @SuppressWarnings("unchecked") + @Override + public T loadObject(long reference) throws IOException { + ByteBuffer buffer = referencesIO.readFromReference(reference); + if (buffer.limit() == 0) { + return null; + } + buffer.rewind(); + byte[] data = buffer.array(); + return (T) kryo.readClassAndObject(new Input(data)); + } + + @Override + public LongArrayList loadReferencesList(long reference) throws IOException { + ByteBuffer buffer = referencesIO.readFromReference(reference); + if (buffer.limit() == 0) { + return null; + } + int itemsCount = buffer.getInt(); + LongArrayList arrayList = new LongArrayList(); + for (int i = 0; i < itemsCount; i++) { + arrayList.add(buffer.getLong()); + } + return arrayList; + } + + @Override + public boolean loadBoolean(long reference) throws IOException { + ByteBuffer buffer = referencesIO.readFromReference(reference); + if (buffer.limit() == 0) { + return false; + } + return buffer.get() == 1; + } + + @Override + public byte loadByte(long reference) throws IOException { + ByteBuffer buffer = referencesIO.readFromReference(reference); + if (buffer.limit() == 0) { + return 0; + } + return buffer.get(); + } + + @Override + public short loadShort(long reference) throws IOException { + ByteBuffer buffer = referencesIO.readFromReference(reference); + if (buffer.limit() == 0) { + return 0; + } + return buffer.getShort(); + } + + @Override + public char loadChar(long reference) throws IOException { + ByteBuffer buffer = referencesIO.readFromReference(reference); + if (buffer.limit() == 0) { + return 0; + } + return buffer.getChar(); + } + + @Override + public int loadInt(long reference) throws IOException { + ByteBuffer buffer = referencesIO.readFromReference(reference); + if (buffer.limit() == 0) { + return 0; + } + return buffer.getInt(); + } + + @Override + public long loadLong(long reference) throws IOException { + ByteBuffer buffer = referencesIO.readFromReference(reference); + if (buffer.limit() == 0) { + return 0; + } + return buffer.getLong(); + } + + @Override + public void setObject(long reference, T value) throws IOException { + if (value != null) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + Output output = new Output(outputStream); + kryo.writeClassAndObject(output, value); + output.flush(); + byte[] data = outputStream.toByteArray(); + ByteBuffer dataByteBuffer = ByteBuffer.wrap(data); + referencesIO.writeToReference(reference, data.length, dataByteBuffer); + } else { + referencesIO.writeToReference(reference, 0, null); + } + } + + @Override + public void setReferencesList(long reference, LongArrayList value) throws IOException { + if (value != null) { + int items = value.size(); + ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * items + Integer.BYTES); + buffer.putInt(items); + for (int i = 0; i < items; i++) { + buffer.putLong(value.getLong(i)); + } + buffer.flip(); + referencesIO.writeToReference(reference, buffer.limit(), buffer); + } else { + referencesIO.writeToReference(reference, 0, null); + } + } + + @Override + public void setBoolean(long reference, boolean value) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES); + buffer.put(value ? (byte) 1 : (byte) 0); + buffer.flip(); + referencesIO.writeToReference(reference, Byte.BYTES, buffer); + } + + @Override + public void setByte(long reference, byte value) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(Byte.BYTES); + buffer.put(value); + buffer.flip(); + referencesIO.writeToReference(reference, Byte.BYTES, buffer); + } + + @Override + public void setShort(long reference, short value) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(Short.BYTES); + buffer.putShort(value); + buffer.flip(); + referencesIO.writeToReference(reference, Short.BYTES, buffer); + } + + @Override + public void setChar(long reference, char value) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(Character.BYTES); + buffer.putChar(value); + buffer.flip(); + referencesIO.writeToReference(reference, Character.BYTES, buffer); + } + + @Override + public void setInt(long reference, int value) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES); + buffer.putInt(value); + buffer.flip(); + referencesIO.writeToReference(reference, Integer.BYTES, buffer); + } + + @Override + public void setLong(long reference, long value) throws IOException { + ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); + buffer.putLong(value); + buffer.flip(); + referencesIO.writeToReference(reference, Long.BYTES, buffer); + } + + @Override + public long newNullObject() throws IOException { + return referencesIO.allocateReference(); + } + + @Override + public void loadProperty(EnhancedObject obj, int propertyId, Method property, DBDataType propertyType, long propertyUID) throws IOException { + obj.setProperty(propertyId, loadData(propertyType, propertyUID, property::getReturnType)); + } + + private void preloadEnhancedObjectProperties(T obj, long[] propertyReferences) { + // 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 setterMethods = new LinkedHashMap<>(); + Map 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, propertyReferences, setterMethods, getterMethods); + } + + private int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) { + int biggestPropertyId = -1; + for (Method property : unorderedPropertyGetters) { + DBPropertyGetter fieldAnnotation = property.getAnnotation(DBPropertyGetter.class); + int propertyId = fieldAnnotation.id(); + if (propertyId > biggestPropertyId) { + biggestPropertyId = propertyId; + } + } + return biggestPropertyId; + } + + private int getBiggestPropertySetterId(Method[] unorderedPropertySetters) { + int biggestPropertyId = -1; + for (Method property : unorderedPropertySetters) { + DBPropertySetter fieldAnnotation = property.getAnnotation(DBPropertySetter.class); + int propertyId = fieldAnnotation.id(); + if (propertyId > biggestPropertyId) { + biggestPropertyId = propertyId; + } + } + return biggestPropertyId; + } + + private void preloadEnhancedObjectFields(T obj, long[] fieldReferences) throws IOException { + // 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, fieldReferences[fieldId]); + fields[fieldId] = field; + orderedFieldTypes[fieldId] = fieldType; + } + // Set fields metadata + obj.setFields(fields, orderedFieldTypes, fieldReferences); + } + + private void loadField(T obj, Field field, DBDataType fieldType, long fieldReference) throws IOException { + Object data = loadData(fieldType, fieldReference, field::getType); + 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); + } + } + + private Field[] getFields(T obj) { + return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class); + } + + private int getBiggestFieldId(Field[] unorderedFields) { + int biggestFieldId = -1; + for (Field field : unorderedFields) { + DBField fieldAnnotation = field.getAnnotation(DBField.class); + int propertyId = fieldAnnotation.id(); + if (propertyId > biggestFieldId) { + biggestFieldId = propertyId; + } + } + return biggestFieldId; + } + + private T instantiateEnhancedObject(Class type) throws IOException { + try { + T obj = type.getConstructor().newInstance(); + obj.database = database; + 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 T preloadEnhancedObject(Class objectType, long[] fieldRefs, long[] methodRefs) throws IOException { + T obj = instantiateEnhancedObject(objectType); + preloadEnhancedObjectFields(obj, fieldRefs); + preloadEnhancedObjectProperties(obj, methodRefs); + return obj; + } + + public long[] allocateNewUIDs(int quantity) throws IOException { + long[] ids = new long[quantity]; + for (int i = 0; i < quantity; i++) { + ids[i] = newNullObject(); + } + return ids; + } + } + + public static class DatabaseReferencesIO implements IReferencesIO { + + private final DatabaseBlocksIO blocksIO; + private final DatabaseReferencesMetadata referencesMetadata; + + public DatabaseReferencesIO(DatabaseBlocksIO blocksIO, DatabaseReferencesMetadata referencesMetadata) { + this.blocksIO = blocksIO; + this.referencesMetadata = referencesMetadata; + } + + @Override + public long allocateReference() throws IOException { + return referencesMetadata.newReference(EMPTY_BLOCK_ID); + } + + @Override + public long allocateReference(int size, ByteBuffer data) throws IOException { + long blockId = (size == 0) ? EMPTY_BLOCK_ID : blocksIO.newBlock(size, data); + return referencesMetadata.newReference(blockId); + } + + @Override + public void writeToReference(long reference, int size, ByteBuffer data) throws IOException { + long blockId = (size == 0) ? EMPTY_BLOCK_ID : blocksIO.newBlock(size, data); + referencesMetadata.editReference(reference, blockId); + } + + @Override + public ByteBuffer readFromReference(long reference) throws IOException { + long blockId = referencesMetadata.getReference(reference); + return blocksIO.readBlock(blockId); + } + } + + 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; + } + + @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) { + return EMPTY_BLOCK_ID; + } + SeekableByteChannel currentFileChannel = metaFileChannel.position(reference * REF_META_BYTES_COUNT); + currentFileChannel.read(buffer); + buffer.flip(); + return buffer.getLong(); + } + + @Override + public long newReference(long blockId) throws IOException { + long newReference = firstFreeReference++; + editReference(newReference, blockId); + return newReference; + } + + @Override + public void editReference(long reference, long blockId) throws IOException { + ByteBuffer data = ByteBuffer.allocate(REF_META_BYTES_COUNT); + data.putLong(blockId); + SeekableByteChannel currentFileChannel = metaFileChannel.position(reference * REF_META_BYTES_COUNT); + data.flip(); + currentFileChannel.write(data); + } + + @Override + public void close() throws IOException { + metaFileChannel.close(); + } + } + + public static class DatabaseBlocksIO implements IBlocksIO { + + private final DatabaseFileIO fileIO; + private final IBlocksMetadata blocksMetadata; + + private DatabaseBlocksIO(DatabaseFileIO fileIO, IBlocksMetadata blocksMetadata) { + this.fileIO = fileIO; + this.blocksMetadata = blocksMetadata; + } + + @Override + public long newBlock(int size, ByteBuffer data) throws IOException { + long index = fileIO.writeAtEnd(size, data); + return blocksMetadata.newBlock(index, size); + } + + @Override + public ByteBuffer readBlock(long blockId) throws IOException { + if (blockId == EMPTY_BLOCK_ID) { + return ByteBuffer.wrap(new byte[0]); + } + BlockInfo blockInfo = blocksMetadata.getBlockInfo(blockId); + return fileIO.readAt(blockInfo.getIndex(), blockInfo.getSize()); + } + + @Override + public void close() { + + } + } + + public static class DatabaseBlocksMetadata implements IBlocksMetadata { + private final SeekableByteChannel metaFileChannel; + private final int BLOCK_META_BYTES_COUNT = Long.BYTES + Integer.BYTES; + private long firstFreeBlock; + + private DatabaseBlocksMetadata(Path metaFile) throws IOException { + metaFileChannel = Files.newByteChannel(metaFile, StandardOpenOption.READ, StandardOpenOption.WRITE); + firstFreeBlock = metaFileChannel.size() / BLOCK_META_BYTES_COUNT; + } + + @Override + public BlockInfo getBlockInfo(long blockId) throws IOException { + if (blockId == EMPTY_BLOCK_ID) { + return EMPTY_BLOCK_INFO; + } + ByteBuffer buffer = ByteBuffer.allocate(BLOCK_META_BYTES_COUNT); + metaFileChannel.position(blockId * BLOCK_META_BYTES_COUNT).read(buffer); + buffer.flip(); + long index = buffer.getLong(); + int size = buffer.getInt(); + return new BlockInfo(index, size); + } + + @Override + public long newBlock(long index, int size) throws IOException { + long newBlockId = firstFreeBlock++; + ByteBuffer data = ByteBuffer.allocate(BLOCK_META_BYTES_COUNT); + data.putLong(index); + data.putInt(size); + data.flip(); + metaFileChannel.position(newBlockId * BLOCK_META_BYTES_COUNT).write(data); + return newBlockId; + } + + @Override + public void close() throws IOException { + metaFileChannel.close(); + } + } + + public static class DatabaseFileIO implements IFileIO { + + private final SeekableByteChannel dataFileChannel; + private final Object dataAccessLock = new Object(); + private long firstFreeIndex; + + private DatabaseFileIO(Path dataFile) throws IOException { + synchronized (dataAccessLock) { + dataFileChannel = Files.newByteChannel(dataFile, StandardOpenOption.READ, StandardOpenOption.WRITE); + firstFreeIndex = dataFileChannel.size(); + } + } + + @Override + public ByteBuffer readAt(long index, int length) throws IOException { + ByteBuffer dataBuffer = ByteBuffer.allocate(length); + dataFileChannel.position(index).read(dataBuffer); + dataBuffer.flip(); + return dataBuffer; + } + + @Override + public void writeAt(long index, int length, ByteBuffer data) throws IOException { + synchronized (dataAccessLock) { + if (data.position() != 0) { + throw new IOException("You didn't flip the ByteBuffer!"); + } + if (firstFreeIndex < index + length) { + firstFreeIndex = index + length; + } + dataFileChannel.position(index).write(data); + } + } + + @Override + public long writeAtEnd(int length, ByteBuffer data) throws IOException { + synchronized (dataAccessLock) { + long index = firstFreeIndex; + firstFreeIndex += length; + writeAt(index, length, data); + return index; + } + } + + @Override + public void close() throws IOException { + synchronized (dataAccessLock) { + dataFileChannel.close(); + } + } + } +} diff --git a/src/main/java/org/warp/cowdb/EnhancedObject.java b/src/main/java/org/warp/cowdb/EnhancedObject.java new file mode 100644 index 0000000..83be611 --- /dev/null +++ b/src/main/java/org/warp/cowdb/EnhancedObject.java @@ -0,0 +1,101 @@ +package org.warp.cowdb; + +import org.apache.commons.lang3.reflect.MethodUtils; +import org.warp.jcwdb.ann.DBDataType; +import org.warp.jcwdb.ann.DBPropertyGetter; +import org.warp.jcwdb.ann.DBPropertySetter; + +import java.io.IOError; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Map; + +public abstract class EnhancedObject { + protected IDatabase database; + private Field[] fields; + private DBDataType[] fieldTypes; + private long[] fieldReferences; + private Method[] propertyGetters; + private Method[] propertySetters; + private DBDataType[] propertyTypes; + private long[] propertyReferences; + private boolean[] loadedProperties; + private Object[] loadedPropertyValues; + private Map setterMethods; + private Map getterMethods; + + public EnhancedObject() { + + } + + public EnhancedObject(IDatabase database) throws IOException { + this.database = database; + database.getDataInitializer().initializeDBObject(this); + } + + public abstract void initialize() throws IOException; + + + public void setFields(Field[] fields, DBDataType[] fieldTypes, long[] fieldReferences) { + this.fields = fields; + this.fieldTypes = fieldTypes; + this.fieldReferences = fieldReferences; + } + + public Method[] getPropertyGetters() { + return MethodUtils.getMethodsWithAnnotation(this.getClass(), DBPropertyGetter.class); + } + + public Method[] getPropertySetters() { + return MethodUtils.getMethodsWithAnnotation(this.getClass(), DBPropertySetter.class); + } + + public void setProperties(Method[] propertyGetters, Method[] propertySetters, DBDataType[] propertyTypes, long[] propertyReferences, Map setterMethods, Map getterMethods) { + this.propertyGetters = propertyGetters; + this.propertySetters = propertySetters; + this.propertyTypes = propertyTypes; + this.propertyReferences = propertyReferences; + this.loadedProperties = new boolean[this.propertyReferences.length]; + this.loadedPropertyValues = new Object[this.propertyReferences.length]; + this.setterMethods = setterMethods; + this.getterMethods = getterMethods; + } + + public EnhancedObjectFullInfo getAllInfo() { + return new EnhancedObjectFullInfo(fieldReferences, fieldTypes, fields, propertyReferences, propertyTypes, loadedPropertyValues); + } + + + public 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); + } + } + + @SuppressWarnings("unchecked") + private T getProperty(int propertyId) throws IOException { + if (!loadedProperties[propertyId]) { + long propertyUID = propertyReferences[propertyId]; + database.getObjectsIO().loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID); + } + return (T) loadedPropertyValues[propertyId]; + } + + public 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(), value); + } + + public void setProperty(int propertyId, T value) { + loadedPropertyValues[propertyId] = value; + loadedProperties[propertyId] = true; + } +} diff --git a/src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java b/src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java new file mode 100644 index 0000000..8cd28ea --- /dev/null +++ b/src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java @@ -0,0 +1,47 @@ +package org.warp.cowdb; + +import org.warp.jcwdb.ann.DBDataType; + +import java.lang.reflect.Field; + +public class EnhancedObjectFullInfo { + private final long[] fieldReferences; + private final DBDataType[] fieldTypes; + private final Field[] fields; + private final long[] propertyReferences; + private final DBDataType[] propertyTypes; + private final Object[] loadedPropertyValues; + + public EnhancedObjectFullInfo(long[] fieldReferences, DBDataType[] fieldTypes, Field[] fields, long[] propertyReferences, DBDataType[] propertyTypes, Object[] loadedPropertyValues) { + this.fieldReferences = fieldReferences; + this.fieldTypes = fieldTypes; + this.fields = fields; + this.propertyReferences = propertyReferences; + this.propertyTypes = propertyTypes; + this.loadedPropertyValues = loadedPropertyValues; + } + + public long[] getFieldReferences() { + return fieldReferences; + } + + public DBDataType[] getFieldTypes() { + return fieldTypes; + } + + public Field[] getFields() { + return fields; + } + + public long[] getPropertyReferences() { + return propertyReferences; + } + + public DBDataType[] getPropertyTypes() { + return propertyTypes; + } + + public Object[] getLoadedPropertyValues() { + return loadedPropertyValues; + } +} diff --git a/src/main/java/org/warp/cowdb/IBlocksIO.java b/src/main/java/org/warp/cowdb/IBlocksIO.java new file mode 100644 index 0000000..0fc0a14 --- /dev/null +++ b/src/main/java/org/warp/cowdb/IBlocksIO.java @@ -0,0 +1,28 @@ +package org.warp.cowdb; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +public interface IBlocksIO { + /** + * Allocate a block + * @param size block size + * @param data block data + * @return the block id + */ + long newBlock(int size, ByteBuffer data) throws IOException; + + /** + * Read a block + * @param blockId block id + * @return block data + */ + ByteBuffer readBlock(long blockId) throws IOException; + + /** + * Close file + */ + void close(); +} diff --git a/src/main/java/org/warp/cowdb/IBlocksMetadata.java b/src/main/java/org/warp/cowdb/IBlocksMetadata.java new file mode 100644 index 0000000..12a6443 --- /dev/null +++ b/src/main/java/org/warp/cowdb/IBlocksMetadata.java @@ -0,0 +1,44 @@ +package org.warp.cowdb; + +import java.io.IOException; + +public interface IBlocksMetadata { + long EMPTY_BLOCK_ID = -1; + BlockInfo EMPTY_BLOCK_INFO = new BlockInfo(0, 0); + + /** + * Get block info + * @param blockId block id + * @return block metadata + */ + BlockInfo getBlockInfo(long blockId) throws IOException; + + /** + * New empty block info + * @return block id + */ + default long newBlock() { + return EMPTY_BLOCK_ID; + } + + /** + * Set block info + * @param index block index + * @param size block size + * @return block id + */ + long newBlock(long index, int size) throws IOException; + + /** + * Set block info + * @param blockInfo block info + * @return block id + */ + default long newBlock(BlockInfo blockInfo) throws IOException { + return this.newBlock(blockInfo.getIndex(), blockInfo.getSize()); + } + /** + * Close file + */ + void close() throws IOException; +} diff --git a/src/main/java/org/warp/cowdb/IDataInitializer.java b/src/main/java/org/warp/cowdb/IDataInitializer.java new file mode 100644 index 0000000..371d5ea --- /dev/null +++ b/src/main/java/org/warp/cowdb/IDataInitializer.java @@ -0,0 +1,7 @@ +package org.warp.cowdb; + +import java.io.IOException; + +public interface IDataInitializer { + void initializeDBObject(EnhancedObject obj) throws IOException; +} diff --git a/src/main/java/org/warp/cowdb/IDatabase.java b/src/main/java/org/warp/cowdb/IDatabase.java new file mode 100644 index 0000000..2e87d39 --- /dev/null +++ b/src/main/java/org/warp/cowdb/IDatabase.java @@ -0,0 +1,10 @@ +package org.warp.cowdb; + +import java.io.IOException; + +public interface IDatabase { + + void close() throws IOException; + IDataInitializer getDataInitializer(); + IObjectsIO getObjectsIO(); +} diff --git a/src/main/java/org/warp/cowdb/IFileIO.java b/src/main/java/org/warp/cowdb/IFileIO.java new file mode 100644 index 0000000..31bda13 --- /dev/null +++ b/src/main/java/org/warp/cowdb/IFileIO.java @@ -0,0 +1,35 @@ +package org.warp.cowdb; + +import java.io.*; +import java.nio.ByteBuffer; + +public interface IFileIO { + /** + * Read *length* bytes in position *index* + * @param index index + * @param length length + * @return bytes + */ + ByteBuffer readAt(long index, int length) throws IOException; + + /** + * Write *length* bytes in position *index* + * @param index index + * @param length length + * @param data bytes + */ + void writeAt(long index, int length, ByteBuffer data) throws IOException; + + /** + * Write *length* bytes in position *index* + * @param length length + * @param data bytes + * @return index + */ + long writeAtEnd(int length, ByteBuffer data) throws IOException; + + /** + * Close the file + */ + void close() throws IOException; +} diff --git a/src/main/java/org/warp/cowdb/IObjectsIO.java b/src/main/java/org/warp/cowdb/IObjectsIO.java new file mode 100644 index 0000000..ac0cb9c --- /dev/null +++ b/src/main/java/org/warp/cowdb/IObjectsIO.java @@ -0,0 +1,109 @@ +package org.warp.cowdb; + +import it.unimi.dsi.fastutil.longs.LongArrayList; +import org.warp.jcwdb.ann.DBDataType; + +import java.io.IOException; +import java.lang.reflect.Method; + +interface IObjectsIO { + T loadEnhancedObject(long reference, Class objectType) throws IOException; + + T loadObject(long reference) throws IOException; + + LongArrayList loadReferencesList(long reference) throws IOException; + + boolean loadBoolean(long reference) throws IOException; + + byte loadByte(long reference) throws IOException; + + short loadShort(long reference) throws IOException; + + char loadChar(long reference) throws IOException; + + int loadInt(long reference) throws IOException; + + long loadLong(long reference) throws IOException; + + void setEnhancedObject(long reference, T value) throws IOException; + + void setObject(long reference, T value) throws IOException; + + void setReferencesList(long reference, LongArrayList value) throws IOException; + + void setBoolean(long reference, boolean value) throws IOException; + + void setByte(long reference, byte value) throws IOException; + + void setShort(long reference, short value) throws IOException; + + void setChar(long reference, char value) throws IOException; + + void setInt(long reference, int value) throws IOException; + + void setLong(long reference, long value) throws IOException; + + long newNullObject() throws IOException; + + default long newEnhancedObject(T value) throws IOException { + long reference = newNullObject(); + if (value != null) { + setEnhancedObject(reference, value); + } + return reference; + } + + default long newObject(T value) throws IOException { + long reference = newNullObject(); + if (value != null) { + setObject(reference, value); + } + return reference; + } + + default long newReferencesList(LongArrayList value) throws IOException { + long reference = newNullObject(); + if (value != null) { + setReferencesList(reference, value); + } + return reference; + } + + default long newBoolean(boolean value) throws IOException { + long reference = newNullObject(); + setBoolean(reference, value); + return reference; + } + + default long newByte(byte value) throws IOException { + long reference = newNullObject(); + setByte(reference, value); + return reference; + } + + default long newShort(short value) throws IOException { + long reference = newNullObject(); + setShort(reference, value); + return reference; + } + + default long newChar(char value) throws IOException { + long reference = newNullObject(); + setChar(reference, value); + return reference; + } + + default long newInt(int value) throws IOException { + long reference = newNullObject(); + setInt(reference, value); + return reference; + } + + default long newLong(long value) throws IOException { + long reference = newNullObject(); + setLong(reference, value); + return reference; + } + + void loadProperty(EnhancedObject enhancedObject, int propertyId, Method propertyGetter, DBDataType propertyType, long propertyUID) throws IOException; +} diff --git a/src/main/java/org/warp/cowdb/IReferencesIO.java b/src/main/java/org/warp/cowdb/IReferencesIO.java new file mode 100644 index 0000000..d89d3e9 --- /dev/null +++ b/src/main/java/org/warp/cowdb/IReferencesIO.java @@ -0,0 +1,37 @@ +package org.warp.cowdb; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +public interface IReferencesIO { + /** + * Allocate a new empty reference + * @return the new reference + */ + long allocateReference() throws IOException; + + /** + * Allocate a new reference with that data + * @param size data size + * @param data bytes + * @return the new reference + */ + long allocateReference(int size, ByteBuffer data) throws IOException; + + /** + * Write some data to the reference + * @param reference reference + * @param size data size + * @param data bytes + */ + void writeToReference(long reference, int size, ByteBuffer data) throws IOException; + + /** + * Read data from the reference + * @param reference reference + * @return bytes + */ + ByteBuffer readFromReference(long reference) throws IOException; +} diff --git a/src/main/java/org/warp/cowdb/IReferencesMetadata.java b/src/main/java/org/warp/cowdb/IReferencesMetadata.java new file mode 100644 index 0000000..8b3d248 --- /dev/null +++ b/src/main/java/org/warp/cowdb/IReferencesMetadata.java @@ -0,0 +1,31 @@ +package org.warp.cowdb; + +import java.io.IOException; + +public interface IReferencesMetadata { + /** + * Get block of reference + * @param reference reference + * @return block id + */ + long getReference(long reference) throws IOException; + + /** + * Allocate a block for a new reference + * @param blockId block id + * @return reference + */ + long newReference(long blockId) throws IOException; + + /** + * Change reference size + * @param reference reference + * @param blockId block id + */ + void editReference(long reference, long blockId) throws IOException; + + /** + * Close file + */ + void close() throws IOException; +} diff --git a/src/main/java/org/warp/jcwdb/Cleaner.java b/src/main/java/org/warp/jcwdb/Cleaner.java index 321e40a..58d792e 100644 --- a/src/main/java/org/warp/jcwdb/Cleaner.java +++ b/src/main/java/org/warp/jcwdb/Cleaner.java @@ -1,11 +1,5 @@ package org.warp.jcwdb; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.nio.channels.ClosedChannelException; - -import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; - public class Cleaner { public static final boolean DISABLE_CLEANER = false; diff --git a/src/main/java/org/warp/jcwdb/FileAllocator.java b/src/main/java/org/warp/jcwdb/FileAllocator.java index 8809afd..afa9649 100644 --- a/src/main/java/org/warp/jcwdb/FileAllocator.java +++ b/src/main/java/org/warp/jcwdb/FileAllocator.java @@ -1,11 +1,10 @@ package org.warp.jcwdb; -import it.unimi.dsi.fastutil.longs.*; -import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator; +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.Collection; import java.util.Collections; import java.util.Map; import java.util.Optional; diff --git a/src/main/java/org/warp/jcwdb/FileIndexManager.java b/src/main/java/org/warp/jcwdb/FileIndexManager.java index 3f52372..4a089cd 100644 --- a/src/main/java/org/warp/jcwdb/FileIndexManager.java +++ b/src/main/java/org/warp/jcwdb/FileIndexManager.java @@ -7,6 +7,7 @@ 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; @@ -15,7 +16,7 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; public class FileIndexManager implements IndexManager { - public static final boolean ALWAYS_ALLOCATE_NEW = false; + public static final boolean ALWAYS_ALLOCATE_NEW = true; private final SeekableByteChannel dataFileChannel, metadataFileChannel; private volatile long metadataFileChannelSize; private final FileAllocator fileAllocator; @@ -114,7 +115,7 @@ public class FileIndexManager implements IndexManager { public T get(long index, DBReader reader) throws IOException { checkClosed(); IndexDetails details = getIndexMetadata(index); - Input i = new Input(Channels.newInputStream(dataFileChannel.position(details.getOffset()))); + Input i = new Input(Channels.newInputStream(dataFileChannel.position(details.getOffset())), details.getSize()); T result = reader.read(i, details.getSize()); return result; } @@ -184,8 +185,10 @@ public class FileIndexManager implements IndexManager { throw new IOException("Unable to write " + size + " in a space of " + indexDetails.getSize()); } final long offset = indexDetails.getOffset(); - final Output o = new Output(Channels.newOutputStream(dataFileChannel.position(offset)), size); + OutputStream os = Channels.newOutputStream(dataFileChannel.position(offset)); + final Output o = new Output(os, size); data.write(o); + os.flush(); o.flush(); } diff --git a/src/main/java/org/warp/jcwdb/IndexDetails.java b/src/main/java/org/warp/jcwdb/IndexDetails.java index 382eb49..d85aeb2 100644 --- a/src/main/java/org/warp/jcwdb/IndexDetails.java +++ b/src/main/java/org/warp/jcwdb/IndexDetails.java @@ -1,7 +1,5 @@ package org.warp.jcwdb; -import java.util.Objects; - public class IndexDetails { /** * The bitmask is used to determine if an index has been deleted diff --git a/src/main/java/org/warp/jcwdb/IndexManager.java b/src/main/java/org/warp/jcwdb/IndexManager.java index be8fc37..d5983b0 100644 --- a/src/main/java/org/warp/jcwdb/IndexManager.java +++ b/src/main/java/org/warp/jcwdb/IndexManager.java @@ -1,11 +1,6 @@ package org.warp.jcwdb; -import com.esotericsoftware.kryo.io.Output; - import java.io.IOException; -import java.util.function.BiConsumer; -import java.util.function.BiPredicate; -import java.util.function.Consumer; public interface IndexManager extends Cleanable { T get(long index, DBReader reader) throws IOException; diff --git a/src/main/java/org/warp/jcwdb/Saveable.java b/src/main/java/org/warp/jcwdb/Saveable.java index b8fcffb..b910fc5 100644 --- a/src/main/java/org/warp/jcwdb/Saveable.java +++ b/src/main/java/org/warp/jcwdb/Saveable.java @@ -1,7 +1,5 @@ package org.warp.jcwdb; -import java.io.IOException; - public interface Saveable { void save(); void saveAndFlush(); diff --git a/src/main/java/org/warp/jcwdb/ann/ConsumerWithIO.java b/src/main/java/org/warp/jcwdb/ann/ConsumerWithIO.java new file mode 100644 index 0000000..3bda55b --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/ConsumerWithIO.java @@ -0,0 +1,15 @@ +package org.warp.jcwdb.ann; + +import java.io.IOException; +import java.util.Objects; + +@FunctionalInterface +public interface ConsumerWithIO { + + void accept(T t) throws IOException; + + default ConsumerWithIO andThen(ConsumerWithIO after) { + Objects.requireNonNull(after); + return (T t) -> { accept(t); after.accept(t); }; + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/DBArrayList.java b/src/main/java/org/warp/jcwdb/ann/DBArrayList.java index 366024f..02907df 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBArrayList.java +++ b/src/main/java/org/warp/jcwdb/ann/DBArrayList.java @@ -13,13 +13,17 @@ public abstract class DBArrayList extends DBObject { @DBField(id = 0, type = DBDataType.UID_LIST) private LongArrayList indices; - public DBArrayList(JCWDatabase database) { - super(database); - indices = new LongArrayList(); + public DBArrayList() { + super(); } - public DBArrayList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); + public DBArrayList(JCWDatabase database) throws IOException { + super(database); + } + + @Override + public void initialize() { + indices = new LongArrayList(); } public T get(int index) { @@ -34,7 +38,7 @@ public abstract class DBArrayList extends DBObject { } public void add(T value) { - long uid = databaseManager.allocateNullValue(); + long uid = database.getDataLoader().allocateNullValue(); synchronized (indicesAccessLock) { indices.add(uid); try { @@ -52,7 +56,7 @@ public abstract class DBArrayList extends DBObject { } public void set(int index, T value) { - long uid = databaseManager.allocateNullValue(); + long uid = database.getDataLoader().allocateNullValue(); synchronized (indicesAccessLock) { indices.set(index, uid); try { @@ -64,7 +68,7 @@ public abstract class DBArrayList extends DBObject { } public void add(int index, T value) { - long uid = databaseManager.allocateNullValue(); + long uid = database.getDataLoader().allocateNullValue(); synchronized (indicesAccessLock) { indices.add(index, uid); try { diff --git a/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java b/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java index 71d7dac..a96af45 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java +++ b/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java @@ -7,22 +7,22 @@ public class DBDBObjectList extends DBArrayList { @DBField(id = 1, type = DBDataType.OBJECT) private Class type; - public DBDBObjectList(JCWDatabase database, Class type) { + public DBDBObjectList() { + super(); + } + + public DBDBObjectList(JCWDatabase database, Class type) throws IOException { super(database); this.type = type; } - public DBDBObjectList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); - } - @Override protected T loadItem(long uid) throws IOException { - return databaseManager.loadDBObject(type, uid); + return database.getDataLoader().loadDBObject(type, uid); } @Override protected void writeItemToDisk(long uid, T item) throws IOException { - databaseManager.writeObjectProperty(uid, DBDataType.DATABASE_OBJECT, item); + database.getDataLoader().writeObjectProperty(uid, DBDataType.DATABASE_OBJECT, item); } } diff --git a/src/main/java/org/warp/jcwdb/ann/DBDataType.java b/src/main/java/org/warp/jcwdb/ann/DBDataType.java index 9e8907b..24eabb0 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBDataType.java +++ b/src/main/java/org/warp/jcwdb/ann/DBDataType.java @@ -9,5 +9,6 @@ public enum DBDataType { CHAR, INTEGER, LONG, - UID_LIST + UID_LIST, + REFERENCES_LIST } \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/ann/DBObject.java b/src/main/java/org/warp/jcwdb/ann/DBObject.java index e70b17a..e68c9cf 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBObject.java +++ b/src/main/java/org/warp/jcwdb/ann/DBObject.java @@ -1,6 +1,6 @@ package org.warp.jcwdb.ann; -import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.commons.lang3.reflect.MethodUtils; import java.io.IOError; import java.io.IOException; @@ -10,8 +10,7 @@ import java.util.LinkedHashMap; import java.util.Map; public abstract class DBObject { - protected final JCWDatabase database; - protected final DatabaseManager databaseManager; + protected JCWDatabase database; private Field[] fields; private DBDataType[] fieldTypes; private long[] fieldUIDs; @@ -27,101 +26,17 @@ public abstract class DBObject { private final Object fieldsAccessLock = new Object(); private final Object propertiesAccessLock = new Object(); - public DBObject(JCWDatabase database) { + public DBObject() { + + } + + public DBObject(JCWDatabase database) throws IOException { this.database = database; - this.databaseManager = database.getDatabaseManager(); - try { - initializeDBObject(); - } catch (IOException e) { - throw new RuntimeException(e); - } + database.initializeDBObject(this); } - public DBObject(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - this.database = database; - this.databaseManager = database.getDatabaseManager(); - this.databaseManager.preloadDBObject(this, objectInfo); - } + public abstract void initialize() throws IOException; - private void initializeDBObject() throws IOException { - initializeDBObjectFields(); - initializeDBObjectProperties(); - } - - private void initializeDBObjectFields() throws IOException { - // Declare the variables needed to get the biggest field Id - Field[] unorderedFields = databaseManager.getFields(this); - // Find the biggest field Id - int biggestFieldId = databaseManager.getBiggestFieldId(unorderedFields); - - // Allocate new UIDs - fieldUIDs = databaseManager.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(); - databaseManager.loadField(this, field, fieldType, fieldUIDs[fieldId]); - fields[fieldId] = field; - orderedFieldTypes[fieldId] = fieldType; - } - // Set fields metadata - setFields(fields, orderedFieldTypes, fieldUIDs); - } - - private void initializeDBObjectProperties() { - // Declare the variables needed to get the biggest property Id - Method[] unorderedPropertyGetters = databaseManager.getPropertyGetters(this); - Method[] unorderedPropertySetters = databaseManager.getPropertySetters(this); - - // Find the biggest property Id - int biggestGetter = databaseManager.getBiggestPropertyGetterId(unorderedPropertyGetters); - int biggestSetter = databaseManager.getBiggestPropertySetterId(unorderedPropertySetters); - int biggestPropertyId = biggestGetter > biggestSetter ? biggestGetter : biggestSetter; - - // Allocate new UIDs - propertyUIDs = databaseManager.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 setterMethods = new LinkedHashMap<>(); - Map 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 - setProperties(propertyGetters, propertySetters, propertyTypes, propertyUIDs, setterMethods, getterMethods); - } public T getProperty() { StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); @@ -144,7 +59,7 @@ public abstract class DBObject { synchronized (propertiesAccessLock) { if (!loadedProperties[propertyId]) { long propertyUID = propertyUIDs[propertyId]; - databaseManager.loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID); + database.getDataLoader().loadProperty(this, propertyId, propertyGetters[propertyId], propertyTypes[propertyId], propertyUID); } return (T) loadedPropertyValues[propertyId]; } @@ -169,13 +84,13 @@ public abstract class DBObject { try { synchronized (propertiesAccessLock) { synchronized (fieldsAccessLock) { - databaseManager.writeObjectInfo(uid, fieldUIDs, propertyUIDs); + database.getDataLoader().writeObjectInfo(uid, fieldUIDs, propertyUIDs); } } synchronized (fieldsAccessLock) { for (int i = 0; i < fieldUIDs.length; i++) { try { - databaseManager.writeObjectProperty(fieldUIDs[i], fieldTypes[i], fields[i].get(this)); + database.getDataLoader().writeObjectProperty(fieldUIDs[i], fieldTypes[i], fields[i].get(this)); } catch (IllegalAccessException e) { throw new IOError(e); } @@ -183,7 +98,7 @@ public abstract class DBObject { } synchronized (propertiesAccessLock) { for (int i = 0; i < propertyUIDs.length; i++) { - databaseManager.writeObjectProperty(propertyUIDs[i], propertyTypes[i], loadedPropertyValues[i]); + database.getDataLoader().writeObjectProperty(propertyUIDs[i], propertyTypes[i], loadedPropertyValues[i]); } } } catch (IOException e) { @@ -211,4 +126,12 @@ public abstract class DBObject { this.getterMethods = getterMethods; } } + + Method[] getPropertyGetters() { + return MethodUtils.getMethodsWithAnnotation(this.getClass(), DBPropertyGetter.class); + } + + public Method[] getPropertySetters() { + return MethodUtils.getMethodsWithAnnotation(this.getClass(), DBPropertySetter.class); + } } diff --git a/src/main/java/org/warp/jcwdb/ann/DBObjectList.java b/src/main/java/org/warp/jcwdb/ann/DBObjectList.java index 44a41a0..0063fee 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBObjectList.java +++ b/src/main/java/org/warp/jcwdb/ann/DBObjectList.java @@ -3,21 +3,22 @@ package org.warp.jcwdb.ann; import java.io.IOException; public class DBObjectList extends DBArrayList { - public DBObjectList(JCWDatabase database) { - super(database); + + public DBObjectList() { + } - public DBObjectList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); + public DBObjectList(JCWDatabase database) throws IOException { + super(database); } @Override public T loadItem(long uid) throws IOException { - return databaseManager.loadObject(uid); + return database.getDataLoader().loadObject(uid); } @Override public void writeItemToDisk(long uid, T item) throws IOException { - databaseManager.writeObjectProperty(uid, DBDataType.OBJECT, item); + database.getDataLoader().writeObjectProperty(uid, DBDataType.OBJECT, item); } } diff --git a/src/main/java/org/warp/jcwdb/ann/DataInitializer.java b/src/main/java/org/warp/jcwdb/ann/DataInitializer.java new file mode 100644 index 0000000..d980a93 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DataInitializer.java @@ -0,0 +1,96 @@ +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 setterMethods = new LinkedHashMap<>(); + Map 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); + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/DataLoader.java b/src/main/java/org/warp/jcwdb/ann/DataLoader.java new file mode 100644 index 0000000..2acd136 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/DataLoader.java @@ -0,0 +1,645 @@ +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 loadRoot(Class rootType, SupplierWithIO ifAbsent) throws IOException { + synchronized (indicesAccessLock) { + if (isDBObjectNull(0)) { + return ifAbsent.getWithIO(); + } else { + return loadDBObject(rootType, 0); + } + } + } + + private T instantiateDBObject(Class 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 setterMethods = new LinkedHashMap<>(); + Map 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> returnType, ConsumerWithIO result) throws IOException { + synchronized (indicesAccessLock) { + switch (propertyType) { + case DATABASE_OBJECT: + DBObject fieldDBObjectValue = loadDBObject((Class) 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 loadDBObject(Class 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 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 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; + } + } +} diff --git a/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java b/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java index 84a0cb4..875b61f 100644 --- a/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java +++ b/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java @@ -1,50 +1,21 @@ 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.Cleanable; import org.warp.jcwdb.Cleaner; -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 DatabaseManager implements Cleanable { public static final long MAX_LOADED_INDICES = 100000; - private final DBObjectIndicesManager objectIndicesManager; - private final FileIndexManager indices; private final Cleaner cleaner; - private final JCWDatabase jcwDatabase; + private final DataLoader dataLoader; private DBObject loadedRootObject = null; - private final Kryo kryo = new Kryo(); private volatile boolean closed; - DatabaseManager(JCWDatabase jcwDatabase, Path dataFile, Path metadataFile, boolean registrationRequired) throws IOException { - this.jcwDatabase = jcwDatabase; - kryo.setRegistrationRequired(registrationRequired); - registerDefaultClasses(); - this.indices = new FileIndexManager(dataFile, metadataFile); - if (!indices.has(0)) { - allocateNullValue(); - } - this.objectIndicesManager = new DBObjectIndicesManager(this.indices); + DatabaseManager(DataLoader dataLoader) { + this.dataLoader = dataLoader; this.cleaner = new Cleaner(this); this.cleaner.start(); Runtime.getRuntime().addShutdownHook(new Thread(() -> { @@ -56,79 +27,13 @@ public class DatabaseManager implements Cleanable { })); } - 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++); - } - - @SuppressWarnings("unchecked") - public T loadRoot(Class rootType) throws IOException { + public T loadRoot(Class rootType, SupplierWithIO ifAbsent) throws IOException { if (loadedRootObject != null) { throw new RuntimeException("Root already set!"); } - if (isDBObjectNull(0)) { - try { - T root = rootType.getConstructor(JCWDatabase.class).newInstance(this.jcwDatabase); - loadedRootObject = root; - return root; - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new IOError(e); - } - } else { - T root = (T) loadDBObject(rootType, 0); - loadedRootObject = root; - return root; - } + T root = dataLoader.loadRoot(rootType, ifAbsent); + loadedRootObject = root; + return root; } public void close() throws IOException { @@ -138,367 +43,12 @@ public class DatabaseManager implements Cleanable { if (loadedRootObject != null) { loadedRootObject.writeToDisk(0); } - indices.close(); + dataLoader.close(); } } - public void preloadDBObject(DBObject obj, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - preloadDBObjectFields(obj, objectInfo.getFields()); - preloadDBObjectProperties(obj, objectInfo.getProperties()); - } - - private void preloadDBObjectFields(DBObject obj, long[] fieldUIDs) throws IOException { - // 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) { - // Declare the variables needed to get the biggest property Id - Method[] unorderedPropertyGetters = getPropertyGetters(obj); - Method[] unorderedPropertySetters = getPropertySetters(obj); - - // 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 setterMethods = new LinkedHashMap<>(); - Map 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); - } - - Method[] getPropertyGetters(DBObject obj) { - return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertyGetter.class); - } - - Method[] getPropertySetters(DBObject obj) { - return MethodUtils.getMethodsWithAnnotation(obj.getClass(), DBPropertySetter.class); - } - - protected Field[] getFields(DBObject obj) { - return FieldUtils.getFieldsWithAnnotation(obj.getClass(), DBField.class); - } - - int getBiggestPropertyGetterId(Method[] unorderedPropertyGetters) { - 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) { - 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) { - 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 { - loadData(propertyType, propertyUID, property::getReturnType, (data) -> obj.setLoadedProperty(propertyId, data)); - } - - public void loadField(DBObject obj, Field field, DBDataType fieldType, long fieldUID) throws IOException { - loadData(fieldType, fieldUID, field::getType, (data) -> { - try { - FieldUtils.writeField(field, obj, data, true); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - }); - } - - @SuppressWarnings("unchecked") - private void loadData(DBDataType propertyType, long dataUID, Supplier> returnType, Consumer result) throws IOException { - switch (propertyType) { - case DATABASE_OBJECT: - DBObject fieldDBObjectValue = loadDBObject((Class) 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 loadDBObject(Class type, long propertyUID) throws IOException { - try { - DBObjectIndicesManager.DBObjectInfo objectInfo = readUIDs(propertyUID); - if (objectInfo == null) return null; - return type.getDeclaredConstructor(JCWDatabase.class, DBObjectIndicesManager.DBObjectInfo.class).newInstance(jcwDatabase, objectInfo); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new IOException(e); - } - } - - private boolean isDBObjectNull(long uid) { - try { - return !objectIndicesManager.has(uid) || objectIndicesManager.get(uid) == null; - } catch (IOException ex) { - throw new IOError(ex); - } - } - - @SuppressWarnings("unchecked") - public T loadObject(long uid) throws IOException { - return indices.get(uid, (i, size) -> size == 0 ? null : (T) kryo.readClassAndObject(i)); - } - - private LongArrayList loadListObject(long uid) throws IOException { - return indices.get(uid, (i, size) -> { - if (size == 0) return null; - LongArrayList list = new LongArrayList(); - int listSize = i.readVarInt(true); - for (int li = 0; li < listSize; li++) { - list.add(i.readVarLong(true)); - } - return list; - }); - } - - public boolean loadBoolean(long uid) throws IOException { - return indices.get(uid, (i, size) -> size != 0 && i.readBoolean()); - } - - public byte loadByte(long uid) throws IOException { - return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readByte()); - } - - public short loadShort(long uid) throws IOException { - return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readShort()); - } - - public char loadChar(long uid) throws IOException { - return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readChar()); - } - - - public int loadInt(long uid) throws IOException { - return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readInt()); - } - - public long loadLong(long uid) throws IOException { - return indices.get(uid, (i, size) -> size == 0 ? 0 : i.readLong()); - } - - /** - * - * @param uid - * @return - */ - public DBObjectIndicesManager.DBObjectInfo readUIDs(long uid) { - try { - return objectIndicesManager.get(uid); - } catch (IOException e) { - throw new IOError(e); - } - } - - public boolean exists(long uid) { - return objectIndicesManager.has(uid); - } - - public void writeObjectInfo(long uid, long[] fieldUIDs, long[] propertyUIDs) throws IOException { - //System.err.println("Saving obj. " + uid); - this.objectIndicesManager.set(uid, fieldUIDs, propertyUIDs); - } - - private void writeObjectInfoNull(long uid) throws IOException { - this.objectIndicesManager.setNull(uid); - } - - public void writeObjectProperty(long uid, DBDataType propertyType, T loadedPropertyValue) throws IOException { - switch (propertyType) { - case BOOLEAN: - indices.set(uid, 1, (o) -> o.writeBoolean(loadedPropertyValue == null ? false : (boolean) loadedPropertyValue)); - //System.err.println("Saving data Boo " + uid + ":" + loadedPropertyValue); - break; - case BYTE: - indices.set(uid, Byte.BYTES, (o) -> o.writeByte(loadedPropertyValue == null ? 0 : (byte) loadedPropertyValue)); - //System.err.println("Saving data Byt " + uid + ":" + loadedPropertyValue); - break; - case SHORT: - indices.set(uid, Short.BYTES, (o) -> o.writeShort(loadedPropertyValue == null ? 0 : (short) loadedPropertyValue)); - //System.err.println("Saving data Shr " + uid + ":" + loadedPropertyValue); - break; - case CHAR: - indices.set(uid, Character.BYTES, (o) -> o.writeChar(loadedPropertyValue == null ? 0 : (char) loadedPropertyValue)); - //System.err.println("Saving data Chr " + uid + ":" + loadedPropertyValue); - break; - case INTEGER: - indices.set(uid, Integer.BYTES, (o) -> o.writeInt(loadedPropertyValue == null ? 0 : (int) loadedPropertyValue)); - //System.err.println("Saving data Int " + uid + ":" + loadedPropertyValue); - break; - case LONG: - indices.set(uid, Long.BYTES, (o) -> 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 -> 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 -> 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) { - kryo.register(clazz, 100 + id); - } - - public long allocateNullValue() { - return indices.add(0); - } - @Override public long clean() { return 0;//indices.clean(); } - - public long[] allocateNewUIDs(int quantity) { - long[] ids = new long[quantity]; - for (int i = 0; i < quantity; i++) { - ids[i] = allocateNullValue(); - } - return ids; - } } diff --git a/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java b/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java index 4e0854d..fbde022 100644 --- a/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java +++ b/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java @@ -1,35 +1,55 @@ 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.database = new DatabaseManager(this, dataFile, metadataFile, registrationRequired); + this.dataLoader = new DataLoader(this, dataFile, metadataFile, registrationRequired); + this.dataInitializer = new DataInitializer(dataLoader); + this.database = new DatabaseManager(dataLoader); } - public T loadRoot(Class rootClass) { - try { - return database.loadRoot(rootClass); - } catch (IOException e) { - throw new RuntimeException(e); - } + public T loadRoot(Class rootClass) throws IOException { + return loadRoot(rootClass, () -> { + try { + return rootClass.getConstructor(JCWDatabase.class).newInstance(this); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new IOException(e); + } + }); } - DatabaseManager getDatabaseManager() { - return database; + public T loadRoot(Class rootClass, SupplierWithIO ifAbsent) throws IOException { + return database.loadRoot(rootClass, ifAbsent); } public void registerClass(Class clazz, int id) { if (id < 0) { throw new IllegalArgumentException(); } - database.registerClass(clazz, id); + 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); + } } diff --git a/src/main/java/org/warp/jcwdb/ann/RunnableWithIO.java b/src/main/java/org/warp/jcwdb/ann/RunnableWithIO.java new file mode 100644 index 0000000..6f3215d --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/RunnableWithIO.java @@ -0,0 +1,19 @@ +package org.warp.jcwdb.ann; + +import java.io.IOException; + +@FunctionalInterface +public interface RunnableWithIO { + /** + * When an object implementing interface Runnable is used + * to create a thread, starting the thread causes the object's + * run method to be called in that separately executing + * thread. + *

+ * The general contract of the method run is that it may + * take any action whatsoever. + * + * @see java.lang.Thread#run() + */ + public abstract void run() throws IOException; +} diff --git a/src/main/java/org/warp/jcwdb/ann/SupplierWithIO.java b/src/main/java/org/warp/jcwdb/ann/SupplierWithIO.java new file mode 100644 index 0000000..2e8fa67 --- /dev/null +++ b/src/main/java/org/warp/jcwdb/ann/SupplierWithIO.java @@ -0,0 +1,8 @@ +package org.warp.jcwdb.ann; + +import java.io.IOException; + +@FunctionalInterface +public interface SupplierWithIO { + public T getWithIO() throws IOException; +} \ No newline at end of file diff --git a/src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java b/src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java index 60eaaa3..f362f50 100644 --- a/src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java +++ b/src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java @@ -20,6 +20,7 @@ public class DBDBObjectListTests { 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); @@ -37,7 +38,7 @@ public class DBDBObjectListTests { } @Test - public void shouldMatchList() { + public void shouldMatchList() throws IOException { checkEmptyList(); assertEquals(200, root.list.size()); for (int i = 0; i < 200; i++) { @@ -45,7 +46,7 @@ public class DBDBObjectListTests { } } - private void checkEmptyList() { + private void checkEmptyList() throws IOException { DBObjectList list = new DBObjectList<>(db.get()); assertEquals(null, list.getLast()); assertEquals(0, list.size()); @@ -63,15 +64,20 @@ public class DBDBObjectListTests { public static class RootWithList extends DBObject { - public RootWithList(JCWDatabase database) { + @DBField(id = 0, type = DBDataType.DATABASE_OBJECT) + public DBDBObjectList list; + + public RootWithList() { + super(); + } + + public RootWithList(JCWDatabase database) throws IOException { super(database); } - public RootWithList(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); + @Override + public void initialize() throws IOException { + list = new DBDBObjectList<>(database, TestUtils.RootClass.class); } - - @DBField(id = 0, type = DBDataType.DATABASE_OBJECT) - public DBDBObjectList list; } } diff --git a/src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java b/src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java index c0e0aa1..dc378fe 100644 --- a/src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java +++ b/src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java @@ -43,20 +43,20 @@ public class DBMultipleDBObjects { public static class RootTwoClasses extends DBObject { - public RootTwoClasses(JCWDatabase database) { - super(database); - } - - public RootTwoClasses(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); - } - @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(); @@ -76,5 +76,10 @@ public class DBMultipleDBObjects { public void setClass4(TestUtils.RootClass value) { setProperty(value); } + + @Override + public void initialize() throws IOException { + + } } } diff --git a/src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java b/src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java index 73160df..5ad71a9 100644 --- a/src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java +++ b/src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java @@ -3,12 +3,9 @@ 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.NestedClass; import org.warp.jcwdb.utils.TestUtils; -import java.io.IOException; - import static org.junit.Assert.*; public class DBNestedDBObjects { diff --git a/src/test/java/org/warp/jcwdb/tests/DBRootCreation.java b/src/test/java/org/warp/jcwdb/tests/DBRootCreation.java index ff962d0..4b2523b 100644 --- a/src/test/java/org/warp/jcwdb/tests/DBRootCreation.java +++ b/src/test/java/org/warp/jcwdb/tests/DBRootCreation.java @@ -10,7 +10,7 @@ import org.warp.jcwdb.utils.TestUtils; import java.io.IOException; -import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; public class DBRootCreation { private TestUtils.WrappedDb db; @@ -21,7 +21,7 @@ public class DBRootCreation { } @Test - public void shouldCreateRoot() { + public void shouldCreateRoot() throws IOException { RootClass root = db.get().loadRoot(RootClass.class); assertTrue(root.test()); } @@ -33,16 +33,17 @@ public class DBRootCreation { public static class RootClass extends DBObject { - public RootClass(JCWDatabase database) { + public RootClass(JCWDatabase database) throws IOException { super(database); } - public RootClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); - } - public boolean test() { return true; } + + @Override + public void initialize() throws IOException { + + } } } diff --git a/src/test/java/org/warp/jcwdb/tests/NDBMultipleEnhancedObjects.java b/src/test/java/org/warp/jcwdb/tests/NDBMultipleEnhancedObjects.java new file mode 100644 index 0000000..82abb62 --- /dev/null +++ b/src/test/java/org/warp/jcwdb/tests/NDBMultipleEnhancedObjects.java @@ -0,0 +1,90 @@ +package org.warp.jcwdb.tests; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.warp.cowdb.EnhancedObject; +import org.warp.cowdb.IDatabase; +import org.warp.jcwdb.ann.DBDataType; +import org.warp.jcwdb.ann.DBField; +import org.warp.jcwdb.ann.DBPropertyGetter; +import org.warp.jcwdb.ann.DBPropertySetter; +import org.warp.jcwdb.utils.NTestUtils; + +import java.io.IOException; + +public class NDBMultipleEnhancedObjects { + private NTestUtils.WrappedDb db; + private RootTwoClasses root; + + @Before + public void setUp() throws Exception { + db = NTestUtils.wrapDb().create((db) -> { + root = db.get().loadRoot(RootTwoClasses.class); + }); + root.class1 = new NTestUtils.RootClass(db.get()); + db.setRootClassValues(root.class1); + root.class2 = new NTestUtils.RootClass(db.get()); + db.setRootClassValues(root.class2); + root.setClass3(new NTestUtils.RootClass(db.get())); + db.setRootClassValues(root.getClass3()); + root.setClass4(new NTestUtils.RootClass(db.get())); + db.setRootClassValues(root.getClass4()); + db.closeAndReopen(); + } + + @Test + public void shouldMatchMultipleEnhancedObjects() { + 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 EnhancedObject { + + @DBField(id = 0, type = DBDataType.DATABASE_OBJECT) + public NTestUtils.RootClass class1; + + @DBField(id = 1, type = DBDataType.DATABASE_OBJECT) + public NTestUtils.RootClass class2; + + public RootTwoClasses() { + super(); + } + + public RootTwoClasses(IDatabase database) throws IOException { + super(database); + } + + @DBPropertyGetter(id = 0, type = DBDataType.DATABASE_OBJECT) + public NTestUtils.RootClass getClass3() { + return getProperty(); + } + + @DBPropertySetter(id = 0, type = DBDataType.DATABASE_OBJECT) + public void setClass3(NTestUtils.RootClass value) { + setProperty(value); + } + + @DBPropertyGetter(id = 1, type = DBDataType.DATABASE_OBJECT) + public NTestUtils.RootClass getClass4() { + return getProperty(); + } + + @DBPropertySetter(id = 1, type = DBDataType.DATABASE_OBJECT) + public void setClass4(NTestUtils.RootClass value) { + setProperty(value); + } + + @Override + public void initialize() throws IOException { + + } + } +} diff --git a/src/test/java/org/warp/jcwdb/utils/ConsumerWithIO.java b/src/test/java/org/warp/jcwdb/utils/ConsumerWithIO.java new file mode 100644 index 0000000..0b85738 --- /dev/null +++ b/src/test/java/org/warp/jcwdb/utils/ConsumerWithIO.java @@ -0,0 +1,32 @@ +package org.warp.jcwdb.utils; + +import java.io.IOException; +import java.util.Objects; + +@FunctionalInterface +public interface ConsumerWithIO { + + /** + * Performs this operation on the given argument. + * + * @param t the input argument + */ + void accept(T t) throws IOException; + + /** + * Returns a composed {@code Consumer} that performs, in sequence, this + * operation followed by the {@code after} operation. If performing either + * operation throws an exception, it is relayed to the caller of the + * composed operation. If performing this operation throws an exception, + * the {@code after} operation will not be performed. + * + * @param after the operation to perform after this operation + * @return a composed {@code Consumer} that performs in sequence this + * operation followed by the {@code after} operation + * @throws NullPointerException if {@code after} is null + */ + default ConsumerWithIO andThen(ConsumerWithIO after) { + Objects.requireNonNull(after); + return (T t) -> { accept(t); after.accept(t); }; + } +} diff --git a/src/test/java/org/warp/jcwdb/utils/NSimplestClass.java b/src/test/java/org/warp/jcwdb/utils/NSimplestClass.java new file mode 100644 index 0000000..55f76d6 --- /dev/null +++ b/src/test/java/org/warp/jcwdb/utils/NSimplestClass.java @@ -0,0 +1,29 @@ +package org.warp.jcwdb.utils; + +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; + +public class NSimplestClass extends EnhancedObject { + + @DBField(id = 0, type = DBDataType.BOOLEAN) + public boolean field1; + + public NSimplestClass() { + + } + + public NSimplestClass(Database database) throws IOException { + super(database); + } + + @Override + public void initialize() throws IOException { + field1 = true; + } +} diff --git a/src/test/java/org/warp/jcwdb/utils/NTestUtils.java b/src/test/java/org/warp/jcwdb/utils/NTestUtils.java new file mode 100644 index 0000000..ffbae03 --- /dev/null +++ b/src/test/java/org/warp/jcwdb/utils/NTestUtils.java @@ -0,0 +1,383 @@ +package org.warp.jcwdb.utils; + +import it.unimi.dsi.fastutil.longs.LongArrayList; +import org.warp.cowdb.Database; +import org.warp.cowdb.EnhancedObject; +import org.warp.cowdb.IDatabase; +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 static org.junit.Assert.*; + +public class NTestUtils { + public static WrappedDb wrapDb() { + return new WrappedDb(); + } + + public static class WrappedDb { + + private Database 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 r) throws IOException { + this.r = () -> r.accept(WrappedDb.this); + this.create(); + return this; + } + + private Database openDatabase() throws IOException { + return new Database(tempDir.resolve(Paths.get("data.db")), tempDir.resolve(Paths.get("blocks.dat")), tempDir.resolve(Paths.get("references.dat"))); + } + + public void delete() throws IOException { + db.close(); + deleteDir(tempDir); + } + + public Database 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 NSimplestClass(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); + NSimplestClass simplestClass9 = new NSimplestClass(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 EnhancedObject { + + @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.REFERENCES_LIST) + public LongArrayList field8; + + @DBField(id = 8, type = DBDataType.DATABASE_OBJECT) + public NSimplestClass field9; + + public RootClass() { + + } + + public RootClass(IDatabase 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.REFERENCES_LIST) + public LongArrayList get8() { + return getProperty(); + } + + @DBPropertyGetter(id = 8, type = DBDataType.DATABASE_OBJECT) + public NSimplestClass 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.REFERENCES_LIST) + public void set8(LongArrayList val) { + setProperty(val); + } + + @DBPropertySetter(id = 8, type = DBDataType.DATABASE_OBJECT) + public void set9(NSimplestClass val) { + setProperty(val); + } + + public boolean test() { + return true; + } + + @Override + public void initialize() throws IOException { + + } + } +} diff --git a/src/test/java/org/warp/jcwdb/utils/NestedClass.java b/src/test/java/org/warp/jcwdb/utils/NestedClass.java index 7a53bb3..042cbd4 100644 --- a/src/test/java/org/warp/jcwdb/utils/NestedClass.java +++ b/src/test/java/org/warp/jcwdb/utils/NestedClass.java @@ -12,6 +12,14 @@ public class NestedClass extends DBObject { @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); @@ -22,11 +30,8 @@ public class NestedClass extends DBObject { return getProperty(); } - public NestedClass(JCWDatabase database) { - super(database); - } + @Override + public void initialize() throws IOException { - public NestedClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); } } diff --git a/src/test/java/org/warp/jcwdb/utils/SimplestClass.java b/src/test/java/org/warp/jcwdb/utils/SimplestClass.java index e73e55d..2791395 100644 --- a/src/test/java/org/warp/jcwdb/utils/SimplestClass.java +++ b/src/test/java/org/warp/jcwdb/utils/SimplestClass.java @@ -9,12 +9,16 @@ public class SimplestClass extends DBObject { @DBField(id = 0, type = DBDataType.BOOLEAN) public boolean field1; - public SimplestClass(JCWDatabase database) { - super(database); - field1 = true; + public SimplestClass() { + } - public SimplestClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); + public SimplestClass(JCWDatabase database) throws IOException { + super(database); + } + + @Override + public void initialize() throws IOException { + field1 = true; } } diff --git a/src/test/java/org/warp/jcwdb/utils/TestUtils.java b/src/test/java/org/warp/jcwdb/utils/TestUtils.java index 5f8c632..ed622e0 100644 --- a/src/test/java/org/warp/jcwdb/utils/TestUtils.java +++ b/src/test/java/org/warp/jcwdb/utils/TestUtils.java @@ -12,7 +12,6 @@ import java.util.Comparator; import java.util.function.Consumer; import static org.junit.Assert.*; -import static org.junit.Assert.assertEquals; public class TestUtils { public static WrappedDb wrapDb() { @@ -23,7 +22,7 @@ public class TestUtils { private JCWDatabase db; private Path tempDir; - private Runnable r; + private RunnableWithIO r; private WrappedDb() { @@ -38,7 +37,7 @@ public class TestUtils { return this; } - public WrappedDb create(Consumer r) throws IOException { + public WrappedDb create(ConsumerWithIO r) throws IOException { this.r = () -> r.accept(WrappedDb.this); this.create(); return this; @@ -70,12 +69,12 @@ public class TestUtils { r.run(); } - public void setRootClassValues(RootClass root) { + public void setRootClassValues(RootClass root) throws IOException { setRootClassFields(root); setRootClassProperties(root); } - public void setRootClassFields(RootClass root) { + public void setRootClassFields(RootClass root) throws IOException { root.field1 = true; root.field2 = 2; root.field3 = 3; @@ -96,7 +95,7 @@ public class TestUtils { } - public void setRootClassProperties(RootClass root) { + public void setRootClassProperties(RootClass root) throws IOException { root.set1(true); root.set2((byte)2); root.set3((short)3); @@ -238,7 +237,7 @@ public class TestUtils { assertEquals(val.getLong(6), Long.MIN_VALUE); } - public void onLoad(Runnable r) { + public void onLoad(RunnableWithIO r) { this.r = r; } } @@ -272,6 +271,14 @@ public class TestUtils { @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(); @@ -362,16 +369,13 @@ public class TestUtils { setProperty(val); } - public RootClass(JCWDatabase database) { - super(database); - } - - public RootClass(JCWDatabase database, DBObjectIndicesManager.DBObjectInfo objectInfo) throws IOException { - super(database, objectInfo); - } - public boolean test() { return true; } + + @Override + public void initialize() throws IOException { + + } } } From b06ee07ad24798bd3413fcbb86690fcda05dc31d Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Fri, 1 Feb 2019 00:04:51 +0100 Subject: [PATCH 5/5] Added CowList --- src/main/java/org/warp/cowdb/Database.java | 81 ++- .../warp/cowdb/EnhancedObjectFullInfo.java | 12 +- src/main/java/org/warp/cowdb/IObjectsIO.java | 4 +- .../lists/CowList.java} | 65 +- .../cowdb/lists/EnhancedObjectCowList.java | 33 + .../org/warp/cowdb/lists/ObjectCowList.java | 29 + src/main/java/org/warp/jcwdb/Castable.java | 5 - src/main/java/org/warp/jcwdb/Cleanable.java | 9 - src/main/java/org/warp/jcwdb/Cleaner.java | 116 ---- src/main/java/org/warp/jcwdb/DBReader.java | 7 - src/main/java/org/warp/jcwdb/DBWriter.java | 7 - .../java/org/warp/jcwdb/FileAllocator.java | 151 ---- .../java/org/warp/jcwdb/FileIndexManager.java | 495 -------------- .../java/org/warp/jcwdb/FullIndexDetails.java | 14 - .../java/org/warp/jcwdb/IndexDetails.java | 62 -- .../java/org/warp/jcwdb/IndexManager.java | 14 - .../warp/jcwdb/NoParserFoundException.java | 14 - src/main/java/org/warp/jcwdb/Saveable.java | 6 - .../org/warp/jcwdb/ann/DBDBObjectList.java | 28 - .../java/org/warp/jcwdb/ann/DBDataType.java | 1 - .../java/org/warp/jcwdb/ann/DBObject.java | 137 ---- .../jcwdb/ann/DBObjectIndicesManager.java | 87 --- .../java/org/warp/jcwdb/ann/DBObjectList.java | 24 - .../org/warp/jcwdb/ann/DataInitializer.java | 96 --- .../java/org/warp/jcwdb/ann/DataLoader.java | 645 ------------------ .../org/warp/jcwdb/ann/DatabaseManager.java | 54 -- .../java/org/warp/jcwdb/ann/JCWDatabase.java | 55 -- .../warp/jcwdb/tests/DBDBObjectListTests.java | 83 --- .../warp/jcwdb/tests/DBMultipleDBObjects.java | 85 --- .../warp/jcwdb/tests/DBNestedDBObjects.java | 66 -- .../org/warp/jcwdb/tests/DBRootCreation.java | 49 -- .../org/warp/jcwdb/tests/DBRootFields.java | 30 - .../warp/jcwdb/tests/DBRootProperties.java | 30 - .../org/warp/jcwdb/utils/NSimplestClass.java | 2 - .../java/org/warp/jcwdb/utils/NTestUtils.java | 2 +- .../org/warp/jcwdb/utils/NestedClass.java | 37 - .../org/warp/jcwdb/utils/SimplestClass.java | 24 - .../java/org/warp/jcwdb/utils/TestUtils.java | 381 ----------- 38 files changed, 172 insertions(+), 2868 deletions(-) rename src/main/java/org/warp/{jcwdb/ann/DBArrayList.java => cowdb/lists/CowList.java} (50%) create mode 100644 src/main/java/org/warp/cowdb/lists/EnhancedObjectCowList.java create mode 100644 src/main/java/org/warp/cowdb/lists/ObjectCowList.java delete mode 100644 src/main/java/org/warp/jcwdb/Castable.java delete mode 100644 src/main/java/org/warp/jcwdb/Cleanable.java delete mode 100644 src/main/java/org/warp/jcwdb/Cleaner.java delete mode 100644 src/main/java/org/warp/jcwdb/DBReader.java delete mode 100644 src/main/java/org/warp/jcwdb/DBWriter.java delete mode 100644 src/main/java/org/warp/jcwdb/FileAllocator.java delete mode 100644 src/main/java/org/warp/jcwdb/FileIndexManager.java delete mode 100644 src/main/java/org/warp/jcwdb/FullIndexDetails.java delete mode 100644 src/main/java/org/warp/jcwdb/IndexDetails.java delete mode 100644 src/main/java/org/warp/jcwdb/IndexManager.java delete mode 100644 src/main/java/org/warp/jcwdb/NoParserFoundException.java delete mode 100644 src/main/java/org/warp/jcwdb/Saveable.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/DBObject.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/DBObjectList.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/DataInitializer.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/DataLoader.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/DatabaseManager.java delete mode 100644 src/main/java/org/warp/jcwdb/ann/JCWDatabase.java delete mode 100644 src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java delete mode 100644 src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java delete mode 100644 src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java delete mode 100644 src/test/java/org/warp/jcwdb/tests/DBRootCreation.java delete mode 100644 src/test/java/org/warp/jcwdb/tests/DBRootFields.java delete mode 100644 src/test/java/org/warp/jcwdb/tests/DBRootProperties.java delete mode 100644 src/test/java/org/warp/jcwdb/utils/NestedClass.java delete mode 100644 src/test/java/org/warp/jcwdb/utils/SimplestClass.java delete mode 100644 src/test/java/org/warp/jcwdb/utils/TestUtils.java diff --git a/src/main/java/org/warp/cowdb/Database.java b/src/main/java/org/warp/cowdb/Database.java index 97ba2dc..795607b 100644 --- a/src/main/java/org/warp/cowdb/Database.java +++ b/src/main/java/org/warp/cowdb/Database.java @@ -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 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); diff --git a/src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java b/src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java index 8cd28ea..6a1ce2c 100644 --- a/src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java +++ b/src/main/java/org/warp/cowdb/EnhancedObjectFullInfo.java @@ -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; } } diff --git a/src/main/java/org/warp/cowdb/IObjectsIO.java b/src/main/java/org/warp/cowdb/IObjectsIO.java index ac0cb9c..39212a6 100644 --- a/src/main/java/org/warp/cowdb/IObjectsIO.java +++ b/src/main/java/org/warp/cowdb/IObjectsIO.java @@ -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 loadEnhancedObject(long reference, Class objectType) throws IOException; 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); } diff --git a/src/main/java/org/warp/jcwdb/ann/DBArrayList.java b/src/main/java/org/warp/cowdb/lists/CowList.java similarity index 50% rename from src/main/java/org/warp/jcwdb/ann/DBArrayList.java rename to src/main/java/org/warp/cowdb/lists/CowList.java index 02907df..fcba1d4 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBArrayList.java +++ b/src/main/java/org/warp/cowdb/lists/CowList.java @@ -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 extends DBObject { +public abstract class CowList 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); - } + long uid = indices.getLong(index); + return loadItem(uid); } } - 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); - } + writeItemToDisk(uid, value); } } - 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); - } + writeItemToDisk(uid, value); } } - 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); - } + writeItemToDisk(uid, value); } } - 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 extends DBObject { @Override public String toString() { - return new StringJoiner(", ", DBArrayList.class.getSimpleName() + "[", "]") + return new StringJoiner(", ", CowList.class.getSimpleName() + "[", "]") .add(indices.size() + " items") .toString(); } diff --git a/src/main/java/org/warp/cowdb/lists/EnhancedObjectCowList.java b/src/main/java/org/warp/cowdb/lists/EnhancedObjectCowList.java new file mode 100644 index 0000000..e35f22f --- /dev/null +++ b/src/main/java/org/warp/cowdb/lists/EnhancedObjectCowList.java @@ -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 extends CowList { + + @DBField(id = 1, type = DBDataType.OBJECT) + private Class type; + + public EnhancedObjectCowList() { + super(); + } + + public EnhancedObjectCowList(IDatabase database, Class 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); + } +} diff --git a/src/main/java/org/warp/cowdb/lists/ObjectCowList.java b/src/main/java/org/warp/cowdb/lists/ObjectCowList.java new file mode 100644 index 0000000..8902c27 --- /dev/null +++ b/src/main/java/org/warp/cowdb/lists/ObjectCowList.java @@ -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 extends CowList { + + 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); + } +} diff --git a/src/main/java/org/warp/jcwdb/Castable.java b/src/main/java/org/warp/jcwdb/Castable.java deleted file mode 100644 index 23e04ed..0000000 --- a/src/main/java/org/warp/jcwdb/Castable.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.warp.jcwdb; - -public interface Castable { - T cast(); -} diff --git a/src/main/java/org/warp/jcwdb/Cleanable.java b/src/main/java/org/warp/jcwdb/Cleanable.java deleted file mode 100644 index 57af24c..0000000 --- a/src/main/java/org/warp/jcwdb/Cleanable.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.warp.jcwdb; - -public interface Cleanable { - /** - * Clean the object - * @return the approximated number of cleaned items - */ - public long clean(); -} diff --git a/src/main/java/org/warp/jcwdb/Cleaner.java b/src/main/java/org/warp/jcwdb/Cleaner.java deleted file mode 100644 index 58d792e..0000000 --- a/src/main/java/org/warp/jcwdb/Cleaner.java +++ /dev/null @@ -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); - } - } - - } -} diff --git a/src/main/java/org/warp/jcwdb/DBReader.java b/src/main/java/org/warp/jcwdb/DBReader.java deleted file mode 100644 index dda81b3..0000000 --- a/src/main/java/org/warp/jcwdb/DBReader.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.warp.jcwdb; - -import com.esotericsoftware.kryo.io.Input; - -public interface DBReader { - T read(Input i, int size); -} diff --git a/src/main/java/org/warp/jcwdb/DBWriter.java b/src/main/java/org/warp/jcwdb/DBWriter.java deleted file mode 100644 index 4e9275e..0000000 --- a/src/main/java/org/warp/jcwdb/DBWriter.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.warp.jcwdb; - -import com.esotericsoftware.kryo.io.Output; - -public interface DBWriter { - void write(Output o); -} diff --git a/src/main/java/org/warp/jcwdb/FileAllocator.java b/src/main/java/org/warp/jcwdb/FileAllocator.java deleted file mode 100644 index afa9649..0000000 --- a/src/main/java/org/warp/jcwdb/FileAllocator.java +++ /dev/null @@ -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> sorted = - freeBytes.entrySet().stream() - .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())); - final VariableWrapper holeOffset = new VariableWrapper<>(-1L); - final VariableWrapper 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> sorted = - freeBytes.entrySet().stream() - .sorted(Map.Entry.comparingByValue()); - Optional> first = sorted.findFirst(); - if (first.isPresent()) { - freeBytes.remove(first.get().getKey()); - } - } - } - - - private void checkClosed() { - if (closed) { - throw new RuntimeException("Index Manager is closed."); - } - } -} diff --git a/src/main/java/org/warp/jcwdb/FileIndexManager.java b/src/main/java/org/warp/jcwdb/FileIndexManager.java deleted file mode 100644 index 4a089cd..0000000 --- a/src/main/java/org/warp/jcwdb/FileIndexManager.java +++ /dev/null @@ -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 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 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 get(long index, DBReader 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; - } -} diff --git a/src/main/java/org/warp/jcwdb/FullIndexDetails.java b/src/main/java/org/warp/jcwdb/FullIndexDetails.java deleted file mode 100644 index 35cc35e..0000000 --- a/src/main/java/org/warp/jcwdb/FullIndexDetails.java +++ /dev/null @@ -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; - } -} diff --git a/src/main/java/org/warp/jcwdb/IndexDetails.java b/src/main/java/org/warp/jcwdb/IndexDetails.java deleted file mode 100644 index d85aeb2..0000000 --- a/src/main/java/org/warp/jcwdb/IndexDetails.java +++ /dev/null @@ -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 + "]"; - } - - -} diff --git a/src/main/java/org/warp/jcwdb/IndexManager.java b/src/main/java/org/warp/jcwdb/IndexManager.java deleted file mode 100644 index d5983b0..0000000 --- a/src/main/java/org/warp/jcwdb/IndexManager.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.warp.jcwdb; - -import java.io.IOException; - -public interface IndexManager extends Cleanable { - T get(long index, DBReader 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; -} diff --git a/src/main/java/org/warp/jcwdb/NoParserFoundException.java b/src/main/java/org/warp/jcwdb/NoParserFoundException.java deleted file mode 100644 index 087270d..0000000 --- a/src/main/java/org/warp/jcwdb/NoParserFoundException.java +++ /dev/null @@ -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; - -} diff --git a/src/main/java/org/warp/jcwdb/Saveable.java b/src/main/java/org/warp/jcwdb/Saveable.java deleted file mode 100644 index b910fc5..0000000 --- a/src/main/java/org/warp/jcwdb/Saveable.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.warp.jcwdb; - -public interface Saveable { - void save(); - void saveAndFlush(); -} diff --git a/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java b/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java deleted file mode 100644 index a96af45..0000000 --- a/src/main/java/org/warp/jcwdb/ann/DBDBObjectList.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.warp.jcwdb.ann; - -import java.io.IOException; - -public class DBDBObjectList extends DBArrayList { - - @DBField(id = 1, type = DBDataType.OBJECT) - private Class type; - - public DBDBObjectList() { - super(); - } - - public DBDBObjectList(JCWDatabase database, Class 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); - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/DBDataType.java b/src/main/java/org/warp/jcwdb/ann/DBDataType.java index 24eabb0..9166a39 100644 --- a/src/main/java/org/warp/jcwdb/ann/DBDataType.java +++ b/src/main/java/org/warp/jcwdb/ann/DBDataType.java @@ -9,6 +9,5 @@ public enum DBDataType { CHAR, INTEGER, LONG, - UID_LIST, REFERENCES_LIST } \ No newline at end of file diff --git a/src/main/java/org/warp/jcwdb/ann/DBObject.java b/src/main/java/org/warp/jcwdb/ann/DBObject.java deleted file mode 100644 index e68c9cf..0000000 --- a/src/main/java/org/warp/jcwdb/ann/DBObject.java +++ /dev/null @@ -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 setterMethods; - private Map 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 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); - } - } - - void setLoadedProperty(int propertyId, T value) { - loadedPropertyValues[propertyId] = value; - loadedProperties[propertyId] = true; - } - - @SuppressWarnings("unchecked") - private 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 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 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 setterMethods, Map 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); - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java b/src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java deleted file mode 100644 index 4005946..0000000 --- a/src/main/java/org/warp/jcwdb/ann/DBObjectIndicesManager.java +++ /dev/null @@ -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; - } - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/DBObjectList.java b/src/main/java/org/warp/jcwdb/ann/DBObjectList.java deleted file mode 100644 index 0063fee..0000000 --- a/src/main/java/org/warp/jcwdb/ann/DBObjectList.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.warp.jcwdb.ann; - -import java.io.IOException; - -public class DBObjectList extends DBArrayList { - - 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); - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/DataInitializer.java b/src/main/java/org/warp/jcwdb/ann/DataInitializer.java deleted file mode 100644 index d980a93..0000000 --- a/src/main/java/org/warp/jcwdb/ann/DataInitializer.java +++ /dev/null @@ -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 setterMethods = new LinkedHashMap<>(); - Map 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); - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/DataLoader.java b/src/main/java/org/warp/jcwdb/ann/DataLoader.java deleted file mode 100644 index 2acd136..0000000 --- a/src/main/java/org/warp/jcwdb/ann/DataLoader.java +++ /dev/null @@ -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 loadRoot(Class rootType, SupplierWithIO ifAbsent) throws IOException { - synchronized (indicesAccessLock) { - if (isDBObjectNull(0)) { - return ifAbsent.getWithIO(); - } else { - return loadDBObject(rootType, 0); - } - } - } - - private T instantiateDBObject(Class 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 setterMethods = new LinkedHashMap<>(); - Map 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> returnType, ConsumerWithIO result) throws IOException { - synchronized (indicesAccessLock) { - switch (propertyType) { - case DATABASE_OBJECT: - DBObject fieldDBObjectValue = loadDBObject((Class) 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 loadDBObject(Class 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 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 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; - } - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java b/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java deleted file mode 100644 index 875b61f..0000000 --- a/src/main/java/org/warp/jcwdb/ann/DatabaseManager.java +++ /dev/null @@ -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 loadRoot(Class rootType, SupplierWithIO 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(); - } -} diff --git a/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java b/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java deleted file mode 100644 index fbde022..0000000 --- a/src/main/java/org/warp/jcwdb/ann/JCWDatabase.java +++ /dev/null @@ -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 loadRoot(Class 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 loadRoot(Class rootClass, SupplierWithIO 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); - } -} diff --git a/src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java b/src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java deleted file mode 100644 index f362f50..0000000 --- a/src/test/java/org/warp/jcwdb/tests/DBDBObjectListTests.java +++ /dev/null @@ -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 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 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); - } - } -} diff --git a/src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java b/src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java deleted file mode 100644 index dc378fe..0000000 --- a/src/test/java/org/warp/jcwdb/tests/DBMultipleDBObjects.java +++ /dev/null @@ -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 { - - } - } -} diff --git a/src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java b/src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java deleted file mode 100644 index 5ad71a9..0000000 --- a/src/test/java/org/warp/jcwdb/tests/DBNestedDBObjects.java +++ /dev/null @@ -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(); - } -} diff --git a/src/test/java/org/warp/jcwdb/tests/DBRootCreation.java b/src/test/java/org/warp/jcwdb/tests/DBRootCreation.java deleted file mode 100644 index 4b2523b..0000000 --- a/src/test/java/org/warp/jcwdb/tests/DBRootCreation.java +++ /dev/null @@ -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 { - - } - } -} diff --git a/src/test/java/org/warp/jcwdb/tests/DBRootFields.java b/src/test/java/org/warp/jcwdb/tests/DBRootFields.java deleted file mode 100644 index 0073e08..0000000 --- a/src/test/java/org/warp/jcwdb/tests/DBRootFields.java +++ /dev/null @@ -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(); - } -} diff --git a/src/test/java/org/warp/jcwdb/tests/DBRootProperties.java b/src/test/java/org/warp/jcwdb/tests/DBRootProperties.java deleted file mode 100644 index a144ecd..0000000 --- a/src/test/java/org/warp/jcwdb/tests/DBRootProperties.java +++ /dev/null @@ -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(); - } -} diff --git a/src/test/java/org/warp/jcwdb/utils/NSimplestClass.java b/src/test/java/org/warp/jcwdb/utils/NSimplestClass.java index 55f76d6..f73ba00 100644 --- a/src/test/java/org/warp/jcwdb/utils/NSimplestClass.java +++ b/src/test/java/org/warp/jcwdb/utils/NSimplestClass.java @@ -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; diff --git a/src/test/java/org/warp/jcwdb/utils/NTestUtils.java b/src/test/java/org/warp/jcwdb/utils/NTestUtils.java index ffbae03..c6f13c5 100644 --- a/src/test/java/org/warp/jcwdb/utils/NTestUtils.java +++ b/src/test/java/org/warp/jcwdb/utils/NTestUtils.java @@ -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); } diff --git a/src/test/java/org/warp/jcwdb/utils/NestedClass.java b/src/test/java/org/warp/jcwdb/utils/NestedClass.java deleted file mode 100644 index 042cbd4..0000000 --- a/src/test/java/org/warp/jcwdb/utils/NestedClass.java +++ /dev/null @@ -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 { - - } -} diff --git a/src/test/java/org/warp/jcwdb/utils/SimplestClass.java b/src/test/java/org/warp/jcwdb/utils/SimplestClass.java deleted file mode 100644 index 2791395..0000000 --- a/src/test/java/org/warp/jcwdb/utils/SimplestClass.java +++ /dev/null @@ -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; - } -} diff --git a/src/test/java/org/warp/jcwdb/utils/TestUtils.java b/src/test/java/org/warp/jcwdb/utils/TestUtils.java deleted file mode 100644 index ed622e0..0000000 --- a/src/test/java/org/warp/jcwdb/utils/TestUtils.java +++ /dev/null @@ -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 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 { - - } - } -}