summaryrefslogtreecommitdiff
path: root/src/main/java/com/keuin/kbackupfabric/util/backup/incremental
diff options
context:
space:
mode:
authorKeuin <[email protected]>2021-01-23 14:10:32 +0800
committerkeuin <[email protected]>2021-01-23 14:10:32 +0800
commit4a1d885afa7217b47d6183488c3dc6537cef05b6 (patch)
tree1b499db6b834cb0709029e30c0d52c0ddf200ffa /src/main/java/com/keuin/kbackupfabric/util/backup/incremental
parent4ac575330130ac4e1b4b35386ffc0aacd431a5a4 (diff)
Version 1.4.6 (preview): added metadata for incremental backup (need integrated test and display implementation)
Diffstat (limited to 'src/main/java/com/keuin/kbackupfabric/util/backup/incremental')
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollection.java73
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectElement.java61
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/ObjectIdentifier.java13
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/Sha256Identifier.java84
-rw-r--r--src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/SingleHashIdentifier.java53
5 files changed, 284 insertions, 0 deletions
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..d36ff59
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollection.java
@@ -0,0 +1,73 @@
+package com.keuin.kbackupfabric.util.backup.incremental;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * This class must be in package `com.keuin.kbackupfabric.util.backup.incremental.ObjectCollection`,
+ * or it will not be compatible with old backups.
+ * It remains only to keep a backward compatibility, and should be converted to `ObjectCollection2` as soon as possible.
+ */
+@Deprecated
+public class ObjectCollection implements Serializable {
+
+ private static final long serialVersionUID = -3098905094513096717L;
+ private final String name;
+ private final Map<String, ObjectElement> elements;
+ private final Map<String, ObjectCollection> subCollections;
+
+ ObjectCollection(String name, Set<ObjectElement> elements, Map<String, ObjectCollection> subCollections) {
+ this.name = Objects.requireNonNull(name);
+ this.elements = new HashMap<>();
+ for (ObjectElement e : elements) {
+ Objects.requireNonNull(e);
+ if (this.elements.put(e.getName(), e) != null) {
+ throw new IllegalStateException("elements conflict with the same name");
+ }
+ }
+ this.subCollections = new HashMap<>(Objects.requireNonNull(subCollections));
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Set<ObjectElement> getElementSet() {
+ return new HashSet<>(elements.values());
+ }
+
+ public Map<String, ObjectElement> getElementMap() {
+ return Collections.unmodifiableMap(elements);
+ }
+
+ public ObjectElement getElement(String name) {
+ return elements.get(name);
+ }
+
+ public Set<ObjectCollection> getSubCollectionSet() {
+ return new HashSet<>(subCollections.values());
+ }
+
+ public Map<String, ObjectCollection> getSubCollectionMap() {
+ return Collections.unmodifiableMap(subCollections);
+ }
+
+ public ObjectCollection getSubCollection(String name) {
+ return subCollections.get(name);
+ }
+
+ @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/ObjectElement.java b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectElement.java
new file mode 100644
index 0000000..a101fd3
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectElement.java
@@ -0,0 +1,61 @@
+package com.keuin.kbackupfabric.util.backup.incremental;
+
+import com.keuin.kbackupfabric.util.backup.incremental.identifier.ObjectIdentifier;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+public class ObjectElement implements Serializable {
+
+ private static final long serialVersionUID = 268304683651745899L;
+ private final String name;
+ private final ObjectIdentifier identifier;
+
+ public ObjectElement(String name, ObjectIdentifier identifier) {
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(identifier);
+ this.name = name;
+ this.identifier = identifier;
+ }
+
+ /**
+ * Get file name.
+ *
+ * @return the file name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get file identifier, which is considered to be different between files with different contents.
+ *
+ * @return the identifier.
+ */
+ public ObjectIdentifier getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ObjectElement that = (ObjectElement) o;
+ return name.equals(that.getName()) &&
+ identifier.equals(that.getIdentifier());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, identifier);
+ }
+
+ @Override
+ public String toString() {
+ return "ObjectElement{" +
+ "name='" + name + '\'' +
+ ", identifier=" + identifier +
+ '}';
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/ObjectIdentifier.java b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/ObjectIdentifier.java
new file mode 100644
index 0000000..aece07d
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/ObjectIdentifier.java
@@ -0,0 +1,13 @@
+package com.keuin.kbackupfabric.util.backup.incremental.identifier;
+
+import java.io.Serializable;
+
+/**
+ * The identifier distinguishing files in the object collection.
+ * It should be based on cryptographic hash function in order to prevent possible attacks to the backup system.
+ * All identifiers should be immutable and implement their own equals method.
+ * Immutable.
+ */
+public interface ObjectIdentifier extends Serializable {
+ 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..31cde29
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/Sha256Identifier.java
@@ -0,0 +1,84 @@
+package com.keuin.kbackupfabric.util.backup.incremental.identifier;
+
+import com.keuin.kbackupfabric.util.BytesUtil;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
+
+/**
+ * Identifier based on sha256.
+ * Immutable.
+ */
+public class Sha256Identifier extends SingleHashIdentifier {
+
+ private static final long serialVersionUID = 968324214777435054L;
+ 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 String marker = "S2";
+
+ public static Sha256Identifier fromFile(File file) throws IOException {
+ if (!file.isFile()) {
+ throw new IllegalArgumentException("file is not a file");
+ }
+ return new Sha256Identifier(DUMMY.hash(file));
+ }
+
+ /**
+ * Load sha-256 from a named file. Only used in StorageObjectLoader.
+ *
+ * @param fileName the file name.
+ * @return identifier.
+ */
+ static Sha256Identifier fromFileName(String fileName) {
+ if (!fileName.matches(marker + "-[0-9A-Fa-f]{32}"))
+ return null;
+ String hexString = fileName.substring(marker.length() + 1);
+ return new Sha256Identifier(BytesUtil.hexToBytes(hexString));
+ }
+
+ protected Sha256Identifier(byte[] hash) {
+ super(hash, marker);
+ 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");
+
+ try (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];
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/SingleHashIdentifier.java b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/SingleHashIdentifier.java
new file mode 100644
index 0000000..6c67918
--- /dev/null
+++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/identifier/SingleHashIdentifier.java
@@ -0,0 +1,53 @@
+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;
+import java.util.Objects;
+
+/**
+ * A simple identifier based on a single hash function.
+ * Immutable.
+ */
+public abstract class SingleHashIdentifier implements ObjectIdentifier {
+
+ private final byte[] hash;
+ private final String type;
+
+ protected SingleHashIdentifier(byte[] hash, String type) {
+ Objects.requireNonNull(hash);
+ Objects.requireNonNull(type);
+ this.hash = Arrays.copyOf(hash, hash.length);
+ this.type = type;
+ }
+
+ /**
+ * The hash function.
+ *
+ * @param file the file to be hashed.
+ * @return the hash bytes.
+ */
+ protected abstract byte[] hash(File file) throws IOException;
+
+ @Override
+ public String getIdentification() {
+ return type + "-" + BytesUtil.bytesToHex(hash);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof com.keuin.kbackupfabric.backup.incremental.identifier.SingleHashIdentifier)) {
+ return false;
+ }
+ return Arrays.equals(hash, ((SingleHashIdentifier) obj).hash);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(type);
+ result = 31 * result + Arrays.hashCode(hash);
+ return result;
+ }
+}