package database.odb; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.nio.ByteBuffer; import java.util.HashSet; import java.util.Set; import java.util.Vector; import com.sleepycat.bind.EntryBinding; import com.sleepycat.bind.serial.SerialBinding; import com.sleepycat.bind.serial.StoredClassCatalog; import com.sleepycat.je.CheckpointConfig; import com.sleepycat.je.Cursor; import com.sleepycat.je.Database; import com.sleepycat.je.DatabaseConfig; import com.sleepycat.je.DatabaseEntry; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.Environment; import com.sleepycat.je.EnvironmentConfig; import com.sleepycat.je.LockMode; import com.sleepycat.je.OperationStatus; import com.sleepycat.persist.EntityStore; @SuppressWarnings("all") public class ObjectDatabase implements Runnable { private Environment environment; private EnvironmentConfig EnvConfig; private DatabaseConfig dbConfig; private EntityStore entityStore; private Thread checkpointThread; private CheckpointConfig checkpointConfig; private EntryBinding dataBinding; private StoredClassCatalog classCatalog; private Database db; private Database classCatalogDb; private Vector<Cursor> cursors = new Vector<Cursor>(); private static boolean debugObjects = false; public ObjectDatabase(String name, boolean allowCreate, boolean useCheckpointThread, boolean allowTransactional, Class targetClass) { EnvConfig = new EnvironmentConfig(); EnvConfig.setAllowCreate(allowCreate); //EnvConfig.setTransactional(allowTransactional); /*EntityModel model = new AnnotationModel(); model.registerClass(CopyOnWriteArrayListProxy.class); model.registerClass(MultimapProxy.class); model.registerClass(VectorProxy.class); Mutations mutation = new Mutations(); mutation.addDeleter(new Deleter(CreatureObject.class.getName(), 0, "performanceAudience")); StoreConfig storeConfig = new StoreConfig(); storeConfig.setModel(model); storeConfig.setAllowCreate(allowCreate); storeConfig.setTransactional(allowTransactional); storeConfig.setMutations(mutation);*/ environment = new Environment(new File(".", "odb/" + name), EnvConfig); //entityStore = new EntityStore(environment, "EntityStore." + name, storeConfig); dbConfig = new DatabaseConfig(); dbConfig.setAllowCreate(true); // dbConfig.setTransactional(false); classCatalogDb = environment.openDatabase(null, "ClassCatalogDB", dbConfig); // Create our class catalog classCatalog = new StoredClassCatalog(classCatalogDb); dataBinding = new SerialBinding(classCatalog, targetClass); db = environment.openDatabase(null, name, dbConfig); if (useCheckpointThread) { checkpointConfig = new CheckpointConfig(); checkpointThread = new Thread(this); checkpointThread.start(); } } public void put(Long key, Object value) { DatabaseEntry theKey = new DatabaseEntry(); theKey.setData(ByteBuffer.allocate(8).putLong(key).array()); DatabaseEntry theData = new DatabaseEntry(); dataBinding.objectToEntry(value, theData); db.put(null, theKey, theData); if (debugObjects) { debugObject(value); if (get(key) == null) { System.out.println("Failed to save " + value.getClass().getSimpleName() + " to database (Key: " + key + ")."); } } } public void put(String key, Object value) { DatabaseEntry theKey = new DatabaseEntry(); theKey.setData(key.getBytes()); DatabaseEntry theData = new DatabaseEntry(); dataBinding.objectToEntry(value, theData); db.put(null, theKey, theData); if (debugObjects) { debugObject(value); if (get(key) == null) { System.out.println("Failed to save " + value.getClass().getSimpleName() + " to database (Key: " + key + ")."); } } } public Object get(Long key) { if(!contains(key)) return null; DatabaseEntry theKey = new DatabaseEntry(); theKey.setData(ByteBuffer.allocate(8).putLong(key).array()); DatabaseEntry theData = new DatabaseEntry(); db.get(null, theKey, theData, LockMode.DEFAULT); // Recreate the object from the retrieved DatabaseEntry using the EntryBinding Object obj = dataBinding.entryToObject(theData); /* if(obj instanceof SWGObject) { ((SWGObject) obj).initializeBaselines(); ((SWGObject) obj).initAfterDBLoad(); ((SWGObject) obj).viewChildren((SWGObject) obj, true, true, child -> { child.initializeBaselines(); child.initAfterDBLoad(); }); }*/ return obj; } public Object get(String key) { if(!contains(key)) return null; DatabaseEntry theKey = new DatabaseEntry(); theKey.setData(key.getBytes()); DatabaseEntry theData = new DatabaseEntry(); db.get(null, theKey, theData, LockMode.DEFAULT); // Recreate the object from the retrieved DatabaseEntry using the EntryBinding return dataBinding.entryToObject(theData); } public void remove(Long key) { DatabaseEntry theKey = new DatabaseEntry(); theKey.setData(ByteBuffer.allocate(8).putLong(key).array()); db.removeSequence(null, theKey); } public ODBCursor getCursor() { Cursor cursor = db.openCursor(null, null); cursors.add(cursor); return new ODBCursor(cursor, dataBinding, this); } public boolean contains(Long key) { DatabaseEntry theKey = new DatabaseEntry(); theKey.setData(ByteBuffer.allocate(8).putLong(key).array()); DatabaseEntry theData = new DatabaseEntry(); return db.get(null, theKey, theData, LockMode.DEFAULT) == OperationStatus.SUCCESS; } public boolean contains(String key) { DatabaseEntry theKey = new DatabaseEntry(); theKey.setData(key.getBytes()); DatabaseEntry theData = new DatabaseEntry(); return db.get(null, theKey, theData, LockMode.DEFAULT) == OperationStatus.SUCCESS; } public Environment getEnvironment() { return environment; } public void compress() { environment.compress(); } public void close() { if (environment != null) { try { cursors.forEach(Cursor::close); environment.sync(); db.close(); classCatalogDb.close(); environment.close(); } catch(DatabaseException dbe) { System.err.println("Error closing environment" + dbe.toString()); } } } @Override public void run() { while(environment != null && environment.isValid()) { try { Thread.sleep(300000); environment.sync(); } catch (InterruptedException e) { e.printStackTrace(); } } } public Vector<Cursor> getCursors() { return cursors; } /* * @description Prints any classes that don't implement Serializable. */ public static void debugObject(Object object) { if (debugObjects) { Set<String> classes = new HashSet<String>(); debugMapUnserializableClasses(classes, object.getClass()); for (String type : classes) { System.err.println(type + " does not implement Serializable."); } } } public static void debugMapUnserializableClasses(Set<String> classes, Class<?> object) { boolean serializable = false; for (Type type : object.getGenericInterfaces()) { if (type != null && type.getTypeName().equals("Serializable")) { serializable = true; } } if (!serializable) { classes.add(object.getSimpleName()); debugObjects = false; } for (Field field : object.getDeclaredFields()) { if (field.getGenericType().getTypeName().contains("<")) { try { String genericType = field.getGenericType().getTypeName().split("<", field.getGenericType().getTypeName().lastIndexOf("<"))[1].replace(">", ""); debugMapUnserializableClasses(classes, Class.forName(genericType)); } catch (Exception e) { } } if (!field.getClass().getPackage().getName().startsWith("engine") && !field.getClass().getPackage().getName().startsWith("main") && !field.getClass().getPackage().getName().startsWith("protocol") && !field.getClass().getPackage().getName().startsWith("resources") && !field.getClass().getPackage().getName().startsWith("service")) { continue; } debugMapUnserializableClasses(classes, field.getClass()); } } }