diff --git a/dhfs-parent/kleppmanntree/pom.xml b/dhfs-parent/kleppmanntree/pom.xml
new file mode 100644
index 00000000..3580a911
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/pom.xml
@@ -0,0 +1,35 @@
+
+
+ 4.0.0
+
+ com.usatiuk.dhfs
+ parent
+ 1.0-SNAPSHOT
+
+
+ com.usatiuk
+ kleppmanntree
+
+
+ 21
+ 21
+ UTF-8
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.32
+ provided
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.9.1
+ test
+
+
+
\ No newline at end of file
diff --git a/dhfs-parent/kleppmanntree/src/lombok.config b/dhfs-parent/kleppmanntree/src/lombok.config
new file mode 100644
index 00000000..f1c474ce
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/lombok.config
@@ -0,0 +1 @@
+lombok.accessors.prefix += _
diff --git a/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/Clock.java b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/Clock.java
new file mode 100644
index 00000000..5a4e4e3b
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/Clock.java
@@ -0,0 +1,7 @@
+package com.usatiuk.kleppmanntree;
+
+public interface Clock> {
+ TimestampT getTimestamp();
+
+ void updateTimestamp(TimestampT receivedTimestamp);
+}
diff --git a/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/CombinedTimestamp.java b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/CombinedTimestamp.java
new file mode 100644
index 00000000..9a43d927
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/CombinedTimestamp.java
@@ -0,0 +1,15 @@
+package com.usatiuk.kleppmanntree;
+
+import java.util.Comparator;
+
+public record CombinedTimestamp, PeerIdT extends Comparable>
+ (TimestampT timestamp, PeerIdT nodeId) implements Comparable> {
+
+
+ @Override
+ public int compareTo(CombinedTimestamp o) {
+ return Comparator.comparing((CombinedTimestamp t) -> t.timestamp)
+ .thenComparing((CombinedTimestamp t) -> t.nodeId)
+ .compare(this, o);
+ }
+}
diff --git a/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/KleppmannTree.java b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/KleppmannTree.java
new file mode 100644
index 00000000..7e1bb94b
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/KleppmannTree.java
@@ -0,0 +1,173 @@
+package com.usatiuk.kleppmanntree;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+public class KleppmannTree, PeerIdT extends Comparable, NameT, MetaT extends NodeMeta, NodeIdT, WrapperT extends TreeNodeWrapper> {
+ private final StorageInterface _storage;
+ private final PeerInterface _peers;
+ private final Clock _clock;
+
+ public KleppmannTree(StorageInterface storage,
+ PeerInterface peers,
+ Clock clock) {
+ _storage = storage;
+ _peers = peers;
+ _clock = clock;
+ }
+
+
+ public NodeIdT traverse(NodeIdT fromId, List names) {
+ if (names.isEmpty()) return fromId;
+
+ var from = _storage.getById(fromId);
+ from.rLock();
+ NodeIdT childId;
+ try {
+ childId = from.getNode().getChildren().get(names.getFirst());
+ } finally {
+ from.rwUnlock();
+ }
+
+ if (childId == null)
+ return null;
+
+ return traverse(childId, names.subList(1, names.size()));
+ }
+
+ public NodeIdT traverse(List names) {
+ return traverse(_storage.getRootId(), names);
+ }
+
+ private void undoOp(LogOpMove op) {
+ if (op.oldInfo() != null) {
+ var node = _storage.getById(op.op().childId());
+ var oldParent = _storage.getById(op.oldInfo().oldParent());
+ var curParent = _storage.getById(op.op().newParentId());
+ curParent.rwLock();
+ oldParent.rwLock();
+ node.rwLock();
+ try {
+ curParent.getNode().getChildren().remove(node.getNode().getMeta().getName());
+ node.getNode().setMeta(op.oldInfo().oldMeta());
+ node.getNode().setParent(oldParent.getNode().getId());
+ oldParent.getNode().getChildren().put(node.getNode().getMeta().getName(), node.getNode().getId());
+ } finally {
+ node.rwUnlock();
+ oldParent.rwUnlock();
+ curParent.rwUnlock();
+ }
+ } else {
+ var node = _storage.getById(op.op().childId());
+ var curParent = _storage.getById(op.op().newParentId());
+ curParent.rwLock();
+ node.rwLock();
+ try {
+ curParent.getNode().getChildren().remove(node.getNode().getMeta().getName());
+ _storage.removeNode(node.getNode().getId());
+ } finally {
+ node.rwUnlock();
+ curParent.rwUnlock();
+ }
+ }
+ }
+
+ private void redoOp(Map.Entry, LogOpMove> entry) {
+ entry.setValue(doOp(entry.getValue().op()));
+ }
+
+ public void applyOp(OpMove op) {
+ _clock.updateTimestamp(op.timestamp().timestamp());
+ var log = _storage.getLog();
+ if (log.isEmpty()) {
+ log.put(op.timestamp(), doOp(op));
+ return;
+ }
+ var cmp = op.timestamp().compareTo(log.lastEntry().getKey());
+ assert cmp != 0;
+ if (cmp < 0) {
+ _storage.globalLock();
+ try {
+ var toUndo = log.tailMap(op.timestamp(), false);
+ for (var entry : toUndo.reversed().entrySet()) {
+ undoOp(entry.getValue());
+ }
+ log.put(op.timestamp(), doOp(op));
+ for (var entry : toUndo.entrySet()) {
+ redoOp(entry);
+ }
+ } finally {
+ _storage.globalUnlock();
+ }
+ } else {
+ log.put(op.timestamp(), doOp(op));
+ }
+ }
+
+ private LogOpMove doOp(OpMove op) {
+ var node = _storage.getById(op.childId());
+ if (node == null) {
+ node = _storage.createNewNode(op.childId());
+ }
+
+ var oldParent = node.getNode().getParent() != null ? _storage.getById(node.getNode().getParent()) : null;
+ var newParent = _storage.getById(op.newParentId());
+
+ if (newParent == null) {
+ throw new IllegalArgumentException("New parent not found");
+ }
+
+ if (oldParent == null) {
+ newParent.rwLock();
+ try {
+ node.rwLock();
+ try {
+ node.getNode().setMeta(op.newMeta());
+ node.getNode().setParent(op.newParentId());
+ newParent.getNode().getChildren().put(node.getNode().getMeta().getName(), node.getNode().getId());
+ return new LogOpMove<>(null, op);
+ } finally {
+ node.rwUnlock();
+ }
+ } finally {
+ newParent.rwUnlock();
+ }
+ }
+
+ // FIXME:
+ _storage.globalLock();
+ try {
+ if (op.childId() == op.newParentId() || isAncestor(op.childId(), op.newParentId()))
+ return new LogOpMove<>(null, op);
+
+ newParent.rwLock();
+ oldParent.rwLock();
+ node.rwLock();
+
+ try {
+ oldParent.getNode().getChildren().remove(node.getNode().getMeta().getName());
+ node.getNode().setMeta(op.newMeta());
+ node.getNode().setParent(newParent.getNode().getId());
+ newParent.getNode().getChildren().put(op.newMeta().getName(), node.getNode().getId());
+ return new LogOpMove<>(new LogOpMoveOld<>(oldParent.getNode().getId(), node.getNode().getMeta()), op);
+ } finally {
+ node.rwUnlock();
+ oldParent.rwUnlock();
+ newParent.rwUnlock();
+ }
+ } finally {
+ _storage.globalUnlock();
+ }
+ }
+
+ private boolean isAncestor(NodeIdT child, NodeIdT parent) {
+ var node = _storage.getById(parent);
+ NodeIdT curParent = null;
+ while ((curParent = node.getNode().getParent()) != null) {
+ if (Objects.equals(child, curParent)) return true;
+ node = _storage.getById(curParent);
+ }
+ return false;
+ }
+}
diff --git a/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/LogOpMove.java b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/LogOpMove.java
new file mode 100644
index 00000000..301ecfa0
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/LogOpMove.java
@@ -0,0 +1,5 @@
+package com.usatiuk.kleppmanntree;
+
+public record LogOpMove, PeerIdT extends Comparable, NameT, MetaT extends NodeMeta, NodeIdT>
+ (LogOpMoveOld oldInfo,
+ OpMove op) {}
diff --git a/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/LogOpMoveOld.java b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/LogOpMoveOld.java
new file mode 100644
index 00000000..ca95fb4b
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/LogOpMoveOld.java
@@ -0,0 +1,4 @@
+package com.usatiuk.kleppmanntree;
+
+public record LogOpMoveOld, NodeIdT>
+ (NodeIdT oldParent, MetaT oldMeta) {}
diff --git a/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/NodeMeta.java b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/NodeMeta.java
new file mode 100644
index 00000000..1f7a0eb7
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/NodeMeta.java
@@ -0,0 +1,5 @@
+package com.usatiuk.kleppmanntree;
+
+public interface NodeMeta {
+ public NameT getName();
+}
diff --git a/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/OpMove.java b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/OpMove.java
new file mode 100644
index 00000000..48c1af69
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/OpMove.java
@@ -0,0 +1,4 @@
+package com.usatiuk.kleppmanntree;
+
+public record OpMove, PeerIdT extends Comparable, NameT, MetaT extends NodeMeta, NodeIdT>
+ (CombinedTimestamp timestamp, NodeIdT newParentId, MetaT newMeta, NodeIdT childId) {}
diff --git a/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/PeerInterface.java b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/PeerInterface.java
new file mode 100644
index 00000000..432353f1
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/PeerInterface.java
@@ -0,0 +1,8 @@
+package com.usatiuk.kleppmanntree;
+
+import java.util.Collection;
+
+public interface PeerInterface> {
+ public PeerIdT getSelfId();
+ public Collection getAllPeers();
+}
diff --git a/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/StorageInterface.java b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/StorageInterface.java
new file mode 100644
index 00000000..38b7dc7e
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/StorageInterface.java
@@ -0,0 +1,34 @@
+package com.usatiuk.kleppmanntree;
+
+import java.util.Collection;
+import java.util.NavigableMap;
+
+public interface StorageInterface<
+ TimestampT extends Comparable,
+ PeerIdT extends Comparable,
+ NameT,
+ MetaT extends NodeMeta,
+ NodeIdT,
+ WrapperT extends TreeNodeWrapper> {
+ NodeIdT getRootId();
+
+ NodeIdT getTrashId();
+
+ NodeIdT getNewNodeId();
+
+ WrapperT getById(NodeIdT id);
+
+ WrapperT createNewNode(NodeIdT id);
+
+ void removeNode(NodeIdT id);
+
+ void lockSet(Collection nodes);
+
+ // It is expected that the map allows concurrent additions at the end
+ NavigableMap, LogOpMove> getLog();
+
+ // Locks all the objects from being changed
+ void globalLock();
+
+ void globalUnlock();
+}
diff --git a/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/TreeNode.java b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/TreeNode.java
new file mode 100644
index 00000000..62be2183
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/TreeNode.java
@@ -0,0 +1,18 @@
+package com.usatiuk.kleppmanntree;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Getter
+@Setter
+public class TreeNode, NodeIdT> {
+ private NodeIdT _parent = null;
+ private final NodeIdT _id;
+ private MetaT _meta = null;
+ private Map _children = new HashMap<>();
+
+ public TreeNode(NodeIdT id) {_id = id;}
+}
diff --git a/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/TreeNodeWrapper.java b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/TreeNodeWrapper.java
new file mode 100644
index 00000000..ceb82cf9
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/main/java/com/usatiuk/kleppmanntree/TreeNodeWrapper.java
@@ -0,0 +1,13 @@
+package com.usatiuk.kleppmanntree;
+
+public interface TreeNodeWrapper, NodeIdT> {
+ void rLock();
+
+ void rUnlock();
+
+ void rwLock();
+
+ void rwUnlock();
+
+ TreeNode getNode();
+}
diff --git a/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/KleppmanTreeSimpleTest.java b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/KleppmanTreeSimpleTest.java
new file mode 100644
index 00000000..54ae29e7
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/KleppmanTreeSimpleTest.java
@@ -0,0 +1,67 @@
+package com.usatiuk.kleppmanntree;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+public class KleppmanTreeSimpleTest {
+ private final TestNode testNode1 = new TestNode(1);
+ private final TestNode testNode2 = new TestNode(2);
+ private final TestNode testNode3 = new TestNode(3);
+ private final TestNode testNode4 = new TestNode(4);
+
+
+ @Test
+ void circularTest() {
+ var d1id = testNode1._storageInterface.getNewNodeId();
+ var d2id = testNode2._storageInterface.getNewNodeId();
+ var op1 = new OpMove<>(new CombinedTimestamp<>(testNode1._clock.getTimestamp(), 1L),
+ testNode1._storageInterface.getRootId(),
+ new TestNodeMetaDir("Test1"),
+ d1id);
+ var op2 = new OpMove<>(new CombinedTimestamp<>(testNode2._clock.getTimestamp(), 2L),
+ testNode2._storageInterface.getRootId(),
+ new TestNodeMetaDir("Test2"),
+ d2id);
+ testNode1._tree.applyOp(op1);
+ testNode2._tree.applyOp(op2);
+ testNode1._tree.applyOp(op2);
+ testNode2._tree.applyOp(op1);
+
+ Assertions.assertEquals(d1id, testNode1._tree.traverse(List.of("Test1")));
+ Assertions.assertEquals(d2id, testNode1._tree.traverse(List.of("Test2")));
+ Assertions.assertEquals(d1id, testNode2._tree.traverse(List.of("Test1")));
+ Assertions.assertEquals(d2id, testNode2._tree.traverse(List.of("Test2")));
+
+ Assertions.assertIterableEquals(List.of("Test1", "Test2"), testNode1._storageInterface.getById(testNode2._storageInterface.getRootId()).getNode().getChildren().keySet());
+ Assertions.assertIterableEquals(List.of("Test1", "Test2"), testNode2._storageInterface.getById(testNode2._storageInterface.getRootId()).getNode().getChildren().keySet());
+
+ var cop1 = new OpMove<>(new CombinedTimestamp<>(testNode1._clock.getTimestamp(), 1L),
+ d1id,
+ new TestNodeMetaDir("Test2"),
+ d2id);
+ testNode1._tree.applyOp(cop1);
+ Assertions.assertEquals(d1id, testNode1._tree.traverse(List.of("Test1")));
+ Assertions.assertEquals(d2id, testNode1._tree.traverse(List.of("Test1", "Test2")));
+ Assertions.assertIterableEquals(List.of("Test1"), testNode1._storageInterface.getById(testNode2._storageInterface.getRootId()).getNode().getChildren().keySet());
+
+ var cop2 = new OpMove<>(new CombinedTimestamp<>(testNode2._clock.getTimestamp(), 2L),
+ d2id,
+ new TestNodeMetaDir("Test1"),
+ d1id);
+ testNode2._tree.applyOp(cop2);
+ Assertions.assertIterableEquals(List.of("Test2"), testNode2._storageInterface.getById(testNode2._storageInterface.getRootId()).getNode().getChildren().keySet());
+ Assertions.assertEquals(d2id, testNode2._tree.traverse(List.of("Test2")));
+ Assertions.assertEquals(d1id, testNode2._tree.traverse(List.of("Test2", "Test1")));
+
+ testNode1._tree.applyOp(cop2);
+ testNode2._tree.applyOp(cop1);
+ // First node wins as it has smaller timestamp
+ Assertions.assertIterableEquals(List.of("Test1"), testNode1._storageInterface.getById(testNode2._storageInterface.getRootId()).getNode().getChildren().keySet());
+ Assertions.assertIterableEquals(List.of("Test2"), testNode1._storageInterface.getById(d1id).getNode().getChildren().keySet());
+ Assertions.assertEquals(d1id, testNode1._tree.traverse(List.of("Test1")));
+ Assertions.assertEquals(d2id, testNode1._tree.traverse(List.of("Test1", "Test2")));
+ }
+
+}
diff --git a/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestClock.java b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestClock.java
new file mode 100644
index 00000000..85c5c03d
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestClock.java
@@ -0,0 +1,15 @@
+package com.usatiuk.kleppmanntree;
+
+public class TestClock implements Clock {
+ private long max = 0;
+
+ @Override
+ public Long getTimestamp() {
+ return max++;
+ }
+
+ @Override
+ public void updateTimestamp(Long receivedTimestamp) {
+ max = Math.max(max, receivedTimestamp) + 1;
+ }
+}
diff --git a/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNode.java b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNode.java
new file mode 100644
index 00000000..ae95537d
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNode.java
@@ -0,0 +1,18 @@
+package com.usatiuk.kleppmanntree;
+
+public class TestNode {
+ protected final long _id;
+
+ protected final TestClock _clock;
+ protected final TestPeerInterface _peerInterface;
+ protected final TestStorageInterface _storageInterface;
+ protected final KleppmannTree _tree;
+
+ public TestNode(long id) {
+ _id = id;
+ _clock = new TestClock();
+ _peerInterface = new TestPeerInterface(_id);
+ _storageInterface = new TestStorageInterface(_id);
+ _tree = new KleppmannTree<>(_storageInterface, _peerInterface, _clock);
+ }
+}
diff --git a/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNodeMetaDir.java b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNodeMetaDir.java
new file mode 100644
index 00000000..d9025397
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNodeMetaDir.java
@@ -0,0 +1,12 @@
+package com.usatiuk.kleppmanntree;
+
+public class TestNodeMetaDir implements NodeMeta {
+ private final String name;
+
+ public TestNodeMetaDir(String name) {this.name = name;}
+
+ @Override
+ public String getName() {
+ return name;
+ }
+}
diff --git a/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNodeMetaFile.java b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNodeMetaFile.java
new file mode 100644
index 00000000..e16ea741
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNodeMetaFile.java
@@ -0,0 +1,19 @@
+package com.usatiuk.kleppmanntree;
+
+import lombok.Getter;
+
+public class TestNodeMetaFile implements NodeMeta {
+ private final String name;
+ @Getter
+ private final long inode;
+
+ public TestNodeMetaFile(String name, long inode) {
+ this.name = name;
+ this.inode = inode;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+}
diff --git a/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNodeWrapper.java b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNodeWrapper.java
new file mode 100644
index 00000000..d7d74e70
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestNodeWrapper.java
@@ -0,0 +1,32 @@
+package com.usatiuk.kleppmanntree;
+
+public class TestNodeWrapper implements TreeNodeWrapper {
+ private final TreeNode _backingNode;
+
+ public TestNodeWrapper(TreeNode backingNode) {_backingNode = backingNode;}
+
+ @Override
+ public void rLock() {
+
+ }
+
+ @Override
+ public void rUnlock() {
+
+ }
+
+ @Override
+ public void rwLock() {
+
+ }
+
+ @Override
+ public void rwUnlock() {
+
+ }
+
+ @Override
+ public TreeNode getNode() {
+ return _backingNode;
+ }
+}
diff --git a/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestPeerInterface.java b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestPeerInterface.java
new file mode 100644
index 00000000..22a5e45a
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestPeerInterface.java
@@ -0,0 +1,20 @@
+package com.usatiuk.kleppmanntree;
+
+import java.util.Collection;
+import java.util.List;
+
+public class TestPeerInterface implements PeerInterface {
+ private final long selfId;
+
+ public TestPeerInterface(long selfId) {this.selfId = selfId;}
+
+ @Override
+ public Long getSelfId() {
+ return selfId;
+ }
+
+ @Override
+ public Collection getAllPeers() {
+ return List.of(1L, 2L, 3L, 4L);
+ }
+}
diff --git a/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestStorageInterface.java b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestStorageInterface.java
new file mode 100644
index 00000000..16f23eb6
--- /dev/null
+++ b/dhfs-parent/kleppmanntree/src/test/java/com/usatiuk/kleppmanntree/TestStorageInterface.java
@@ -0,0 +1,75 @@
+package com.usatiuk.kleppmanntree;
+
+import java.util.*;
+
+public class TestStorageInterface implements StorageInterface {
+ private long _curId = 1;
+ private final long _peerId;
+
+ private final Map> _nodes = new HashMap<>();
+ private final NavigableMap, LogOpMove> _log = new TreeMap<>();
+
+ public TestStorageInterface(long peerId) {
+ _peerId = peerId;
+ _nodes.put(getRootId(), new TreeNode<>(getRootId()));
+ _nodes.put(getTrashId(), new TreeNode<>(getTrashId()));
+ }
+
+ @Override
+ public Long getRootId() {
+ return 0L;
+ }
+
+ @Override
+ public Long getTrashId() {
+ return -1L;
+ }
+
+ @Override
+ public Long getNewNodeId() {
+ return _curId++ | _peerId << 32;
+ }
+
+ @Override
+ public TestNodeWrapper getById(Long id) {
+ var node = _nodes.get(id);
+ return node == null ? null : new TestNodeWrapper(node);
+ }
+
+ @Override
+ public TestNodeWrapper createNewNode(Long id) {
+ if (!_nodes.containsKey(id)) {
+ var newNode = new TreeNode(id);
+ _nodes.put(id, newNode);
+ return new TestNodeWrapper(newNode);
+ }
+ throw new IllegalStateException("Node with id " + id + " already exists");
+ }
+
+ @Override
+ public void removeNode(Long id) {
+ if (!_nodes.containsKey(id))
+ throw new IllegalStateException("Node with id " + id + " doesn't exist");
+ _nodes.remove(id);
+ }
+
+ @Override
+ public void lockSet(Collection nodes) {
+
+ }
+
+ @Override
+ public NavigableMap, LogOpMove> getLog() {
+ return _log;
+ }
+
+ @Override
+ public void globalLock() {
+
+ }
+
+ @Override
+ public void globalUnlock() {
+
+ }
+}
diff --git a/dhfs-parent/pom.xml b/dhfs-parent/pom.xml
index 34dad8f4..9a4079cf 100644
--- a/dhfs-parent/pom.xml
+++ b/dhfs-parent/pom.xml
@@ -12,6 +12,7 @@
pom
server
+ kleppmanntree