diff options
15 files changed, 189 insertions, 23 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 index d5d766a..e0ef4f7 100644 --- a/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollection.java +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollection.java @@ -1,31 +1,50 @@ package com.keuin.kbackupfabric.util.backup.incremental; -import com.keuin.kbackupfabric.util.backup.incremental.identifier.ObjectIdentifier; - -import java.util.Objects; -import java.util.Set; +import java.util.*; public class ObjectCollection { private final String name; - private final Set<ObjectIdentifier> elements; - private final Set<ObjectCollection> subCollections; + private final Map<String, ObjectElement> elements; + private final Map<String, ObjectCollection> subCollections; - ObjectCollection(String name, Set<ObjectIdentifier> elements, Set<ObjectCollection> subCollections) { + ObjectCollection(String name, Set<ObjectElement> elements, Map<String, ObjectCollection> subCollections) { this.name = Objects.requireNonNull(name); - this.elements = Objects.requireNonNull(elements); - this.subCollections = Objects.requireNonNull(subCollections); + 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<ObjectIdentifier> getElements() { - return elements; + 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 Set<ObjectCollection> getSubCollections() { - return subCollections; + public ObjectCollection getSubCollection(String name) { + return subCollections.get(name); } @Override 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 index 8b974db..be9e426 100644 --- a/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollectionFactory.java +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollectionFactory.java @@ -7,10 +7,7 @@ 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; +import java.util.*; /** * Incremental backup is implemented as git-like file collection. @@ -26,19 +23,21 @@ public class ObjectCollectionFactory <T extends ObjectIdentifier> { } public ObjectCollection fromDirectory(File directory) throws IOException { - final Set<ObjectIdentifier> subFiles = new HashSet<>(); - final Set<ObjectCollection> subCollections = new HashSet<>(); + final Set<ObjectElement> subFiles = new HashSet<>(); + final Map<String, ObjectCollection> subCollections = new HashMap<>(); 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();) { + for (Iterator<Path> iter = Files.walk(directory.toPath(), 1).iterator(); iter.hasNext();) { Path path = iter.next(); + if (Files.isSameFile(path, directory.toPath())) + continue; File file = path.toFile(); if (file.isDirectory()) { - subCollections.add(fromDirectory(file)); + subCollections.put(file.getName(), fromDirectory(file)); } else { - subFiles.add(identifierFactory.fromFile(file)); + subFiles.add(new ObjectElement(file.getName(), identifierFactory.fromFile(file))); } } 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..1232fb9 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectElement.java @@ -0,0 +1,59 @@ +package com.keuin.kbackupfabric.util.backup.incremental; + +import com.keuin.kbackupfabric.util.backup.incremental.identifier.ObjectIdentifier; + +import java.util.Objects; + +/** + * Representing a file in a ObjectCollection. + * Immutable. + */ +public class ObjectElement { + 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.name) && + identifier.equals(that.identifier); + } + + @Override + public int hashCode() { + return Objects.hash(name, identifier); + } + + @Override + public String toString() { + return "ObjectElement{" + + "name='" + name + '\'' + + ", identifier=" + identifier + + '}'; + } +} 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 index 62798e1..6744616 100644 --- 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 @@ -4,6 +4,7 @@ package com.keuin.kbackupfabric.util.backup.incremental.identifier; * 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 { 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 index 9f56b0e..64716ed 100644 --- 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 @@ -7,6 +7,10 @@ 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 int SHA256_LENGTH = 32; 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 index 62ba47c..3b96f79 100644 --- 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 @@ -8,6 +8,7 @@ import java.util.Arrays; /** * A simple identifier based on a single hash function. + * Immutable. */ public abstract class SingleHashIdentifier implements ObjectIdentifier { diff --git a/src/test/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollectionFactoryTest.java b/src/test/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollectionFactoryTest.java new file mode 100644 index 0000000..60a42cf --- /dev/null +++ b/src/test/java/com/keuin/kbackupfabric/util/backup/incremental/ObjectCollectionFactoryTest.java @@ -0,0 +1,81 @@ +package com.keuin.kbackupfabric.util.backup.incremental; + +import com.keuin.kbackupfabric.util.backup.incremental.identifier.Sha256Identifier; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +import static org.junit.Assert.*; + +public class ObjectCollectionFactoryTest { + + private void validate(ObjectCollection collection, List<String> subCollections, Map<String, String> subElements) { + assertEquals(subCollections.size(), collection.getSubCollectionMap().size()); + assertEquals(subElements.size(), collection.getElementSet().size()); + for (Map.Entry<String, ObjectCollection> c : collection.getSubCollectionMap().entrySet()) { + assertEquals(c.getKey(), c.getValue().getName()); + assertTrue(subCollections.contains(c.getKey())); + } + for (Map.Entry<String, ObjectElement> entry : collection.getElementMap().entrySet()) { +// assertTrue(subElements.contains(e.getIdentification())); + assertEquals(subElements.get(entry.getKey()), entry.getValue().getIdentifier().getIdentification()); + } + } + + @Test + public void fromDirectory() { + try { + ObjectCollectionFactory<Sha256Identifier> factory = + new ObjectCollectionFactory<>(Sha256Identifier.getFactory()); + ObjectCollection collection = + factory.fromDirectory(new File("./testfile/ObjectCollectionFactoryTest")); + + assertEquals("ObjectCollectionFactoryTest", collection.getName()); + assertEquals(3, collection.getSubCollectionMap().size()); + assertEquals(2, collection.getElementSet().size()); + + final Map<String, String> elements = new HashMap<>(); + + // check root dir + elements.put("a", "261CA0D59FEE8FD169802BB8030A07CF23E5C1593FA81A16C6D0A8CF27DAA2ED"); + elements.put("b", "B3FED75012C4969DC63A50EBC4E745FF77E4A06E0B04720EF71EF033032EBAF7"); + validate(collection, Arrays.asList("1", "2", "3"), elements); + elements.clear(); + + // check `1` + elements.put("a", "E8620F35A5DB33B1257CC51245DDACDA8AF3E0D431A8A38473575E468BCBD0BD"); + elements.put("b", "19EE41585A674274891DE5A4B365DBAB9C49C576AB6F86CD515B683724D2DBBD"); + validate(collection.getSubCollectionMap().get("1"), Arrays.asList("11", "12"), elements); + elements.clear(); + + // check `2` + validate(collection.getSubCollectionMap().get("2"), Collections.emptyList(), Collections.emptyMap()); + + // check `3` + validate(collection.getSubCollectionMap().get("3"), Collections.emptyList(), Collections.emptyMap()); + + // check `11` + validate(collection.getSubCollectionMap().get("1").getSubCollectionMap().get("11"), Collections.singletonList("111"), Collections.emptyMap()); + + // check `111` + elements.put("a", "1EDBE882A757E1FAFCA77A9D3BE3FF5D2BB3E2037B238C865F1F957C431F43B4"); + elements.put("b", "30BA7CD8B4AD93A8B3826CD8D1518790924EEBB930EC04DF7DFB03A50B17D7BC"); + validate( + collection.getSubCollectionMap().get("1").getSubCollectionMap().get("11").getSubCollectionMap().get("111"), + Collections.emptyList(), + elements + ); + elements.clear(); + + // check `12` + validate(collection.getSubCollectionMap().get("1").getSubCollectionMap().get("12"), Collections.emptyList(), Collections.emptyMap()); + + + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } +}
\ No newline at end of file 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 index 2296e01..53e92e2 100644 --- 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 @@ -6,17 +6,19 @@ import java.io.File; import java.io.IOException; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; public class Sha256IdentifierTest { @Test public void fromFile() { try { - Sha256Identifier sha256 = Sha256Identifier.fromFile(new File("./src/test/sha256")); + Sha256Identifier sha256 = Sha256Identifier.fromFile(new File("./testfile/Sha256IdentifierTest")); String str = sha256.getIdentification().toUpperCase(); assertEquals("315F5BDB76D078C43B8AC0064E4A0164612B1FCE77C869345BFC94C75894EDD3", str); } catch (IOException e) { e.printStackTrace(); + fail(); } } diff --git a/testfile/ObjectCollectionFactoryTest/1/11/111/a b/testfile/ObjectCollectionFactoryTest/1/11/111/a Binary files differnew file mode 100644 index 0000000..13a4f6c --- /dev/null +++ b/testfile/ObjectCollectionFactoryTest/1/11/111/a diff --git a/testfile/ObjectCollectionFactoryTest/1/11/111/b b/testfile/ObjectCollectionFactoryTest/1/11/111/b Binary files differnew file mode 100644 index 0000000..52755f5 --- /dev/null +++ b/testfile/ObjectCollectionFactoryTest/1/11/111/b diff --git a/testfile/ObjectCollectionFactoryTest/1/a b/testfile/ObjectCollectionFactoryTest/1/a Binary files differnew file mode 100644 index 0000000..4936a74 --- /dev/null +++ b/testfile/ObjectCollectionFactoryTest/1/a diff --git a/testfile/ObjectCollectionFactoryTest/1/b b/testfile/ObjectCollectionFactoryTest/1/b Binary files differnew file mode 100644 index 0000000..7c0897e --- /dev/null +++ b/testfile/ObjectCollectionFactoryTest/1/b diff --git a/testfile/ObjectCollectionFactoryTest/a b/testfile/ObjectCollectionFactoryTest/a Binary files differnew file mode 100644 index 0000000..210ecec --- /dev/null +++ b/testfile/ObjectCollectionFactoryTest/a diff --git a/testfile/ObjectCollectionFactoryTest/b b/testfile/ObjectCollectionFactoryTest/b Binary files differnew file mode 100644 index 0000000..344e596 --- /dev/null +++ b/testfile/ObjectCollectionFactoryTest/b diff --git a/src/test/sha256 b/testfile/Sha256IdentifierTest index 5dd01c1..5dd01c1 100644 --- a/src/test/sha256 +++ b/testfile/Sha256IdentifierTest |