197 lines
7.5 KiB
Java
197 lines
7.5 KiB
Java
package it.cavallium.strangedb.java.database;
|
|
|
|
import it.cavallium.strangedb.database.DatabaseCore;
|
|
import it.cavallium.strangedb.database.references.ReferenceInfo;
|
|
import it.cavallium.strangedb.java.objects.EnhancedObject;
|
|
import it.cavallium.strangedb.functionalinterfaces.FunctionWithIO;
|
|
import it.cavallium.strangedb.java.objects.EnhancedObjectIndices;
|
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
|
|
|
import java.io.IOException;
|
|
import java.nio.ByteBuffer;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.StandardCopyOption;
|
|
|
|
import static it.cavallium.strangedb.database.references.DatabaseReferencesMetadata.*;
|
|
import static it.cavallium.strangedb.java.database.DatabaseObjectsIO.ENHANCED_OBJECT_METADATA_CLEANER;
|
|
import static it.cavallium.strangedb.java.database.DatabaseObjectsIO.REFERENCES_LIST_CLEANER;
|
|
|
|
public class DatabaseJava extends DatabaseCore implements IDatabaseTools {
|
|
private final IDatabaseTools databaseTools;
|
|
private final DatabaseObjectsIO objectsIO;
|
|
private EnhancedObject loadedRootObject;
|
|
private boolean hasLoadedRootObject;
|
|
|
|
public DatabaseJava(Path dataFile, Path blocksMetaFile, Path referencesMetaFile) throws IOException {
|
|
super(dataFile, blocksMetaFile, referencesMetaFile);
|
|
this.databaseTools = this;
|
|
this.objectsIO = new DatabaseObjectsIO(databaseTools, referencesIO);
|
|
this.hasLoadedRootObject = false;
|
|
}
|
|
|
|
@Override
|
|
public synchronized void close() throws IOException {
|
|
if (this.closed) {
|
|
throw new IOException("The database has been already closed!");
|
|
}
|
|
if (hasLoadedRootObject) {
|
|
this.objectsIO.setEnhancedObject(0, loadedRootObject);
|
|
}
|
|
super.close();
|
|
}
|
|
|
|
public <T extends EnhancedObject> T loadRoot(FunctionWithIO<IDatabaseTools, T> ifAbsent) throws IOException {
|
|
if (hasLoadedRootObject) {
|
|
throw new RuntimeException("Root already set!");
|
|
}
|
|
T root;
|
|
if (referencesMetadata.getFirstFreeReference() > 0) {
|
|
root = objectsIO.loadEnhancedObject(0);
|
|
} else {
|
|
if (objectsIO.newNullObject() != 0) {
|
|
throw new IOException("Can't allocate root!");
|
|
} else {
|
|
root = ifAbsent.apply(DatabaseJava.this);
|
|
objectsIO.setEnhancedObject(0, root);
|
|
}
|
|
}
|
|
loadedRootObject = root;
|
|
hasLoadedRootObject = true;
|
|
return root;
|
|
}
|
|
|
|
public <T extends EnhancedObject> T loadRoot(FunctionWithIO<IDatabaseTools, T> ifAbsent, Class<?> forcedClassType) throws IOException {
|
|
if (hasLoadedRootObject) {
|
|
throw new RuntimeException("Root already set!");
|
|
}
|
|
T root;
|
|
if (referencesMetadata.getFirstFreeReference() > 0) {
|
|
root = objectsIO.loadEnhancedObject(0, forcedClassType);
|
|
} else {
|
|
if (objectsIO.newNullObject() != 0) {
|
|
throw new IOException("Can't allocate root!");
|
|
} else {
|
|
root = ifAbsent.apply(DatabaseJava.this);
|
|
objectsIO.setEnhancedObject(0, root);
|
|
}
|
|
}
|
|
loadedRootObject = root;
|
|
hasLoadedRootObject = true;
|
|
return root;
|
|
}
|
|
|
|
@Override
|
|
public void closeAndClean() throws IOException {
|
|
if (!this.closed) {
|
|
this.close();
|
|
}
|
|
Path newDataFile = dataFile.resolveSibling("compressed-data-file.tmp");
|
|
Path newBlocksFile = blocksMetaFile.resolveSibling("compressed-blocks-file.tmp");
|
|
Path newReferencesFile = referencesMetaFile.resolveSibling("compressed-references-file.tmp");
|
|
Path backupDataFile = dataFile.resolveSibling("backup-data.db.bak");
|
|
Path backupBlocksFile = blocksMetaFile.resolveSibling("backup-blocks.dat.bak");
|
|
Path backupReferencesFile = referencesMetaFile.resolveSibling("backup-references.dat.bak");
|
|
Files.copy(dataFile, backupDataFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
|
Files.copy(blocksMetaFile, backupBlocksFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
|
Files.copy(referencesMetaFile, backupReferencesFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
|
|
Files.move(dataFile, newDataFile, StandardCopyOption.REPLACE_EXISTING);
|
|
Files.move(blocksMetaFile, newBlocksFile, StandardCopyOption.REPLACE_EXISTING);
|
|
Files.move(referencesMetaFile, newReferencesFile, StandardCopyOption.REPLACE_EXISTING);
|
|
DatabaseJava databaseToClean = instantiateNewDatabase(newDataFile, newBlocksFile, newReferencesFile);
|
|
DatabaseJava newDatabase = instantiateNewDatabase(dataFile, blocksMetaFile, referencesMetaFile);
|
|
|
|
long firstFreeReference = databaseToClean.referencesMetadata.getFirstFreeReference();
|
|
long referencesCount = 0;
|
|
long blocksCount = databaseToClean.blocksMetadata.getTotalBlocksCount();
|
|
long writtenReferences = 0;
|
|
long writtenBlocks = 0;
|
|
|
|
LongArrayList idsToKeep = new LongArrayList();
|
|
cleanRef(databaseToClean, idsToKeep, 0);
|
|
|
|
for (int referenceID = 0; referenceID < firstFreeReference; referenceID++) {
|
|
try {
|
|
ReferenceInfo ref = databaseToClean.referencesMetadata.getCleanReference(referenceID);
|
|
if (!NONEXISTENT_REFERENCE_INFO.equals(ref)) {
|
|
referencesCount++;
|
|
if (idsToKeep.contains(referenceID)) {
|
|
ByteBuffer buffer = databaseToClean.referencesIO.readFromReference(referenceID);
|
|
newDatabase.referencesIO.writeToReference(referenceID, ref.getCleanerId(), buffer.limit(), buffer);
|
|
writtenReferences++;
|
|
if (buffer.limit() > 0) {
|
|
writtenBlocks++;
|
|
}
|
|
}
|
|
}
|
|
} catch (IOException ex) {
|
|
System.out.println("Error while reading reference " + referenceID + ". References written: " + writtenReferences);
|
|
}
|
|
}
|
|
System.out.println("[Java Cleaner] References written: " + writtenReferences + ". Removed " + (blocksCount - writtenBlocks) + " blocks. Removed " + (referencesCount - writtenReferences) + " references.");
|
|
databaseToClean.close();
|
|
newDatabase.close();
|
|
Files.deleteIfExists(newDataFile);
|
|
Files.deleteIfExists(newBlocksFile);
|
|
Files.deleteIfExists(newReferencesFile);
|
|
}
|
|
|
|
private void cleanRef(DatabaseJava db, LongArrayList idsToKeep, long ref) throws IOException {
|
|
idsToKeep.add(ref);
|
|
ReferenceInfo refInfo = db.referencesMetadata.getCleanReference(ref);
|
|
if (!NONEXISTENT_REFERENCE_INFO.equals(refInfo)) {
|
|
switch (refInfo.getCleanerId()) {
|
|
case ENHANCED_OBJECT_METADATA_CLEANER: {
|
|
EnhancedObjectIndices enhancedObjectUids = db.objectsIO.loadEnhancedObjectUids(ref);
|
|
for (long fieldUid : enhancedObjectUids.fieldUids) {
|
|
cleanRef(db, idsToKeep, fieldUid);
|
|
}
|
|
for (long propUid : enhancedObjectUids.propertyUids) {
|
|
cleanRef(db, idsToKeep, propUid);
|
|
}
|
|
cleanRef(db, idsToKeep, enhancedObjectUids.nativeDataUid);
|
|
break;
|
|
}
|
|
case ERRORED_CLEANER: {
|
|
System.err.println("Errored cleaner found! Skipping...");
|
|
break;
|
|
}
|
|
case REFERENCES_LIST_CLEANER: {
|
|
LongArrayList refList = db.objectsIO.loadReferencesList(ref);
|
|
for (long elemRef : refList) {
|
|
cleanRef(db, idsToKeep, elemRef);
|
|
}
|
|
break;
|
|
}
|
|
case BLANK_DATA_CLEANER: {
|
|
break;
|
|
}
|
|
default: {
|
|
System.err.println("Unrecognized cleaner found! Skipping...");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void registerClass(Class<?> type, int id) {
|
|
this.objectsIO.registerClass(type, id);
|
|
}
|
|
|
|
protected DatabaseJava instantiateNewDatabase(Path dataFile, Path blocksMetaFile, Path referencesMetaFile) throws IOException {
|
|
DatabaseJava newDatabaseJava = new DatabaseJava(dataFile, blocksMetaFile, referencesMetaFile);
|
|
this.getObjectsIO().getRegisteredClasses().forEach(newDatabaseJava::registerClass);
|
|
return newDatabaseJava;
|
|
}
|
|
|
|
@Override
|
|
public void initializeEnhancedObject(EnhancedObject enhancedObject) throws IOException {
|
|
this.objectsIO.getDataInitializer().initializeDbObject(enhancedObject);
|
|
}
|
|
|
|
@Override
|
|
public DatabaseObjectsIO getObjectsIO() {
|
|
return objectsIO;
|
|
}
|
|
|
|
}
|