Implemented cleaner

This commit is contained in:
Andrea Cavalli 2019-03-06 23:56:04 +01:00
parent 5fb0a8a057
commit 84ec606794
6 changed files with 164 additions and 2 deletions

View File

@ -6,7 +6,7 @@
<groupId>org.warp</groupId>
<artifactId>jcwdb</artifactId>
<version>1.5.2</version>
<version>1.5.3</version>
<name>jcwdb</name>
<url>https://git.ignuranza.net/andreacavalli/JCWDB</url>

View File

@ -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 extends EnhancedObject> T loadRoot(Class<T> type, FunctionWithIO<IDatabaseTools, T> ifAbsent) throws IOException {

View File

@ -42,4 +42,10 @@ public interface IBlocksMetadata {
* Close file
*/
void close() throws IOException;
/**
* Get total count of blocks
* @return
*/
long getTotalBlocksCount();
}

View File

@ -5,4 +5,8 @@ import java.io.IOException;
public interface IDatabase {
void close() throws IOException;
boolean isClosed();
void closeAndClean() throws IOException;
}

View File

@ -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;
}
}

View File

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