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);
+ }
+ }
+}