From 84ec606794bd2eee9e06f076901e570a246e3231 Mon Sep 17 00:00:00 2001 From: Andrea Cavalli Date: Wed, 6 Mar 2019 23:56:04 +0100 Subject: [PATCH] Implemented cleaner --- pom.xml | 2 +- src/main/java/org/warp/cowdb/Database.java | 61 ++++++++++++- .../java/org/warp/cowdb/IBlocksMetadata.java | 6 ++ src/main/java/org/warp/cowdb/IDatabase.java | 4 + .../database/DatabaseBlocksMetadata.java | 5 ++ src/test/java/org/warp/jcwdb/tests/Clean.java | 88 +++++++++++++++++++ 6 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 src/test/java/org/warp/jcwdb/tests/Clean.java diff --git a/pom.xml b/pom.xml index fd15675..a2c7bc4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.warp jcwdb - 1.5.2 + 1.5.3 jcwdb https://git.ignuranza.net/andreacavalli/JCWDB diff --git a/src/main/java/org/warp/cowdb/Database.java b/src/main/java/org/warp/cowdb/Database.java index 0e2857b..eeba6c5 100644 --- a/src/main/java/org/warp/cowdb/Database.java +++ b/src/main/java/org/warp/cowdb/Database.java @@ -3,8 +3,10 @@ package org.warp.cowdb; import org.warp.cowdb.database.*; import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; public class Database implements IDatabase, IDatabaseTools { @@ -15,7 +17,11 @@ public class Database implements IDatabase, IDatabaseTools { private final DatabaseReferencesIO referencesIO; private final DatabaseReferencesMetadata referencesMetadata; private final DatabaseObjectsIO objectsIO; + private final Path dataFile; + private final Path blocksMetaFile; + private final Path referencesMetaFile; private EnhancedObject loadedRootObject; + private volatile boolean closed; public Database(Path dataFile, Path blocksMetaFile, Path referencesMetaFile) throws IOException { if (Files.notExists(dataFile)) { @@ -27,6 +33,9 @@ public class Database implements IDatabase, IDatabaseTools { if (Files.notExists(referencesMetaFile)) { Files.createFile(referencesMetaFile); } + this.dataFile = dataFile; + this.blocksMetaFile = blocksMetaFile; + this.referencesMetaFile = referencesMetaFile; this.databaseTools = this; this.fileIO = new DatabaseFileIO(dataFile); this.blocksMetadata = new DatabaseBlocksMetadata(blocksMetaFile); @@ -37,11 +46,61 @@ public class Database implements IDatabase, IDatabaseTools { } @Override - public void close() throws IOException { + public synchronized void close() throws IOException { + if (this.closed) { + throw new IOException("The database has been already closed!"); + } this.objectsIO.setEnhancedObject(0, loadedRootObject); 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); + Database databaseToClean = new Database(newDataFile, newBlocksFile, newReferencesFile); + Database newDatabase = new Database(dataFile, blocksMetaFile, referencesMetaFile); + + long referencesCount = databaseToClean.referencesMetadata.getFirstFreeReference(); + long blocksCount = databaseToClean.blocksMetadata.getTotalBlocksCount(); + long writtenReferences = 0; + + for (int referenceID = 0; referenceID < referencesCount; referenceID++) { + try { + ByteBuffer buffer = databaseToClean.referencesIO.readFromReference(referenceID); + newDatabase.referencesIO.writeToReference(referenceID, buffer.limit(), buffer); + writtenReferences++; + } catch (IOException ex) { + System.out.println("Error while reading reference " + referenceID + ". References written: " + writtenReferences); + } + } + System.out.println("References written: " + writtenReferences + ". Removed " + (blocksCount - writtenReferences) + " blocks. Removed " + (referencesCount - writtenReferences) + " references."); + databaseToClean.close(); + newDatabase.close(); + Files.deleteIfExists(newDataFile); + Files.deleteIfExists(newBlocksFile); + Files.deleteIfExists(newReferencesFile); } public T loadRoot(Class type, FunctionWithIO ifAbsent) throws IOException { diff --git a/src/main/java/org/warp/cowdb/IBlocksMetadata.java b/src/main/java/org/warp/cowdb/IBlocksMetadata.java index 31c0dea..985af39 100644 --- a/src/main/java/org/warp/cowdb/IBlocksMetadata.java +++ b/src/main/java/org/warp/cowdb/IBlocksMetadata.java @@ -42,4 +42,10 @@ public interface IBlocksMetadata { * Close file */ void close() throws IOException; + + /** + * Get total count of blocks + * @return + */ + long getTotalBlocksCount(); } diff --git a/src/main/java/org/warp/cowdb/IDatabase.java b/src/main/java/org/warp/cowdb/IDatabase.java index 0ffeec8..828ea45 100644 --- a/src/main/java/org/warp/cowdb/IDatabase.java +++ b/src/main/java/org/warp/cowdb/IDatabase.java @@ -5,4 +5,8 @@ import java.io.IOException; public interface IDatabase { void close() throws IOException; + + boolean isClosed(); + + void closeAndClean() throws IOException; } diff --git a/src/main/java/org/warp/cowdb/database/DatabaseBlocksMetadata.java b/src/main/java/org/warp/cowdb/database/DatabaseBlocksMetadata.java index d878a61..849e9e7 100644 --- a/src/main/java/org/warp/cowdb/database/DatabaseBlocksMetadata.java +++ b/src/main/java/org/warp/cowdb/database/DatabaseBlocksMetadata.java @@ -75,4 +75,9 @@ public class DatabaseBlocksMetadata implements IBlocksMetadata { data.flip(); return metaFileChannel.write(data, blockId * BLOCK_META_BYTES_COUNT); } + + @Override + public long getTotalBlocksCount() { + return firstFreeBlock; + } } diff --git a/src/test/java/org/warp/jcwdb/tests/Clean.java b/src/test/java/org/warp/jcwdb/tests/Clean.java new file mode 100644 index 0000000..5a12baf --- /dev/null +++ b/src/test/java/org/warp/jcwdb/tests/Clean.java @@ -0,0 +1,88 @@ +package org.warp.jcwdb.tests; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.warp.cowdb.EnhancedObject; +import org.warp.cowdb.IDatabaseTools; +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; + +public class Clean { + private NTestUtils.WrappedDb db; + private RootTwoClasses root; + + @Before + public void setUp() throws Exception { + db = NTestUtils.wrapDb().create((db) -> { + root = db.get().loadRoot(RootTwoClasses.class, RootTwoClasses::new); + }); + root.class1 = new NTestUtils.RootClass(db.get()); + db.setRootClassValues(root.class1); + root.class2 = new NTestUtils.RootClass(db.get()); + db.setRootClassValues(root.class2); + root.setClass3(new NTestUtils.RootClass(db.get())); + db.setRootClassValues(root.getClass3()); + root.setClass4(new NTestUtils.RootClass(db.get())); + db.setRootClassValues(root.getClass4()); + } + + @Test + public void shouldMatchMultipleEnhancedObjects() throws IOException { + db.testRootClassValues(root.class1); + db.testRootClassValues(root.class2); + db.testRootClassValues(root.getClass3()); + db.testRootClassValues(root.getClass4()); + db.get().closeAndClean(); + db = NTestUtils.wrapDb().create((db) -> { + root = db.get().loadRoot(RootTwoClasses.class, RootTwoClasses::new); + }); + } + + @After + public void tearDown() throws Exception { + db.delete(); + } + + public static class RootTwoClasses extends EnhancedObject { + + @DBField(id = 0, type = DBDataType.ENHANCED_OBJECT) + public NTestUtils.RootClass class1; + + @DBField(id = 1, type = DBDataType.ENHANCED_OBJECT) + public NTestUtils.RootClass class2; + + public RootTwoClasses() { + super(); + } + + public RootTwoClasses(IDatabaseTools databaseTools) throws IOException { + super(databaseTools); + } + + @DBPropertyGetter(id = 0, type = DBDataType.ENHANCED_OBJECT) + public NTestUtils.RootClass getClass3() { + return getProperty(); + } + + @DBPropertySetter(id = 0, type = DBDataType.ENHANCED_OBJECT) + public void setClass3(NTestUtils.RootClass value) { + setProperty(value); + } + + @DBPropertyGetter(id = 1, type = DBDataType.ENHANCED_OBJECT) + public NTestUtils.RootClass getClass4() { + return getProperty(); + } + + @DBPropertySetter(id = 1, type = DBDataType.ENHANCED_OBJECT) + public void setClass4(NTestUtils.RootClass value) { + setProperty(value); + } + } +}