Serialized classes update mechanism
Added serialized classes update mechanism and removed the useless initialize method.
This commit is contained in:
parent
1abc761fe6
commit
10ca07d4c5
@ -35,7 +35,6 @@ public class Database implements IDatabase {
|
||||
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 {
|
||||
@ -54,12 +53,11 @@ public class Database implements IDatabase {
|
||||
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;
|
||||
return objectsIO.dataInitializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -77,8 +75,8 @@ public class Database implements IDatabase {
|
||||
|
||||
public <T extends EnhancedObject> T loadRoot(Class<T> type) throws IOException {
|
||||
return loadRoot(type, () -> {
|
||||
T obj = objectsIO.instantiateEnhancedObject(type);
|
||||
dataInitializer.initializeDBObject(obj);
|
||||
T obj = objectsIO.toInstance(type);
|
||||
objectsIO.dataInitializer.initializeDBObject(obj);
|
||||
return obj;
|
||||
});
|
||||
}
|
||||
@ -118,7 +116,6 @@ public class Database implements IDatabase {
|
||||
public void initializeDBObject(EnhancedObject obj) throws IOException {
|
||||
initializeDBObjectFields(obj);
|
||||
initializeDBObjectProperties(obj);
|
||||
obj.initialize();
|
||||
}
|
||||
|
||||
private void initializeDBObjectFields(EnhancedObject obj) throws IOException {
|
||||
@ -197,18 +194,44 @@ public class Database implements IDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
public static class DatabaseEnhancedObjectUpgrader implements EnhancedObjectUpgrader {
|
||||
private final DatabaseObjectsIO objectsIO;
|
||||
private final long[] fieldRefs;
|
||||
private final long[] methodRefs;
|
||||
|
||||
public DatabaseEnhancedObjectUpgrader(DatabaseObjectsIO objectsIO, long[] fieldRefs, long[] methodRefs) {
|
||||
this.objectsIO = objectsIO;
|
||||
this.fieldRefs = fieldRefs;
|
||||
this.methodRefs = methodRefs;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getField(int id, DBDataType type, Supplier<Class<?>> enhancedClassType) throws IOException {
|
||||
return objectsIO.loadData(type, fieldRefs[id], enhancedClassType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getMethod(int id, DBDataType type, Supplier<Class<?>> enhancedClassType) throws IOException {
|
||||
return objectsIO.loadData(type, methodRefs[id], enhancedClassType);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DatabaseObjectsIO implements IObjectsIO {
|
||||
|
||||
private final IDatabase database;
|
||||
private final DatabaseReferencesIO referencesIO;
|
||||
|
||||
private final Object accessLock = new Object();
|
||||
private final DatabaseDataInitializer dataInitializer;
|
||||
|
||||
private Kryo kryo = new Kryo();
|
||||
|
||||
private DatabaseObjectsIO(IDatabase database, DatabaseReferencesIO referencesIO) {
|
||||
this.database = database;
|
||||
this.referencesIO = referencesIO;
|
||||
this.dataInitializer = new DatabaseDataInitializer(this);
|
||||
kryo.setRegistrationRequired(false);
|
||||
int id = -90;
|
||||
registerClass(boolean[].class, id++);
|
||||
@ -271,6 +294,7 @@ public class Database implements IDatabase {
|
||||
if (buffer.limit() == 0) {
|
||||
return null;
|
||||
}
|
||||
int serializedVersion = Byte.toUnsignedInt(buffer.get());
|
||||
int fieldsCount = buffer.getInt();
|
||||
int methodsCount = buffer.getInt();
|
||||
long[] fieldRefs = new long[fieldsCount];
|
||||
@ -281,7 +305,7 @@ public class Database implements IDatabase {
|
||||
for (int i = 0; i < methodsCount; i++) {
|
||||
methodRefs[i] = buffer.getLong();
|
||||
}
|
||||
return preloadEnhancedObject(objectType, fieldRefs, methodRefs);
|
||||
return preloadEnhancedObject(objectType, serializedVersion, fieldRefs, methodRefs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,8 +373,9 @@ public class Database implements IDatabase {
|
||||
synchronized (accessLock) {
|
||||
if (value != null) {
|
||||
EnhancedObjectFullInfo objectFullInfo = value.getAllInfo();
|
||||
int totalSize = Integer.BYTES * 2 + objectFullInfo.getFieldReferences().length * Long.BYTES + objectFullInfo.getPropertyReferences().length * Long.BYTES;
|
||||
int totalSize = Byte.BYTES + Integer.BYTES * 2 + objectFullInfo.getFieldReferences().length * Long.BYTES + objectFullInfo.getPropertyReferences().length * Long.BYTES;
|
||||
ByteBuffer buffer = ByteBuffer.allocate(totalSize);
|
||||
buffer.put((byte) objectFullInfo.getVersion());
|
||||
buffer.putInt(objectFullInfo.getFieldReferences().length);
|
||||
buffer.putInt(objectFullInfo.getPropertyReferences().length);
|
||||
for (int i = 0; i < objectFullInfo.getFieldReferences().length; i++) {
|
||||
@ -359,15 +384,25 @@ public class Database implements IDatabase {
|
||||
for (int i = 0; i < objectFullInfo.getPropertyReferences().length; i++) {
|
||||
buffer.putLong(objectFullInfo.getPropertyReferences()[i]);
|
||||
}
|
||||
long[] fieldReferences = objectFullInfo.getFieldReferences();
|
||||
DBDataType[] fieldTypes = objectFullInfo.getFieldTypes();
|
||||
Field[] fields = objectFullInfo.getFields();
|
||||
long[] propertyReferences = objectFullInfo.getPropertyReferences();
|
||||
DBDataType[] propertyTypes = objectFullInfo.getPropertyTypes();
|
||||
Object[] propertyValues = objectFullInfo.getLoadedPropertyValues();
|
||||
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 IOException(e);
|
||||
if (fields[i] != null) {
|
||||
try {
|
||||
setData(fieldReferences[i], fieldTypes[i], fields[i].get(value));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < objectFullInfo.getPropertyReferences().length; i++) {
|
||||
setData(objectFullInfo.getPropertyReferences()[i], objectFullInfo.getPropertyTypes()[i], objectFullInfo.getLoadedPropertyValues()[i]);
|
||||
if (propertyValues[i] != null) {
|
||||
setData(propertyReferences[i], propertyTypes[i], propertyValues[i]);
|
||||
}
|
||||
}
|
||||
buffer.flip();
|
||||
referencesIO.writeToReference(reference, totalSize, buffer);
|
||||
@ -713,7 +748,7 @@ public class Database implements IDatabase {
|
||||
return biggestFieldId;
|
||||
}
|
||||
|
||||
private <T extends EnhancedObject> T instantiateEnhancedObject(Class<T> type) throws IOException {
|
||||
private <T extends EnhancedObject> T toInstance(Class<T> type) throws IOException {
|
||||
try {
|
||||
T obj = type.getConstructor().newInstance();
|
||||
obj.database = database;
|
||||
@ -725,10 +760,26 @@ public class Database implements IDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends EnhancedObject> T preloadEnhancedObject(Class<T> objectType, long[] fieldRefs, long[] methodRefs) throws IOException {
|
||||
T obj = instantiateEnhancedObject(objectType);
|
||||
preloadEnhancedObjectFields(obj, fieldRefs);
|
||||
preloadEnhancedObjectProperties(obj, methodRefs);
|
||||
private <T extends EnhancedObject> T preloadEnhancedObject(Class<T> objectType, int serializedVersion, long[] fieldRefs, long[] methodRefs) throws IOException {
|
||||
// Instantiate the class to an object
|
||||
T obj = toInstance(objectType);
|
||||
|
||||
// Check the serialized version
|
||||
DBClass dbClass = objectType.getAnnotation(DBClass.class);
|
||||
int classVersion = 0;
|
||||
if (dbClass != null) {
|
||||
classVersion = dbClass.version();
|
||||
}
|
||||
if (classVersion > serializedVersion) {
|
||||
DatabaseEnhancedObjectUpgrader enhancedObjectUpgrader = new DatabaseEnhancedObjectUpgrader(this, fieldRefs, methodRefs);
|
||||
dataInitializer.initializeDBObject(obj);
|
||||
obj.onUpgrade(serializedVersion, enhancedObjectUpgrader);
|
||||
} else if (classVersion < serializedVersion) {
|
||||
throw new IllegalStateException("The serialized class is more recent than the current version of that class!");
|
||||
} else {
|
||||
preloadEnhancedObjectFields(obj, fieldRefs);
|
||||
preloadEnhancedObjectProperties(obj, methodRefs);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,19 @@
|
||||
package org.warp.cowdb;
|
||||
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||
import org.warp.jcwdb.ann.DBClass;
|
||||
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 final int version;
|
||||
protected IDatabase database;
|
||||
private Field[] fields;
|
||||
private DBDataType[] fieldTypes;
|
||||
@ -26,17 +28,15 @@ public abstract class EnhancedObject {
|
||||
private Map<String, DBPropertyGetter> getterMethods;
|
||||
|
||||
public EnhancedObject() {
|
||||
|
||||
version = getClassVersion();
|
||||
}
|
||||
|
||||
public EnhancedObject(IDatabase database) throws IOException {
|
||||
this.database = database;
|
||||
version = getClassVersion();
|
||||
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;
|
||||
@ -63,7 +63,7 @@ public abstract class EnhancedObject {
|
||||
}
|
||||
|
||||
public EnhancedObjectFullInfo getAllInfo() {
|
||||
return new EnhancedObjectFullInfo(fieldReferences, fieldTypes, fields, propertyReferences, propertyTypes, loadedPropertyValues);
|
||||
return new EnhancedObjectFullInfo(version, fieldReferences, fieldTypes, fields, propertyReferences, propertyTypes, loadedPropertyValues);
|
||||
}
|
||||
|
||||
|
||||
@ -98,4 +98,19 @@ public abstract class EnhancedObject {
|
||||
loadedPropertyValues[propertyId] = value;
|
||||
loadedProperties[propertyId] = true;
|
||||
}
|
||||
|
||||
private int getClassVersion() {
|
||||
DBClass classAnnotation = this.getClass().getAnnotation(DBClass.class);
|
||||
return classAnnotation == null ? 0 : classAnnotation.version();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an unloaded class is older than the actual version
|
||||
* @param oldObjectVersion
|
||||
* @param enhancedObjectUpgrader
|
||||
* @throws IOException
|
||||
*/
|
||||
public void onUpgrade(int oldObjectVersion, EnhancedObjectUpgrader enhancedObjectUpgrader) throws IOException {
|
||||
throw new NotImplementedException("Method onUpgrade() is not implemented for class " + this.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import org.warp.jcwdb.ann.DBDataType;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class EnhancedObjectFullInfo {
|
||||
private final int version;
|
||||
private final long[] fieldReferences;
|
||||
private final DBDataType[] fieldTypes;
|
||||
private final Field[] fields;
|
||||
@ -12,7 +13,11 @@ public class EnhancedObjectFullInfo {
|
||||
private final DBDataType[] propertyTypes;
|
||||
private final Object[] loadedPropertyValues;
|
||||
|
||||
EnhancedObjectFullInfo(long[] fieldReferences, DBDataType[] fieldTypes, Field[] fields, long[] propertyReferences, DBDataType[] propertyTypes, Object[] loadedPropertyValues) {
|
||||
EnhancedObjectFullInfo(int version, long[] fieldReferences, DBDataType[] fieldTypes, Field[] fields, long[] propertyReferences, DBDataType[] propertyTypes, Object[] loadedPropertyValues) {
|
||||
this.version = version;
|
||||
if (version > 255) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.fieldReferences = fieldReferences;
|
||||
this.fieldTypes = fieldTypes;
|
||||
this.fields = fields;
|
||||
@ -21,6 +26,10 @@ public class EnhancedObjectFullInfo {
|
||||
this.loadedPropertyValues = loadedPropertyValues;
|
||||
}
|
||||
|
||||
int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
long[] getFieldReferences() {
|
||||
return fieldReferences;
|
||||
}
|
||||
|
17
src/main/java/org/warp/cowdb/EnhancedObjectUpgrader.java
Normal file
17
src/main/java/org/warp/cowdb/EnhancedObjectUpgrader.java
Normal file
@ -0,0 +1,17 @@
|
||||
package org.warp.cowdb;
|
||||
|
||||
import org.warp.jcwdb.ann.DBDataType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface EnhancedObjectUpgrader {
|
||||
Object getField(int id, DBDataType type, Supplier<Class<?>> enhancedClassType) throws IOException;
|
||||
default Object getField(int id, DBDataType type) throws IOException {
|
||||
return getField(id, type, null);
|
||||
}
|
||||
Object getMethod(int id, DBDataType type, Supplier<Class<?>> enhancedClassType) throws IOException;
|
||||
default Object getMethod(int id, DBDataType type) throws IOException {
|
||||
return getField(id, type, null);
|
||||
}
|
||||
}
|
@ -25,11 +25,6 @@ public class EnhancedObjectCowList<T extends EnhancedObject> extends CowList<T>
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
public EnhancedObjectCowList(IDatabase database, Class<T> type) throws IOException {
|
||||
super(database);
|
||||
this.type = type;
|
||||
|
@ -27,11 +27,6 @@ public class ObjectCowList<T> extends CowList<T> {
|
||||
indices = new LongArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected T loadItem(long uid) throws IOException {
|
||||
return database.getObjectsIO().loadObject(uid);
|
||||
|
12
src/main/java/org/warp/jcwdb/ann/DBClass.java
Normal file
12
src/main/java/org/warp/jcwdb/ann/DBClass.java
Normal file
@ -0,0 +1,12 @@
|
||||
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.TYPE})
|
||||
public @interface DBClass {
|
||||
int version() default 0;
|
||||
}
|
58
src/test/java/org/warp/jcwdb/tests/EnhancedClassUpdate.java
Normal file
58
src/test/java/org/warp/jcwdb/tests/EnhancedClassUpdate.java
Normal file
@ -0,0 +1,58 @@
|
||||
package org.warp.jcwdb.tests;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.warp.cowdb.Database;
|
||||
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;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class EnhancedClassUpdate {
|
||||
|
||||
private Path path1;
|
||||
private Path path2;
|
||||
private Path path3;
|
||||
private Database db;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
path1 = Files.createTempFile("db-tests-", ".db");
|
||||
path2 = Files.createTempFile("db-tests-", ".db");
|
||||
path3 = Files.createTempFile("db-tests-", ".db");
|
||||
db = new Database(path1, path2, path3);
|
||||
OldClass root = db.loadRoot(OldClass.class);
|
||||
root.field1 = "Abc";
|
||||
root.field2 = 12;
|
||||
root.field4 = 13;
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldUpdateClass() throws IOException {
|
||||
db = new Database(path1, path2, path3);
|
||||
V2Class root = db.loadRoot(V2Class.class);
|
||||
assertEquals(root.field4, "Abc");
|
||||
assertEquals(root.field2, 12);
|
||||
assertEquals(root.field1, 13L);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
Files.deleteIfExists(path1);
|
||||
Files.deleteIfExists(path2);
|
||||
Files.deleteIfExists(path3);
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ import org.warp.jcwdb.utils.NTestUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class NDBMultipleEnhancedObjects {
|
||||
public class MultipleEnhancedObjects {
|
||||
private NTestUtils.WrappedDb db;
|
||||
private RootTwoClasses root;
|
||||
|
||||
@ -81,10 +81,5 @@ public class NDBMultipleEnhancedObjects {
|
||||
public void setClass4(NTestUtils.RootClass value) {
|
||||
setProperty(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() throws IOException {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
19
src/test/java/org/warp/jcwdb/tests/OldClass.java
Normal file
19
src/test/java/org/warp/jcwdb/tests/OldClass.java
Normal file
@ -0,0 +1,19 @@
|
||||
package org.warp.jcwdb.tests;
|
||||
|
||||
import org.warp.cowdb.EnhancedObject;
|
||||
import org.warp.jcwdb.ann.DBClass;
|
||||
import org.warp.jcwdb.ann.DBDataType;
|
||||
import org.warp.jcwdb.ann.DBField;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class OldClass extends EnhancedObject {
|
||||
@DBField(id = 0, type = DBDataType.OBJECT)
|
||||
public String field1;
|
||||
|
||||
@DBField(id = 1, type = DBDataType.INTEGER)
|
||||
public int field2;
|
||||
|
||||
@DBField(id = 3, type = DBDataType.INTEGER)
|
||||
public int field4;
|
||||
}
|
33
src/test/java/org/warp/jcwdb/tests/V2Class.java
Normal file
33
src/test/java/org/warp/jcwdb/tests/V2Class.java
Normal file
@ -0,0 +1,33 @@
|
||||
package org.warp.jcwdb.tests;
|
||||
|
||||
import org.warp.cowdb.EnhancedObject;
|
||||
import org.warp.cowdb.EnhancedObjectUpgrader;
|
||||
import org.warp.jcwdb.ann.DBClass;
|
||||
import org.warp.jcwdb.ann.DBDataType;
|
||||
import org.warp.jcwdb.ann.DBField;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@DBClass(version = 1)
|
||||
public class V2Class extends EnhancedObject {
|
||||
@DBField(id = 0, type = DBDataType.LONG)
|
||||
public long field1;
|
||||
|
||||
@DBField(id = 1, type = DBDataType.INTEGER)
|
||||
public int field2;
|
||||
|
||||
@DBField(id = 3, type = DBDataType.OBJECT)
|
||||
public String field4;
|
||||
|
||||
@Override
|
||||
public void onUpgrade(int oldObjectVersion, EnhancedObjectUpgrader enhancedObjectUpgrader) throws IOException {
|
||||
switch (oldObjectVersion) {
|
||||
case 0: {
|
||||
field1 = (long) (Integer) enhancedObjectUpgrader.getField(3, DBDataType.INTEGER);
|
||||
field2 = (int) enhancedObjectUpgrader.getField(1, DBDataType.INTEGER);
|
||||
field4 = (String) enhancedObjectUpgrader.getField(0, DBDataType.OBJECT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,10 +18,6 @@ public class NSimplestClass extends EnhancedObject {
|
||||
|
||||
public NSimplestClass(Database database) throws IOException {
|
||||
super(database);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() throws IOException {
|
||||
field1 = true;
|
||||
}
|
||||
}
|
||||
|
@ -374,10 +374,5 @@ public class NTestUtils {
|
||||
public boolean test() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() throws IOException {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user