package it.cavallium.strangedb.database; import it.cavallium.strangedb.database.blocks.DatabaseBlocksIO; import it.cavallium.strangedb.database.blocks.DatabaseBlocksMetadata; import it.cavallium.strangedb.database.references.DatabaseReferencesIO; import it.cavallium.strangedb.database.references.DatabaseReferencesMetadata; import it.cavallium.strangedb.database.references.ReferenceInfo; 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.NONEXISTENT_REFERENCE_INFO; public class DatabaseCore implements IDatabase { private final DatabaseFileIO fileIO; private final DatabaseBlocksIO blocksIO; protected final DatabaseBlocksMetadata blocksMetadata; protected final DatabaseReferencesIO referencesIO; protected final DatabaseReferencesMetadata referencesMetadata; protected final Path dataFile; protected final Path blocksMetaFile; protected final Path referencesMetaFile; protected volatile boolean closed; public DatabaseCore(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.dataFile = dataFile; this.blocksMetaFile = blocksMetaFile; this.referencesMetaFile = 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); } @Override public synchronized void close() throws IOException { if (this.closed) { throw new IOException("The database has been already closed!"); } this.referencesMetadata.close(); this.blocksMetadata.close(); this.fileIO.close(); this.closed = true; } @Override public boolean isClosed() { return closed; } @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); DatabaseCore databaseToClean = new DatabaseCore(newDataFile, newBlocksFile, newReferencesFile); DatabaseCore newDatabase = new DatabaseCore(dataFile, blocksMetaFile, referencesMetaFile); long referencesCount = databaseToClean.referencesMetadata.getFirstFreeReference(); long blocksCount = databaseToClean.blocksMetadata.getTotalBlocksCount(); long writtenReferences = 0; long writtenBlocks = 0; for (int referenceID = 0; referenceID < referencesCount; referenceID++) { try { ReferenceInfo ref = databaseToClean.referencesMetadata.getCleanReference(referenceID); if (!NONEXISTENT_REFERENCE_INFO.equals(ref)) { 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("[Core 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); } }