From afa32431d242d60471e5431d654784ee64b63bbf Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Fri, 27 Dec 2019 11:28:46 +0100 Subject: Begin Stanik impelementation * Get cloned hierarchy * Test on empty hierarchy --- .../agent/messages/GetHierarchyMessage.java | 22 ++++++++++ .../agent/messages/HierarchyMessage.java | 17 ++++++++ .../cloudatlas/agent/messages/ResponseMessage.java | 31 ++++++++++++++ .../cloudatlas/agent/messages/StanikMessage.java | 25 +++++++++++ .../edu/mimuw/cloudatlas/agent/modules/Module.java | 10 +++++ .../mimuw/cloudatlas/agent/modules/ModuleType.java | 4 +- .../edu/mimuw/cloudatlas/agent/modules/Stanik.java | 31 ++++++++++++++ .../edu/mimuw/cloudatlas/agent/MockExecutor.java | 24 +++++++++++ .../mimuw/cloudatlas/agent/modules/StanikTest.java | 49 ++++++++++++++++++++++ 9 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetHierarchyMessage.java create mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/HierarchyMessage.java create mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/ResponseMessage.java create mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java create mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java create mode 100644 src/test/java/pl/edu/mimuw/cloudatlas/agent/MockExecutor.java create mode 100644 src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetHierarchyMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetHierarchyMessage.java new file mode 100644 index 0000000..d3c749d --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetHierarchyMessage.java @@ -0,0 +1,22 @@ +package pl.edu.mimuw.cloudatlas.agent.messages; + +import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; + +public class GetHierarchyMessage extends StanikMessage { + private ModuleType requestingModule; + private long requestId; + + public GetHierarchyMessage(String messageId, long timestamp, ModuleType requestingModule, long requestId) { + super(messageId, timestamp, Type.GET_HIERARCHY); + this.requestingModule = requestingModule; + this.requestId = requestId; + } + + public ModuleType getRequestingModule() { + return requestingModule; + } + + public long getRequestId() { + return requestId; + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/HierarchyMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/HierarchyMessage.java new file mode 100644 index 0000000..9f7ca70 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/HierarchyMessage.java @@ -0,0 +1,17 @@ +package pl.edu.mimuw.cloudatlas.agent.messages; + +import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; +import pl.edu.mimuw.cloudatlas.model.ZMI; + +public class HierarchyMessage extends ResponseMessage { + private ZMI zmi; + + public HierarchyMessage(String messageId, ModuleType destinationModule, long timestamp, long requestId, ZMI zmi) { + super(messageId, destinationModule, timestamp, Type.HIERARCHY, requestId); + this.zmi = zmi; + } + + public ZMI getZMI() { + return zmi; + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/ResponseMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/ResponseMessage.java new file mode 100644 index 0000000..171cf07 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/ResponseMessage.java @@ -0,0 +1,31 @@ +package pl.edu.mimuw.cloudatlas.agent.messages; + +import pl.edu.mimuw.cloudatlas.agent.modules.Module; +import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; + +public abstract class ResponseMessage extends AgentMessage { + public enum Type { + HIERARCHY + } + + Type type; + long requestId; + + public ResponseMessage(String messageId, ModuleType destinationModule, long timestamp, Type type, long requestId) { + super(messageId, destinationModule, timestamp); + this.type = type; + this.requestId = requestId; + } + + public void callMe(Module module) throws InterruptedException, Module.InvalidMessageType { + module.handleTyped(this); + } + + public long getRequestId() { + return requestId; + } + + public Type getType() { + return type; + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java new file mode 100644 index 0000000..d2b3064 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java @@ -0,0 +1,25 @@ +package pl.edu.mimuw.cloudatlas.agent.messages; + +import pl.edu.mimuw.cloudatlas.agent.modules.Module; +import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; + +public abstract class StanikMessage extends AgentMessage { + public enum Type { + GET_HIERARCHY + } + + private Type type; + + public StanikMessage(String messageId, long timestamp, Type type) { + super(messageId, ModuleType.STATE, timestamp); + this.type = type; + } + + public Type getType() { + return type; + } + + public void callMe(Module module) throws InterruptedException, Module.InvalidMessageType { + module.handleTyped(this); + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Module.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Module.java index a50a95f..d0bf083 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Module.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Module.java @@ -3,7 +3,9 @@ package pl.edu.mimuw.cloudatlas.agent.modules; import pl.edu.mimuw.cloudatlas.agent.Executor; import pl.edu.mimuw.cloudatlas.agent.messages.AgentMessage; import pl.edu.mimuw.cloudatlas.agent.messages.TimerSchedulerMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.ResponseMessage; import pl.edu.mimuw.cloudatlas.agent.messages.RMIMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.StanikMessage; /* * A Module is a (potentially stateful) event handler. @@ -34,6 +36,14 @@ public abstract class Module { throw new InvalidMessageType("Got an RMIMessage in module " + moduleType.toString()); } + public void handleTyped(StanikMessage message) throws InterruptedException, InvalidMessageType { + throw new InvalidMessageType("Got a StanikMessage in module " + moduleType.toString()); + } + + public void handleTyped(ResponseMessage message) throws InterruptedException, InvalidMessageType { + throw new InvalidMessageType("Got a ResponseMessage in module " + moduleType.toString()); + } + public void setExecutor(Executor executor) { this.executor = executor; } diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/ModuleType.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/ModuleType.java index ff4a92e..d221f06 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/ModuleType.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/ModuleType.java @@ -8,5 +8,7 @@ public enum ModuleType { GOSSIP_IN, GOSSIP_OUT, STATE, - QUERY + QUERY, + // for testing + TEST } diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java new file mode 100644 index 0000000..b8db08a --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java @@ -0,0 +1,31 @@ +package pl.edu.mimuw.cloudatlas.agent.modules; + +import pl.edu.mimuw.cloudatlas.agent.messages.AgentMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.GetHierarchyMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.HierarchyMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.StanikMessage; +import pl.edu.mimuw.cloudatlas.model.ZMI; + +public class Stanik extends Module { + private ZMI hierarchy; + + public Stanik() { + super(ModuleType.STATE); + hierarchy = new ZMI(); + } + + public void handleTyped(StanikMessage message) throws InterruptedException, InvalidMessageType { + switch(message.getType()) { + case GET_HIERARCHY: + handleGetHierarchy((GetHierarchyMessage) message); + break; + default: + throw new InvalidMessageType("This type of message cannot be handled by Stanik"); + } + } + + public void handleGetHierarchy(GetHierarchyMessage message) throws InterruptedException { + HierarchyMessage response = new HierarchyMessage("", message.getRequestingModule(), 0, message.getRequestId(), hierarchy.clone()); + sendMessage(response); + } +} diff --git a/src/test/java/pl/edu/mimuw/cloudatlas/agent/MockExecutor.java b/src/test/java/pl/edu/mimuw/cloudatlas/agent/MockExecutor.java new file mode 100644 index 0000000..1da50ad --- /dev/null +++ b/src/test/java/pl/edu/mimuw/cloudatlas/agent/MockExecutor.java @@ -0,0 +1,24 @@ +package pl.edu.mimuw.cloudatlas.agent; + +import java.util.concurrent.LinkedBlockingQueue; + +import pl.edu.mimuw.cloudatlas.agent.modules.Module; +import pl.edu.mimuw.cloudatlas.agent.messages.AgentMessage; + +/* + * Instead of passing messages to an EventBus, this one just collects them + * locally for inspection in unit tests. + */ +public class MockExecutor extends Executor { + public LinkedBlockingQueue messagesToPass; + + public MockExecutor(Module module) { + super(module); + messagesToPass = new LinkedBlockingQueue(); + } + + @Override + public void passMessage(AgentMessage message) throws InterruptedException { + messagesToPass.put(message); + } +} diff --git a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java new file mode 100644 index 0000000..7c869e7 --- /dev/null +++ b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java @@ -0,0 +1,49 @@ +package pl.edu.mimuw.cloudatlas.agent.modules; + +import java.util.List; +import java.util.Map.Entry; + +import pl.edu.mimuw.cloudatlas.agent.messages.AgentMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.GetHierarchyMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.HierarchyMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.ResponseMessage; +import pl.edu.mimuw.cloudatlas.agent.MockExecutor; +import pl.edu.mimuw.cloudatlas.model.Attribute; +import pl.edu.mimuw.cloudatlas.model.Value; +import pl.edu.mimuw.cloudatlas.model.ZMI; + +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +public class StanikTest { + private Stanik stanik; + private MockExecutor executor; + + @Before + public void setupEventBus() { + stanik = new Stanik(); + executor = new MockExecutor(stanik); + } + + @Test + public void getEmptyHierarchy() throws Exception { + GetHierarchyMessage message = new GetHierarchyMessage("test_msg", 0, ModuleType.TEST, 42); + stanik.handleTyped(message); + assertEquals(1, executor.messagesToPass.size()); + ResponseMessage receivedMessage = (ResponseMessage) executor.messagesToPass.take(); + assertEquals(ModuleType.TEST, receivedMessage.getDestinationModule()); + assertEquals(ResponseMessage.Type.HIERARCHY, receivedMessage.getType()); + assertEquals(42, receivedMessage.getRequestId()); + HierarchyMessage hierarchyMessage = (HierarchyMessage) receivedMessage; + ZMI zmi = hierarchyMessage.getZMI(); + assertNull(zmi.getFather()); + assertTrue(zmi.getSons().isEmpty()); + boolean empty = true; + for (Entry entry : zmi.getAttributes()) { + empty = false; + break; + } + assertTrue(empty); + } +} -- cgit v1.2.3 From 614e4e0d4fd07967f928fac122cc36b66a513944 Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Fri, 27 Dec 2019 19:11:38 +0100 Subject: Test GET_HIERARCHY deep copies --- .../mimuw/cloudatlas/agent/modules/StanikTest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java index 7c869e7..1c1e216 100644 --- a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java +++ b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java @@ -9,7 +9,9 @@ import pl.edu.mimuw.cloudatlas.agent.messages.HierarchyMessage; import pl.edu.mimuw.cloudatlas.agent.messages.ResponseMessage; import pl.edu.mimuw.cloudatlas.agent.MockExecutor; import pl.edu.mimuw.cloudatlas.model.Attribute; +import pl.edu.mimuw.cloudatlas.model.AttributesMap; import pl.edu.mimuw.cloudatlas.model.Value; +import pl.edu.mimuw.cloudatlas.model.ValueInt; import pl.edu.mimuw.cloudatlas.model.ZMI; import org.junit.Before; @@ -46,4 +48,21 @@ public class StanikTest { } assertTrue(empty); } + + @Test + public void hierarchyIsDeepCopy() throws Exception { + GetHierarchyMessage message = new GetHierarchyMessage("test_msg", 0, ModuleType.TEST, 42); + stanik.handleTyped(message); + HierarchyMessage receivedMessage = (HierarchyMessage) executor.messagesToPass.poll(); + assertNotNull(receivedMessage); + AttributesMap attributes = receivedMessage.getZMI().getAttributes(); + assertNull(attributes.getOrNull("foo")); + attributes.add("foo", new ValueInt(1337l)); + + GetHierarchyMessage newMessage = new GetHierarchyMessage("test_msg2", 123, ModuleType.TEST, 43); + stanik.handleTyped(newMessage); + HierarchyMessage newReceivedMessage = (HierarchyMessage) executor.messagesToPass.poll(); + AttributesMap newAttributes = newReceivedMessage.getZMI().getAttributes(); + assertNull(newAttributes.getOrNull("foo")); + } } -- cgit v1.2.3 From 5f7e37d7b26832b3b512f9dda310cb9bc92c93fb Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Fri, 27 Dec 2019 21:08:44 +0100 Subject: Create new zones with UpdateAttributes message --- .../cloudatlas/agent/messages/StanikMessage.java | 3 +- .../agent/messages/UpdateAttributesMessage.java | 23 +++++++++++ .../edu/mimuw/cloudatlas/agent/modules/Stanik.java | 48 ++++++++++++++++++++++ .../java/pl/edu/mimuw/cloudatlas/model/ZMI.java | 17 ++++++++ .../mimuw/cloudatlas/agent/modules/StanikTest.java | 38 +++++++++++++++++ 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/UpdateAttributesMessage.java diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java index d2b3064..8661c90 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java @@ -5,7 +5,8 @@ import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; public abstract class StanikMessage extends AgentMessage { public enum Type { - GET_HIERARCHY + GET_HIERARCHY, + UPDATE_ATTRIBUTES } private Type type; diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/UpdateAttributesMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/UpdateAttributesMessage.java new file mode 100644 index 0000000..7e41631 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/UpdateAttributesMessage.java @@ -0,0 +1,23 @@ +package pl.edu.mimuw.cloudatlas.agent.messages; + +import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; +import pl.edu.mimuw.cloudatlas.model.AttributesMap; + +public class UpdateAttributesMessage extends StanikMessage { + private String pathName; + private AttributesMap attributes; + + public UpdateAttributesMessage(String messageId, long timestamp, String pathName, AttributesMap attributes) { + super(messageId, timestamp, Type.UPDATE_ATTRIBUTES); + this.pathName = pathName; + this.attributes = attributes; + } + + public String getPathName() { + return pathName; + } + + public AttributesMap getAttributes() { + return attributes; + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java index b8db08a..a457a94 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java @@ -1,9 +1,17 @@ package pl.edu.mimuw.cloudatlas.agent.modules; +import java.util.Map.Entry; + import pl.edu.mimuw.cloudatlas.agent.messages.AgentMessage; import pl.edu.mimuw.cloudatlas.agent.messages.GetHierarchyMessage; import pl.edu.mimuw.cloudatlas.agent.messages.HierarchyMessage; import pl.edu.mimuw.cloudatlas.agent.messages.StanikMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.UpdateAttributesMessage; +import pl.edu.mimuw.cloudatlas.model.AttributesMap; +import pl.edu.mimuw.cloudatlas.model.Attribute; +import pl.edu.mimuw.cloudatlas.model.PathName; +import pl.edu.mimuw.cloudatlas.model.Value; +import pl.edu.mimuw.cloudatlas.model.ValueString; import pl.edu.mimuw.cloudatlas.model.ZMI; public class Stanik extends Module { @@ -19,6 +27,9 @@ public class Stanik extends Module { case GET_HIERARCHY: handleGetHierarchy((GetHierarchyMessage) message); break; + case UPDATE_ATTRIBUTES: + handleUpdateAttributes((UpdateAttributesMessage) message); + break; default: throw new InvalidMessageType("This type of message cannot be handled by Stanik"); } @@ -28,4 +39,41 @@ public class Stanik extends Module { HierarchyMessage response = new HierarchyMessage("", message.getRequestingModule(), 0, message.getRequestId(), hierarchy.clone()); sendMessage(response); } + + public void handleUpdateAttributes(UpdateAttributesMessage message) { + try { + addMissingZones(new PathName(message.getPathName())); + ZMI zone = hierarchy.findDescendant(message.getPathName()); + for (Entry entry : zone.getAttributes()) { + Attribute attribute = entry.getKey(); + Value newValue = message.getAttributes().getOrNull(attribute); + if (newValue == null) { + zone.getAttributes().remove(attribute); + } + } + for (Entry entry : message.getAttributes()) { + zone.getAttributes().addOrChange(entry.getKey(), entry.getValue()); + } + } catch (ZMI.NoSuchZoneException e) { + System.out.println("ERROR: zone should exist after being added"); + } + } + + private void addMissingZones(PathName path) { + try { + if (!hierarchy.descendantExists(path)) { + addMissingZones(path.levelUp()); + ZMI parent = hierarchy.findDescendant(path.levelUp()); + ZMI newSon = new ZMI(parent); + newSon.getAttributes().add("name", new ValueString(path.getSingletonName())); + parent.addSon(newSon); + } + } catch (ZMI.NoSuchZoneException e) { + System.out.println("ERROR: zone should exist after being added"); + } + } + + public ZMI getHierarchy() { + return hierarchy; + } } diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java b/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java index 7f2f604..54fbf43 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java @@ -111,6 +111,23 @@ public class ZMI implements Cloneable, Serializable { return descendant; } + public boolean descendantExists(PathName path) { + try { + findDescendant(path); + return true; + } catch (NoSuchZoneException e) { + return false; + } + } + + /* + * Convenient version of findDescendant that takes String representation of + * path. + */ + public ZMI findDescendant(String pathString) throws NoSuchZoneException { + return findDescendant(new PathName(pathString)); + } + /** * Gets the list of sons of this ZMI. Modifying a value in the returned list will cause an exception. * diff --git a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java index 1c1e216..31b701d 100644 --- a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java +++ b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java @@ -7,11 +7,13 @@ import pl.edu.mimuw.cloudatlas.agent.messages.AgentMessage; import pl.edu.mimuw.cloudatlas.agent.messages.GetHierarchyMessage; import pl.edu.mimuw.cloudatlas.agent.messages.HierarchyMessage; import pl.edu.mimuw.cloudatlas.agent.messages.ResponseMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.UpdateAttributesMessage; import pl.edu.mimuw.cloudatlas.agent.MockExecutor; import pl.edu.mimuw.cloudatlas.model.Attribute; import pl.edu.mimuw.cloudatlas.model.AttributesMap; import pl.edu.mimuw.cloudatlas.model.Value; import pl.edu.mimuw.cloudatlas.model.ValueInt; +import pl.edu.mimuw.cloudatlas.model.ValueString; import pl.edu.mimuw.cloudatlas.model.ZMI; import org.junit.Before; @@ -65,4 +67,40 @@ public class StanikTest { AttributesMap newAttributes = newReceivedMessage.getZMI().getAttributes(); assertNull(newAttributes.getOrNull("foo")); } + + @Test + public void updateRootAttributes() throws Exception { + AttributesMap attributes = new AttributesMap(); + attributes.add("foo", new ValueInt(1337l)); + attributes.add("bar", new ValueString("baz")); + UpdateAttributesMessage message = new UpdateAttributesMessage("test_msg", 0, "/", attributes); + stanik.handleTyped(message); + AttributesMap actualAttributes = stanik.getHierarchy().getAttributes(); + assertEquals(2, countAttributes(actualAttributes)); + assertEquals(new ValueInt(1337l), actualAttributes.get("foo")); + assertEquals(new ValueString("baz"), actualAttributes.get("bar")); + } + + @Test + public void updateWithNewZone() throws Exception { + AttributesMap attributes = new AttributesMap(); + attributes.add("foo", new ValueInt(1337l)); + attributes.add("bar", new ValueString("baz")); + attributes.add("name", new ValueString("new")); + UpdateAttributesMessage message = new UpdateAttributesMessage("test_msg", 0, "/new", attributes); + stanik.handleTyped(message); + AttributesMap actualAttributes = stanik.getHierarchy().findDescendant("/new").getAttributes(); + assertEquals(3, countAttributes(actualAttributes)); + assertEquals(new ValueInt(1337l), actualAttributes.getOrNull("foo")); + assertEquals(new ValueString("baz"), actualAttributes.getOrNull("bar")); + } + + public int countAttributes(AttributesMap attributes) { + int count = 0; + for (Entry attribute : attributes) { + count++; + } + + return count; + } } -- cgit v1.2.3 From 899e0768dad8418d1d1f2ee9294519d8ad8791a7 Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Sat, 28 Dec 2019 14:09:00 +0100 Subject: Test attribute removal --- .../edu/mimuw/cloudatlas/agent/modules/StanikTest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java index 31b701d..869dc1c 100644 --- a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java +++ b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java @@ -95,6 +95,24 @@ public class StanikTest { assertEquals(new ValueString("baz"), actualAttributes.getOrNull("bar")); } + @Test + public void updateWithRemovedAttributes() throws Exception { + AttributesMap attributes = new AttributesMap(); + attributes.add("foo", new ValueInt(1337l)); + attributes.add("bar", new ValueString("baz")); + UpdateAttributesMessage message = new UpdateAttributesMessage("test_msg", 0, "/", attributes); + stanik.handleTyped(message); + + AttributesMap newAttributes = new AttributesMap(); + newAttributes.add("foo", new ValueInt(1338l)); + UpdateAttributesMessage newMessage = new UpdateAttributesMessage("test_msg2", 0, "/", newAttributes); + stanik.handleTyped(newMessage); + + AttributesMap actualAttributes = stanik.getHierarchy().getAttributes(); + assertEquals(1, countAttributes(actualAttributes)); + assertEquals(new ValueInt(1338l), actualAttributes.getOrNull("foo")); + } + public int countAttributes(AttributesMap attributes) { int count = 0; for (Entry attribute : attributes) { -- cgit v1.2.3 From 8e0aed0711a26fec5c0497cee9777744f1d48663 Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Sat, 28 Dec 2019 15:47:06 +0100 Subject: Apply only fresher attributes --- .../edu/mimuw/cloudatlas/agent/modules/Stanik.java | 83 +++++++++++++++++++--- .../mimuw/cloudatlas/agent/modules/StanikTest.java | 42 ++++++++--- 2 files changed, 107 insertions(+), 18 deletions(-) diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java index a457a94..76ca0b6 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java @@ -1,5 +1,6 @@ package pl.edu.mimuw.cloudatlas.agent.modules; +import java.util.Iterator; import java.util.Map.Entry; import pl.edu.mimuw.cloudatlas.agent.messages.AgentMessage; @@ -10,16 +11,27 @@ import pl.edu.mimuw.cloudatlas.agent.messages.UpdateAttributesMessage; import pl.edu.mimuw.cloudatlas.model.AttributesMap; import pl.edu.mimuw.cloudatlas.model.Attribute; import pl.edu.mimuw.cloudatlas.model.PathName; +import pl.edu.mimuw.cloudatlas.model.Type; +import pl.edu.mimuw.cloudatlas.model.TypePrimitive; import pl.edu.mimuw.cloudatlas.model.Value; +import pl.edu.mimuw.cloudatlas.model.ValueBoolean; import pl.edu.mimuw.cloudatlas.model.ValueString; +import pl.edu.mimuw.cloudatlas.model.ValueTime; import pl.edu.mimuw.cloudatlas.model.ZMI; public class Stanik extends Module { + private class InvalidUpdateAttributesMessage extends Exception { + public InvalidUpdateAttributesMessage(String message) { + super(message); + } + } + private ZMI hierarchy; public Stanik() { super(ModuleType.STATE); hierarchy = new ZMI(); + hierarchy.getAttributes().add("timestamp", new ValueTime(0l)); } public void handleTyped(StanikMessage message) throws InterruptedException, InvalidMessageType { @@ -42,23 +54,75 @@ public class Stanik extends Module { public void handleUpdateAttributes(UpdateAttributesMessage message) { try { + validateUpdateAttributesMessage(message); addMissingZones(new PathName(message.getPathName())); ZMI zone = hierarchy.findDescendant(message.getPathName()); - for (Entry entry : zone.getAttributes()) { - Attribute attribute = entry.getKey(); - Value newValue = message.getAttributes().getOrNull(attribute); - if (newValue == null) { - zone.getAttributes().remove(attribute); - } - } - for (Entry entry : message.getAttributes()) { - zone.getAttributes().addOrChange(entry.getKey(), entry.getValue()); + AttributesMap attributes = zone.getAttributes(); + if (valueLower(attributes.get("timestamp"), message.getAttributes().get("timestamp"))) { + transferAttributes(message.getAttributes(), attributes); + } else { + System.out.println("DEBUG: not applying update with older attributes"); } + } catch (InvalidUpdateAttributesMessage e) { + System.out.println("ERROR: invalid UpdateAttributesMessage " + e.getMessage()); } catch (ZMI.NoSuchZoneException e) { System.out.println("ERROR: zone should exist after being added"); } } + private boolean valueLower(Value a, Value b) { + return ((ValueBoolean) a.isLowerThan(b)).getValue(); + } + + private void validateUpdateAttributesMessage(UpdateAttributesMessage message) throws InvalidUpdateAttributesMessage { + validateZoneName(message); + validateHasTimeStamp(message); + } + + private void validateZoneName(UpdateAttributesMessage message) throws InvalidUpdateAttributesMessage { + Value name = message.getAttributes().getOrNull("name"); + if (message.getPathName().equals("/")) { + if (name != null && !name.isNull()) { + throw new InvalidUpdateAttributesMessage("The root zone should have a null name"); + } + } else { + if (valueNonNullOfType(name, TypePrimitive.STRING)) { + ValueString nameString = (ValueString) name; + String expectedName = (new PathName(message.getPathName())).getSingletonName(); + if (!nameString.getValue().equals(expectedName)) { + throw new InvalidUpdateAttributesMessage("The zone's name attribute should match its path name"); + } + } else { + throw new InvalidUpdateAttributesMessage("Zone attributes should have a name attribute of type String"); + } + } + } + + private void validateHasTimeStamp(UpdateAttributesMessage message) throws InvalidUpdateAttributesMessage { + if (!valueNonNullOfType(message.getAttributes().getOrNull("timestamp"), TypePrimitive.TIME)) { + throw new InvalidUpdateAttributesMessage("Zone attriutes should have a timestamp attribute of type Time"); + } + } + + private boolean valueNonNullOfType(Value value, Type type) { + return value != null && !value.isNull() && value.getType().isCompatible(type); + } + + private void transferAttributes(AttributesMap fromAttributes, AttributesMap toAttributes) { + Iterator> iterator = toAttributes.iterator(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + Attribute attribute = entry.getKey(); + Value newValue = fromAttributes.getOrNull(attribute); + if (newValue == null) { + iterator.remove(); + } + } + for (Entry entry : fromAttributes) { + toAttributes.addOrChange(entry.getKey(), entry.getValue()); + } + } + private void addMissingZones(PathName path) { try { if (!hierarchy.descendantExists(path)) { @@ -66,6 +130,7 @@ public class Stanik extends Module { ZMI parent = hierarchy.findDescendant(path.levelUp()); ZMI newSon = new ZMI(parent); newSon.getAttributes().add("name", new ValueString(path.getSingletonName())); + newSon.getAttributes().add("timestamp", new ValueTime(0l)); parent.addSon(newSon); } } catch (ZMI.NoSuchZoneException e) { diff --git a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java index 869dc1c..12fa531 100644 --- a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java +++ b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java @@ -14,6 +14,7 @@ import pl.edu.mimuw.cloudatlas.model.AttributesMap; import pl.edu.mimuw.cloudatlas.model.Value; import pl.edu.mimuw.cloudatlas.model.ValueInt; import pl.edu.mimuw.cloudatlas.model.ValueString; +import pl.edu.mimuw.cloudatlas.model.ValueTime; import pl.edu.mimuw.cloudatlas.model.ZMI; import org.junit.Before; @@ -43,12 +44,7 @@ public class StanikTest { ZMI zmi = hierarchyMessage.getZMI(); assertNull(zmi.getFather()); assertTrue(zmi.getSons().isEmpty()); - boolean empty = true; - for (Entry entry : zmi.getAttributes()) { - empty = false; - break; - } - assertTrue(empty); + assertEquals(1, countAttributes(zmi.getAttributes())); } @Test @@ -73,12 +69,14 @@ public class StanikTest { AttributesMap attributes = new AttributesMap(); attributes.add("foo", new ValueInt(1337l)); attributes.add("bar", new ValueString("baz")); + attributes.add("timestamp", new ValueTime("2012/12/21 04:20:00.000")); UpdateAttributesMessage message = new UpdateAttributesMessage("test_msg", 0, "/", attributes); stanik.handleTyped(message); AttributesMap actualAttributes = stanik.getHierarchy().getAttributes(); - assertEquals(2, countAttributes(actualAttributes)); + assertEquals(3, countAttributes(actualAttributes)); assertEquals(new ValueInt(1337l), actualAttributes.get("foo")); assertEquals(new ValueString("baz"), actualAttributes.get("bar")); + assertEquals(new ValueTime("2012/12/21 04:20:00.000"), actualAttributes.getOrNull("timestamp")); } @Test @@ -87,12 +85,15 @@ public class StanikTest { attributes.add("foo", new ValueInt(1337l)); attributes.add("bar", new ValueString("baz")); attributes.add("name", new ValueString("new")); + attributes.add("timestamp", new ValueTime("2012/12/21 04:20:00.000")); UpdateAttributesMessage message = new UpdateAttributesMessage("test_msg", 0, "/new", attributes); stanik.handleTyped(message); AttributesMap actualAttributes = stanik.getHierarchy().findDescendant("/new").getAttributes(); - assertEquals(3, countAttributes(actualAttributes)); + assertEquals(4, countAttributes(actualAttributes)); assertEquals(new ValueInt(1337l), actualAttributes.getOrNull("foo")); assertEquals(new ValueString("baz"), actualAttributes.getOrNull("bar")); + assertEquals(new ValueString("new"), actualAttributes.getOrNull("name")); + assertEquals(new ValueTime("2012/12/21 04:20:00.000"), actualAttributes.getOrNull("timestamp")); } @Test @@ -101,16 +102,39 @@ public class StanikTest { attributes.add("foo", new ValueInt(1337l)); attributes.add("bar", new ValueString("baz")); UpdateAttributesMessage message = new UpdateAttributesMessage("test_msg", 0, "/", attributes); + attributes.add("timestamp", new ValueTime("2012/12/21 04:20:00.000")); stanik.handleTyped(message); AttributesMap newAttributes = new AttributesMap(); + newAttributes.add("timestamp", new ValueTime("2012/12/21 04:20:42.000")); newAttributes.add("foo", new ValueInt(1338l)); UpdateAttributesMessage newMessage = new UpdateAttributesMessage("test_msg2", 0, "/", newAttributes); stanik.handleTyped(newMessage); AttributesMap actualAttributes = stanik.getHierarchy().getAttributes(); - assertEquals(1, countAttributes(actualAttributes)); + assertEquals(2, countAttributes(actualAttributes)); assertEquals(new ValueInt(1338l), actualAttributes.getOrNull("foo")); + assertEquals(new ValueTime("2012/12/21 04:20:42.000"), actualAttributes.getOrNull("timestamp")); + } + + @Test + public void dontApplyUpdateWithOlderTimestamp() throws Exception { + AttributesMap attributes = new AttributesMap(); + attributes.add("foo", new ValueInt(1337l)); + attributes.add("timestamp", new ValueTime("2012/12/21 04:20:00.000")); + UpdateAttributesMessage message = new UpdateAttributesMessage("test_msg", 0, "/", attributes); + stanik.handleTyped(message); + + AttributesMap oldAttributes = new AttributesMap(); + oldAttributes.add("foo", new ValueInt(1336l)); + oldAttributes.add("timestamp", new ValueTime("2012/12/21 04:19:00.000")); + UpdateAttributesMessage newMessage = new UpdateAttributesMessage("test_msg2", 0, "/", oldAttributes); + stanik.handleTyped(newMessage); + + AttributesMap actualAttributes = stanik.getHierarchy().getAttributes(); + assertEquals(2, countAttributes(actualAttributes)); + assertEquals(new ValueInt(1337l), actualAttributes.getOrNull("foo")); + assertEquals(new ValueTime("2012/12/21 04:20:00.000"), actualAttributes.getOrNull("timestamp")); } public int countAttributes(AttributesMap attributes) { -- cgit v1.2.3 From af6f6f37403d0e23a09a6ba4476271c2a0eaf623 Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Sun, 29 Dec 2019 14:12:31 +0100 Subject: Include timestamped queries in state --- .../agent/messages/GetHierarchyMessage.java | 22 ------------ .../cloudatlas/agent/messages/GetStateMessage.java | 22 ++++++++++++ .../agent/messages/HierarchyMessage.java | 17 --------- .../cloudatlas/agent/messages/ResponseMessage.java | 2 +- .../cloudatlas/agent/messages/StanikMessage.java | 2 +- .../cloudatlas/agent/messages/StateMessage.java | 29 +++++++++++++++ .../edu/mimuw/cloudatlas/agent/modules/Stanik.java | 22 ++++++++---- .../mimuw/cloudatlas/agent/modules/StanikTest.java | 41 ++++++++++++---------- 8 files changed, 91 insertions(+), 66 deletions(-) delete mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetHierarchyMessage.java create mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetStateMessage.java delete mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/HierarchyMessage.java create mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StateMessage.java diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetHierarchyMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetHierarchyMessage.java deleted file mode 100644 index d3c749d..0000000 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetHierarchyMessage.java +++ /dev/null @@ -1,22 +0,0 @@ -package pl.edu.mimuw.cloudatlas.agent.messages; - -import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; - -public class GetHierarchyMessage extends StanikMessage { - private ModuleType requestingModule; - private long requestId; - - public GetHierarchyMessage(String messageId, long timestamp, ModuleType requestingModule, long requestId) { - super(messageId, timestamp, Type.GET_HIERARCHY); - this.requestingModule = requestingModule; - this.requestId = requestId; - } - - public ModuleType getRequestingModule() { - return requestingModule; - } - - public long getRequestId() { - return requestId; - } -} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetStateMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetStateMessage.java new file mode 100644 index 0000000..63392e8 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/GetStateMessage.java @@ -0,0 +1,22 @@ +package pl.edu.mimuw.cloudatlas.agent.messages; + +import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; + +public class GetStateMessage extends StanikMessage { + private ModuleType requestingModule; + private long requestId; + + public GetStateMessage(String messageId, long timestamp, ModuleType requestingModule, long requestId) { + super(messageId, timestamp, Type.GET_STATE); + this.requestingModule = requestingModule; + this.requestId = requestId; + } + + public ModuleType getRequestingModule() { + return requestingModule; + } + + public long getRequestId() { + return requestId; + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/HierarchyMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/HierarchyMessage.java deleted file mode 100644 index 9f7ca70..0000000 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/HierarchyMessage.java +++ /dev/null @@ -1,17 +0,0 @@ -package pl.edu.mimuw.cloudatlas.agent.messages; - -import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; -import pl.edu.mimuw.cloudatlas.model.ZMI; - -public class HierarchyMessage extends ResponseMessage { - private ZMI zmi; - - public HierarchyMessage(String messageId, ModuleType destinationModule, long timestamp, long requestId, ZMI zmi) { - super(messageId, destinationModule, timestamp, Type.HIERARCHY, requestId); - this.zmi = zmi; - } - - public ZMI getZMI() { - return zmi; - } -} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/ResponseMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/ResponseMessage.java index 171cf07..02b3337 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/ResponseMessage.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/ResponseMessage.java @@ -5,7 +5,7 @@ import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; public abstract class ResponseMessage extends AgentMessage { public enum Type { - HIERARCHY + STATE } Type type; diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java index 8661c90..da35f2d 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java @@ -5,7 +5,7 @@ import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; public abstract class StanikMessage extends AgentMessage { public enum Type { - GET_HIERARCHY, + GET_STATE, UPDATE_ATTRIBUTES } diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StateMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StateMessage.java new file mode 100644 index 0000000..f7f490b --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StateMessage.java @@ -0,0 +1,29 @@ +package pl.edu.mimuw.cloudatlas.agent.messages; + +import java.util.Map; +import java.util.Map.Entry; + +import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; +import pl.edu.mimuw.cloudatlas.model.Attribute; +import pl.edu.mimuw.cloudatlas.model.ValueQuery; +import pl.edu.mimuw.cloudatlas.model.ValueTime; +import pl.edu.mimuw.cloudatlas.model.ZMI; + +public class StateMessage extends ResponseMessage { + private ZMI zmi; + private Map> queries; + + public StateMessage(String messageId, ModuleType destinationModule, long timestamp, long requestId, ZMI zmi, Map> queries) { + super(messageId, destinationModule, timestamp, Type.STATE, requestId); + this.zmi = zmi; + this.queries = queries; + } + + public ZMI getZMI() { + return zmi; + } + + public Map> getQueries() { + return queries; + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java index 76ca0b6..a31f265 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java @@ -1,20 +1,22 @@ package pl.edu.mimuw.cloudatlas.agent.modules; import java.util.Iterator; +import java.util.HashMap; import java.util.Map.Entry; import pl.edu.mimuw.cloudatlas.agent.messages.AgentMessage; -import pl.edu.mimuw.cloudatlas.agent.messages.GetHierarchyMessage; -import pl.edu.mimuw.cloudatlas.agent.messages.HierarchyMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.GetStateMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.StateMessage; import pl.edu.mimuw.cloudatlas.agent.messages.StanikMessage; import pl.edu.mimuw.cloudatlas.agent.messages.UpdateAttributesMessage; -import pl.edu.mimuw.cloudatlas.model.AttributesMap; import pl.edu.mimuw.cloudatlas.model.Attribute; +import pl.edu.mimuw.cloudatlas.model.AttributesMap; import pl.edu.mimuw.cloudatlas.model.PathName; import pl.edu.mimuw.cloudatlas.model.Type; import pl.edu.mimuw.cloudatlas.model.TypePrimitive; import pl.edu.mimuw.cloudatlas.model.Value; import pl.edu.mimuw.cloudatlas.model.ValueBoolean; +import pl.edu.mimuw.cloudatlas.model.ValueQuery; import pl.edu.mimuw.cloudatlas.model.ValueString; import pl.edu.mimuw.cloudatlas.model.ValueTime; import pl.edu.mimuw.cloudatlas.model.ZMI; @@ -27,17 +29,19 @@ public class Stanik extends Module { } private ZMI hierarchy; + private HashMap> queries; public Stanik() { super(ModuleType.STATE); hierarchy = new ZMI(); + queries = new HashMap>(); hierarchy.getAttributes().add("timestamp", new ValueTime(0l)); } public void handleTyped(StanikMessage message) throws InterruptedException, InvalidMessageType { switch(message.getType()) { - case GET_HIERARCHY: - handleGetHierarchy((GetHierarchyMessage) message); + case GET_STATE: + handleGetState((GetStateMessage) message); break; case UPDATE_ATTRIBUTES: handleUpdateAttributes((UpdateAttributesMessage) message); @@ -47,8 +51,8 @@ public class Stanik extends Module { } } - public void handleGetHierarchy(GetHierarchyMessage message) throws InterruptedException { - HierarchyMessage response = new HierarchyMessage("", message.getRequestingModule(), 0, message.getRequestId(), hierarchy.clone()); + public void handleGetState(GetStateMessage message) throws InterruptedException { + StateMessage response = new StateMessage("", message.getRequestingModule(), 0, message.getRequestId(), hierarchy.clone(), (HashMap>) queries.clone()); sendMessage(response); } @@ -141,4 +145,8 @@ public class Stanik extends Module { public ZMI getHierarchy() { return hierarchy; } + + public HashMap> getQueries() { + return queries; + } } diff --git a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java index 12fa531..dd44e8f 100644 --- a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java +++ b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java @@ -1,11 +1,13 @@ package pl.edu.mimuw.cloudatlas.agent.modules; +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import pl.edu.mimuw.cloudatlas.agent.messages.AgentMessage; -import pl.edu.mimuw.cloudatlas.agent.messages.GetHierarchyMessage; -import pl.edu.mimuw.cloudatlas.agent.messages.HierarchyMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.GetStateMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.StateMessage; import pl.edu.mimuw.cloudatlas.agent.messages.ResponseMessage; import pl.edu.mimuw.cloudatlas.agent.messages.UpdateAttributesMessage; import pl.edu.mimuw.cloudatlas.agent.MockExecutor; @@ -13,6 +15,7 @@ import pl.edu.mimuw.cloudatlas.model.Attribute; import pl.edu.mimuw.cloudatlas.model.AttributesMap; import pl.edu.mimuw.cloudatlas.model.Value; import pl.edu.mimuw.cloudatlas.model.ValueInt; +import pl.edu.mimuw.cloudatlas.model.ValueQuery; import pl.edu.mimuw.cloudatlas.model.ValueString; import pl.edu.mimuw.cloudatlas.model.ValueTime; import pl.edu.mimuw.cloudatlas.model.ZMI; @@ -32,34 +35,36 @@ public class StanikTest { } @Test - public void getEmptyHierarchy() throws Exception { - GetHierarchyMessage message = new GetHierarchyMessage("test_msg", 0, ModuleType.TEST, 42); + public void getEmptyState() throws Exception { + GetStateMessage message = new GetStateMessage("test_msg", 0, ModuleType.TEST, 42); stanik.handleTyped(message); assertEquals(1, executor.messagesToPass.size()); ResponseMessage receivedMessage = (ResponseMessage) executor.messagesToPass.take(); assertEquals(ModuleType.TEST, receivedMessage.getDestinationModule()); - assertEquals(ResponseMessage.Type.HIERARCHY, receivedMessage.getType()); + assertEquals(ResponseMessage.Type.STATE, receivedMessage.getType()); assertEquals(42, receivedMessage.getRequestId()); - HierarchyMessage hierarchyMessage = (HierarchyMessage) receivedMessage; - ZMI zmi = hierarchyMessage.getZMI(); + StateMessage stateMessage = (StateMessage) receivedMessage; + ZMI zmi = stateMessage.getZMI(); assertNull(zmi.getFather()); assertTrue(zmi.getSons().isEmpty()); - assertEquals(1, countAttributes(zmi.getAttributes())); + assertEquals(1, iterableSize(zmi.getAttributes())); + Map> queries = stateMessage.getQueries(); + assertEquals(0, iterableSize(queries.keySet())); } @Test public void hierarchyIsDeepCopy() throws Exception { - GetHierarchyMessage message = new GetHierarchyMessage("test_msg", 0, ModuleType.TEST, 42); + GetStateMessage message = new GetStateMessage("test_msg", 0, ModuleType.TEST, 42); stanik.handleTyped(message); - HierarchyMessage receivedMessage = (HierarchyMessage) executor.messagesToPass.poll(); + StateMessage receivedMessage = (StateMessage) executor.messagesToPass.poll(); assertNotNull(receivedMessage); AttributesMap attributes = receivedMessage.getZMI().getAttributes(); assertNull(attributes.getOrNull("foo")); attributes.add("foo", new ValueInt(1337l)); - GetHierarchyMessage newMessage = new GetHierarchyMessage("test_msg2", 123, ModuleType.TEST, 43); + GetStateMessage newMessage = new GetStateMessage("test_msg2", 123, ModuleType.TEST, 43); stanik.handleTyped(newMessage); - HierarchyMessage newReceivedMessage = (HierarchyMessage) executor.messagesToPass.poll(); + StateMessage newReceivedMessage = (StateMessage) executor.messagesToPass.poll(); AttributesMap newAttributes = newReceivedMessage.getZMI().getAttributes(); assertNull(newAttributes.getOrNull("foo")); } @@ -73,7 +78,7 @@ public class StanikTest { UpdateAttributesMessage message = new UpdateAttributesMessage("test_msg", 0, "/", attributes); stanik.handleTyped(message); AttributesMap actualAttributes = stanik.getHierarchy().getAttributes(); - assertEquals(3, countAttributes(actualAttributes)); + assertEquals(3, iterableSize(actualAttributes)); assertEquals(new ValueInt(1337l), actualAttributes.get("foo")); assertEquals(new ValueString("baz"), actualAttributes.get("bar")); assertEquals(new ValueTime("2012/12/21 04:20:00.000"), actualAttributes.getOrNull("timestamp")); @@ -89,7 +94,7 @@ public class StanikTest { UpdateAttributesMessage message = new UpdateAttributesMessage("test_msg", 0, "/new", attributes); stanik.handleTyped(message); AttributesMap actualAttributes = stanik.getHierarchy().findDescendant("/new").getAttributes(); - assertEquals(4, countAttributes(actualAttributes)); + assertEquals(4, iterableSize(actualAttributes)); assertEquals(new ValueInt(1337l), actualAttributes.getOrNull("foo")); assertEquals(new ValueString("baz"), actualAttributes.getOrNull("bar")); assertEquals(new ValueString("new"), actualAttributes.getOrNull("name")); @@ -112,7 +117,7 @@ public class StanikTest { stanik.handleTyped(newMessage); AttributesMap actualAttributes = stanik.getHierarchy().getAttributes(); - assertEquals(2, countAttributes(actualAttributes)); + assertEquals(2, iterableSize(actualAttributes)); assertEquals(new ValueInt(1338l), actualAttributes.getOrNull("foo")); assertEquals(new ValueTime("2012/12/21 04:20:42.000"), actualAttributes.getOrNull("timestamp")); } @@ -132,14 +137,14 @@ public class StanikTest { stanik.handleTyped(newMessage); AttributesMap actualAttributes = stanik.getHierarchy().getAttributes(); - assertEquals(2, countAttributes(actualAttributes)); + assertEquals(2, iterableSize(actualAttributes)); assertEquals(new ValueInt(1337l), actualAttributes.getOrNull("foo")); assertEquals(new ValueTime("2012/12/21 04:20:00.000"), actualAttributes.getOrNull("timestamp")); } - public int countAttributes(AttributesMap attributes) { + public int iterableSize(Iterable iterable) { int count = 0; - for (Entry attribute : attributes) { + for (T attribute : iterable) { count++; } -- cgit v1.2.3 From c3121bfa6c00682173a7f1fba9d4c524b8ef517e Mon Sep 17 00:00:00 2001 From: Marcin Chrzanowski Date: Sun, 29 Dec 2019 16:49:43 +0100 Subject: Handle queries in state --- .../cloudatlas/agent/messages/StanikMessage.java | 3 +- .../agent/messages/UpdateQueriesMessage.java | 21 ++++++++ .../edu/mimuw/cloudatlas/agent/modules/Stanik.java | 15 ++++++ .../mimuw/cloudatlas/agent/modules/StanikTest.java | 58 ++++++++++++++++++++++ 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/UpdateQueriesMessage.java diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java index da35f2d..b23f6e0 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/StanikMessage.java @@ -6,7 +6,8 @@ import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; public abstract class StanikMessage extends AgentMessage { public enum Type { GET_STATE, - UPDATE_ATTRIBUTES + UPDATE_ATTRIBUTES, + UPDATE_QUERIES } private Type type; diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/UpdateQueriesMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/UpdateQueriesMessage.java new file mode 100644 index 0000000..58ad55a --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/UpdateQueriesMessage.java @@ -0,0 +1,21 @@ +package pl.edu.mimuw.cloudatlas.agent.messages; + +import java.util.Map; +import java.util.Map.Entry; + +import pl.edu.mimuw.cloudatlas.model.Attribute; +import pl.edu.mimuw.cloudatlas.model.ValueQuery; +import pl.edu.mimuw.cloudatlas.model.ValueTime; + +public class UpdateQueriesMessage extends StanikMessage { + private Map> queries; + + public UpdateQueriesMessage(String messageId, long timestamp, Map> queries) { + super(messageId, timestamp, Type.UPDATE_QUERIES); + this.queries = queries; + } + + public Map> getQueries() { + return queries; + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java index a31f265..e8721b3 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Stanik.java @@ -9,6 +9,7 @@ import pl.edu.mimuw.cloudatlas.agent.messages.GetStateMessage; import pl.edu.mimuw.cloudatlas.agent.messages.StateMessage; import pl.edu.mimuw.cloudatlas.agent.messages.StanikMessage; import pl.edu.mimuw.cloudatlas.agent.messages.UpdateAttributesMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.UpdateQueriesMessage; import pl.edu.mimuw.cloudatlas.model.Attribute; import pl.edu.mimuw.cloudatlas.model.AttributesMap; import pl.edu.mimuw.cloudatlas.model.PathName; @@ -46,6 +47,9 @@ public class Stanik extends Module { case UPDATE_ATTRIBUTES: handleUpdateAttributes((UpdateAttributesMessage) message); break; + case UPDATE_QUERIES: + handleUpdateQueries((UpdateQueriesMessage) message); + break; default: throw new InvalidMessageType("This type of message cannot be handled by Stanik"); } @@ -74,6 +78,17 @@ public class Stanik extends Module { } } + public void handleUpdateQueries(UpdateQueriesMessage message) { + for (Entry> entry : message.getQueries().entrySet()) { + Attribute attribute = entry.getKey(); + ValueTime timestamp = entry.getValue().getValue(); + Entry currentTimestampedQuery = queries.get(attribute); + if (currentTimestampedQuery == null || valueLower(currentTimestampedQuery.getValue(), timestamp)) { + queries.put(entry.getKey(), entry.getValue()); + } + } + } + private boolean valueLower(Value a, Value b) { return ((ValueBoolean) a.isLowerThan(b)).getValue(); } diff --git a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java index dd44e8f..aeaf19f 100644 --- a/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java +++ b/src/test/java/pl/edu/mimuw/cloudatlas/agent/modules/StanikTest.java @@ -2,6 +2,8 @@ package pl.edu.mimuw.cloudatlas.agent.modules; import java.util.Iterator; import java.util.List; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -10,6 +12,7 @@ import pl.edu.mimuw.cloudatlas.agent.messages.GetStateMessage; import pl.edu.mimuw.cloudatlas.agent.messages.StateMessage; import pl.edu.mimuw.cloudatlas.agent.messages.ResponseMessage; import pl.edu.mimuw.cloudatlas.agent.messages.UpdateAttributesMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.UpdateQueriesMessage; import pl.edu.mimuw.cloudatlas.agent.MockExecutor; import pl.edu.mimuw.cloudatlas.model.Attribute; import pl.edu.mimuw.cloudatlas.model.AttributesMap; @@ -142,6 +145,61 @@ public class StanikTest { assertEquals(new ValueTime("2012/12/21 04:20:00.000"), actualAttributes.getOrNull("timestamp")); } + @Test + public void addQuery() throws Exception { + HashMap> queries = new HashMap>(); + queries.put(new Attribute("&query"), new SimpleImmutableEntry(new ValueQuery("SELECT 1 AS one"), new ValueTime(42l))); + UpdateQueriesMessage message = new UpdateQueriesMessage("test_msg", 0, queries); + stanik.handleTyped(message); + + HashMap> actualQueries = stanik.getQueries(); + assertEquals(1, iterableSize(actualQueries.keySet())); + assertTrue(actualQueries.containsKey(new Attribute("&query"))); + Entry timestampedQuery = actualQueries.get(new Attribute("&query")); + assertEquals(new ValueTime(42l), timestampedQuery.getValue()); + assertEquals(new ValueQuery("SELECT 1 AS one"), timestampedQuery.getKey()); + } + + @Test + public void updateQueries() throws Exception { + HashMap> queries = new HashMap>(); + queries.put(new Attribute("&query1"), new SimpleImmutableEntry(new ValueQuery("SELECT 1 AS one"), new ValueTime(42l))); + queries.put(new Attribute("&query3"), new SimpleImmutableEntry(new ValueQuery("SELECT 23 AS x"), new ValueTime(43l))); + queries.put(new Attribute("&query4"), new SimpleImmutableEntry(new ValueQuery("SELECT 1000 AS foo"), new ValueTime(43l))); + UpdateQueriesMessage message = new UpdateQueriesMessage("test_msg", 0, queries); + stanik.handleTyped(message); + + HashMap> otherQueries = new HashMap>(); + otherQueries.put(new Attribute("&query1"), new SimpleImmutableEntry(new ValueQuery("SELECT 2 AS one"), new ValueTime(41l))); + otherQueries.put(new Attribute("&query2"), new SimpleImmutableEntry(new ValueQuery("SELECT 42 AS answer"), new ValueTime(39l))); + otherQueries.put(new Attribute("&query3"), new SimpleImmutableEntry(new ValueQuery("SELECT 17 AS y"), new ValueTime(44l))); + UpdateQueriesMessage otherMessage = new UpdateQueriesMessage("test_msg", 0, otherQueries); + stanik.handleTyped(otherMessage); + + HashMap> actualQueries = stanik.getQueries(); + assertEquals(4, iterableSize(actualQueries.keySet())); + assertTrue(actualQueries.containsKey(new Attribute("&query1"))); + assertTrue(actualQueries.containsKey(new Attribute("&query2"))); + assertTrue(actualQueries.containsKey(new Attribute("&query3"))); + assertTrue(actualQueries.containsKey(new Attribute("&query4"))); + + Entry timestampedQuery1 = actualQueries.get(new Attribute("&query1")); + assertEquals(new ValueTime(42l), timestampedQuery1.getValue()); + assertEquals(new ValueQuery("SELECT 1 AS one"), timestampedQuery1.getKey()); + + Entry timestampedQuery2 = actualQueries.get(new Attribute("&query2")); + assertEquals(new ValueTime(39l), timestampedQuery2.getValue()); + assertEquals(new ValueQuery("SELECT 42 AS answer"), timestampedQuery2.getKey()); + + Entry timestampedQuery3 = actualQueries.get(new Attribute("&query3")); + assertEquals(new ValueTime(44l), timestampedQuery3.getValue()); + assertEquals(new ValueQuery("SELECT 17 AS y"), timestampedQuery3.getKey()); + + Entry timestampedQuery4 = actualQueries.get(new Attribute("&query4")); + assertEquals(new ValueTime(43l), timestampedQuery4.getValue()); + assertEquals(new ValueQuery("SELECT 1000 AS foo"), timestampedQuery4.getKey()); + } + public int iterableSize(Iterable iterable) { int count = 0; for (T attribute : iterable) { -- cgit v1.2.3