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