summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKeuin <[email protected]>2021-01-12 12:51:23 +0800
committerkeuin <[email protected]>2021-01-12 12:51:23 +0800
commitafd26cdd12fef4bd2aafa2ac8d708e18d277a2fe (patch)
treee389ccbe9fb48e03de25b38d3ef7d5c273c5ec97 /src
parent8b05c48d76bbb05c2b440e57df1d9b3323edc598 (diff)
Implement object collection(not tested) and sha256(tested)
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/BytesUtil.java16
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/inc/ObjectCollectionManager.java11
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollection.java45
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollectionFactory.java48
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/FileIdentifierFactory.java8
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/ObjectIdentifier.java (renamed from src/main/java/com/keuin/kbackupfabric/util/backup/inc/identifier/ObjectIdentifier.java)3
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/Sha256Identifier.java67
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/SingleHashIdentifier.java (renamed from src/main/java/com/keuin/kbackupfabric/util/backup/inc/identifier/SingleHashIdentifier.java)12
-rw-r--r--src/test/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/Sha256IdentifierTest.java23
-rw-r--r--src/test/sha2561
10 files changed, 220 insertions, 14 deletions
diff --git a/src/main/java/com/keuin/kbackupfabric/util/BytesUtil.java b/src/main/java/com/keuin/kbackupfabric/util/BytesUtil.java
new file mode 100644
index 0000000..6ded7b8
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/util/BytesUtil.java
@@ -0,0 +1,16 @@
+package com.keuin.kbackupfabric.util;
+
+import java.nio.charset.StandardCharsets;
+
+public class BytesUtil {
+ private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
+ public static String bytesToHex(byte[] bytes) {
+ byte[] hexChars = new byte[bytes.length * 2];
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = HEX_ARRAY[v >>> 4];
+ hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
+ }
+ return new String(hexChars, StandardCharsets.UTF_8);
+ }
+}
diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/inc/ObjectCollectionManager.java b/src/main/java/com/keuin/kbackupfabric/util/backup/inc/ObjectCollectionManager.java
deleted file mode 100644
index 9560261..0000000
--- a/src/main/java/com/keuin/kbackupfabric/util/backup/inc/ObjectCollectionManager.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.keuin.kbackupfabric.util.backup.inc;
-
-/**
- * Incremental backup is implemented as git-like file collection.
- * Files are called `objects`, the collection contains all files distinguished by their
- * identifiers. Usually, identifier is the combination of hash and other short information (such as size and another hash).
- * The identifier should use hashes that are strong enough, to prevent possible collisions.
- */
-public class ObjectCollectionManager {
-
-}
diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollection.java b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollection.java
new file mode 100644
index 0000000..d5d766a
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollection.java
@@ -0,0 +1,45 @@
+package com.keuin.kbackupfabric.util.backup.incremental;
+
+import com.keuin.kbackupfabric.util.backup.incremental.identifier.ObjectIdentifier;
+
+import java.util.Objects;
+import java.util.Set;
+
+public class ObjectCollection {
+ private final String name;
+ private final Set<ObjectIdentifier> elements;
+ private final Set<ObjectCollection> subCollections;
+
+ ObjectCollection(String name, Set<ObjectIdentifier> elements, Set<ObjectCollection> subCollections) {
+ this.name = Objects.requireNonNull(name);
+ this.elements = Objects.requireNonNull(elements);
+ this.subCollections = Objects.requireNonNull(subCollections);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Set<ObjectIdentifier> getElements() {
+ return elements;
+ }
+
+ public Set<ObjectCollection> getSubCollections() {
+ return subCollections;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ObjectCollection that = (ObjectCollection) o;
+ return name.equals(that.name) &&
+ elements.equals(that.elements) &&
+ subCollections.equals(that.subCollections);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, elements, subCollections);
+ }
+}
diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollectionFactory.java b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollectionFactory.java
new file mode 100644
index 0000000..8b974db
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollectionFactory.java
@@ -0,0 +1,48 @@
+package com.keuin.kbackupfabric.util.backup.incremental;
+
+import com.keuin.kbackupfabric.util.backup.incremental.identifier.FileIdentifierFactory;
+import com.keuin.kbackupfabric.util.backup.incremental.identifier.ObjectIdentifier;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Incremental backup is implemented as git-like file collection.
+ * Files are called `objects`, the collection contains all files distinguished by their
+ * identifiers. Usually, identifier is the combination of hash and other short information (such as size and another hash).
+ * The identifier should use hashes that are strong enough, to prevent possible collisions.
+ */
+public class ObjectCollectionFactory <T extends ObjectIdentifier> {
+ private final FileIdentifierFactory<T> identifierFactory;
+
+ public ObjectCollectionFactory(FileIdentifierFactory<T> identifierFactory) {
+ this.identifierFactory = identifierFactory;
+ }
+
+ public ObjectCollection fromDirectory(File directory) throws IOException {
+ final Set<ObjectIdentifier> subFiles = new HashSet<>();
+ final Set<ObjectCollection> subCollections = new HashSet<>();
+
+ if (!Objects.requireNonNull(directory).isDirectory())
+ throw new IllegalArgumentException("given file is not a directory");
+
+ for (Iterator<Path> iter = Files.walk(directory.toPath()).iterator(); iter.hasNext();) {
+ Path path = iter.next();
+ File file = path.toFile();
+ if (file.isDirectory()) {
+ subCollections.add(fromDirectory(file));
+ } else {
+ subFiles.add(identifierFactory.fromFile(file));
+ }
+ }
+
+ return new ObjectCollection(directory.getName(), subFiles, subCollections);
+ }
+
+}
diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/FileIdentifierFactory.java b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/FileIdentifierFactory.java
new file mode 100644
index 0000000..34ad9a0
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/FileIdentifierFactory.java
@@ -0,0 +1,8 @@
+package com.keuin.kbackupfabric.util.backup.incremental.identifier;
+
+import java.io.File;
+import java.io.IOException;
+
+public interface FileIdentifierFactory<T extends ObjectIdentifier> {
+ T fromFile(File file) throws IOException;
+}
diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/inc/identifier/ObjectIdentifier.java b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/ObjectIdentifier.java
index 079f49d..62798e1 100644
--- a/src/main/java/com/keuin/kbackupfabric/util/backup/inc/identifier/ObjectIdentifier.java
+++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/ObjectIdentifier.java
@@ -1,4 +1,4 @@
-package com.keuin.kbackupfabric.util.backup.inc.identifier;
+package com.keuin.kbackupfabric.util.backup.incremental.identifier;
/**
* The identifier distinguishing files in the object collection.
@@ -6,4 +6,5 @@ package com.keuin.kbackupfabric.util.backup.inc.identifier;
* All identifiers should be immutable and implement their own equals method.
*/
public interface ObjectIdentifier {
+ String getIdentification();
}
diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/Sha256Identifier.java b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/Sha256Identifier.java
new file mode 100644
index 0000000..9f56b0e
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/Sha256Identifier.java
@@ -0,0 +1,67 @@
+package com.keuin.kbackupfabric.util.backup.incremental.identifier;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
+
+public class Sha256Identifier extends SingleHashIdentifier {
+
+ private static final int SHA256_LENGTH = 32;
+ private static final Sha256Identifier DUMMY = new Sha256Identifier(new byte[SHA256_LENGTH]); // only for using its hash method
+ private static final FileIdentifierFactory<Sha256Identifier> factory = Sha256Identifier::fromFile;
+
+ public static Sha256Identifier fromFile(File file) throws IOException {
+ if (!Objects.requireNonNull(file).isFile()) {
+ throw new IllegalArgumentException("file is not a file");
+ }
+ return new Sha256Identifier(DUMMY.hash(file));
+ }
+
+ public static FileIdentifierFactory<Sha256Identifier> getFactory() {
+ return factory;
+ }
+
+ protected Sha256Identifier(byte[] hash) {
+ super(hash);
+ Objects.requireNonNull(hash);
+ if (hash.length != SHA256_LENGTH) {
+ throw new IllegalStateException(String.format("SHA256 must be %d bytes", SHA256_LENGTH));
+ }
+ }
+
+ @Override
+ protected byte[] hash(File file) throws IOException {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+
+ FileInputStream inputStream = new FileInputStream(file);
+
+ // This does not work. I don't know why
+// FileChannel channel = inputStream.getChannel();
+// ByteBuffer buffer = ByteBuffer.allocate(128);
+// int readLength;
+// while ((readLength = channel.read(buffer)) > 0)
+// digest.update(buffer);
+
+ // This also works, without warnings
+ byte[] readBuffer = new byte[1024 * 1024];
+ int readLength;
+ while ((readLength = inputStream.read(readBuffer)) > 0)
+ digest.update(readBuffer,0, readLength);
+
+ // The below lines also works, but the IDE will complain about the while loop
+// DigestInputStream digestInputStream = new DigestInputStream(inputStream, digest);
+// while(digestInputStream.read() > 0)
+// ;
+
+ return digest.digest();
+ } catch (NoSuchAlgorithmException ignored) {
+ // this shouldn't happen
+ return new byte[SHA256_LENGTH];
+ }
+ }
+
+}
diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/inc/identifier/SingleHashIdentifier.java b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/SingleHashIdentifier.java
index 50b23bb..62ba47c 100644
--- a/src/main/java/com/keuin/kbackupfabric/util/backup/inc/identifier/SingleHashIdentifier.java
+++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/SingleHashIdentifier.java
@@ -1,6 +1,9 @@
-package com.keuin.kbackupfabric.util.backup.inc.identifier;
+package com.keuin.kbackupfabric.util.backup.incremental.identifier;
+
+import com.keuin.kbackupfabric.util.BytesUtil;
import java.io.File;
+import java.io.IOException;
import java.util.Arrays;
/**
@@ -20,7 +23,12 @@ public abstract class SingleHashIdentifier implements ObjectIdentifier {
* @param file the file to be hashed.
* @return the hash bytes.
*/
- protected abstract byte[] hash(File file);
+ protected abstract byte[] hash(File file) throws IOException;
+
+ @Override
+ public String getIdentification() {
+ return BytesUtil.bytesToHex(hash);
+ }
@Override
public boolean equals(Object obj) {
diff --git a/src/test/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/Sha256IdentifierTest.java b/src/test/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/Sha256IdentifierTest.java
new file mode 100644
index 0000000..2296e01
--- /dev/null
+++ b/src/test/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/Sha256IdentifierTest.java
@@ -0,0 +1,23 @@
+package com.keuin.kbackupfabric.util.backup.incremental.identifier;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+public class Sha256IdentifierTest {
+
+ @Test
+ public void fromFile() {
+ try {
+ Sha256Identifier sha256 = Sha256Identifier.fromFile(new File("./src/test/sha256"));
+ String str = sha256.getIdentification().toUpperCase();
+ assertEquals("315F5BDB76D078C43B8AC0064E4A0164612B1FCE77C869345BFC94C75894EDD3", str);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/test/sha256 b/src/test/sha256
new file mode 100644
index 0000000..5dd01c1
--- /dev/null
+++ b/src/test/sha256
@@ -0,0 +1 @@
+Hello, world! \ No newline at end of file