276 lines
9.2 KiB
Java
276 lines
9.2 KiB
Java
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<WeakReference<DBObject>> 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<DBObject> 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> 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;
|
|
}
|
|
}
|