diff options
author | Martin <marcin.j.chrzanowski@gmail.com> | 2020-01-03 18:29:55 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-03 18:29:55 +0100 |
commit | 9be545d6f2609d5fe8143a1964d44f6eee588ea2 (patch) | |
tree | f0bb8d159249e9ddf328e92d7fb5eda10122ab3b /src/main/java/pl/edu/mimuw/cloudatlas | |
parent | fda20e7aa496926d4f4d78921925025040414d9b (diff) | |
parent | ec2da92e9636e219f5ad7caa73c9334dae12e5b1 (diff) |
Merge pull request #83 from m-chrzan/remik
Use new modular architecture when implementing the RMI API
Diffstat (limited to 'src/main/java/pl/edu/mimuw/cloudatlas')
14 files changed, 366 insertions, 55 deletions
diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/Agent.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/Agent.java index 392e0a1..70da4af 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/Agent.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/Agent.java @@ -1,22 +1,46 @@ package pl.edu.mimuw.cloudatlas.agent; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.UnicastRemoteObject; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import pl.edu.mimuw.cloudatlas.agent.NewApiImplementation; +import pl.edu.mimuw.cloudatlas.agent.messages.UpdateAttributesMessage; import pl.edu.mimuw.cloudatlas.agent.modules.Module; import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; import pl.edu.mimuw.cloudatlas.agent.modules.Qurnik; -import pl.edu.mimuw.cloudatlas.agent.modules.RMI; +import pl.edu.mimuw.cloudatlas.agent.modules.Remik; import pl.edu.mimuw.cloudatlas.agent.modules.Stanik; import pl.edu.mimuw.cloudatlas.agent.modules.TimerScheduler; +import pl.edu.mimuw.cloudatlas.api.Api; +import pl.edu.mimuw.cloudatlas.interpreter.Main; +import pl.edu.mimuw.cloudatlas.model.PathName; +import pl.edu.mimuw.cloudatlas.model.ZMI; public class Agent { + private static EventBus eventBus; + + public static void runRegistry() { + try { + NewApiImplementation api = new NewApiImplementation(eventBus); + Api apiStub = + (Api) UnicastRemoteObject.exportObject(api, 0); + Registry registry = LocateRegistry.getRegistry(); + registry.rebind("Api", apiStub); + System.out.println("Agent: api bound"); + } catch (Exception e) { + System.err.println("Agent registry initialization exception:"); + e.printStackTrace(); + } + } public static HashMap<ModuleType, Module> initializeModules() { HashMap<ModuleType, Module> modules = new HashMap<ModuleType, Module>(); modules.put(ModuleType.TIMER_SCHEDULER, new TimerScheduler(ModuleType.TIMER_SCHEDULER)); - modules.put(ModuleType.RMI, new RMI(ModuleType.RMI)); + modules.put(ModuleType.RMI, new Remik()); modules.put(ModuleType.STATE, new Stanik()); modules.put(ModuleType.QUERY, new Qurnik()); // TODO add modules as we implement them @@ -61,15 +85,39 @@ public class Agent { HashMap<ModuleType, Executor> executors = initializeExecutors(modules); ArrayList<Thread> executorThreads = initializeExecutorThreads(executors); - Thread eventBusThread = new Thread(new EventBus(executors)); + eventBus = new EventBus(executors); + Thread eventBusThread = new Thread(eventBus); System.out.println("Initializing event bus"); eventBusThread.start(); + } - System.out.println("Closing executors"); - closeExecutors(executorThreads); + private static void initZones() { + try { + ZMI root = Main.createTestHierarchy2(); + addZoneAndChildren(root, new PathName("")); + System.out.println("Initialized with test hierarchy"); + } catch (Exception e) { + System.out.println("ERROR: failed to create test hierarchy"); + } + } + + private static void addZoneAndChildren(ZMI zmi, PathName pathName) { + try { + System.out.println("trying to add " + pathName.toString()); + UpdateAttributesMessage message = new UpdateAttributesMessage("", 0, pathName.toString(), zmi.getAttributes()); + System.out.println("added it!"); + eventBus.addMessage(message); + for (ZMI son : zmi.getSons()) { + addZoneAndChildren(son, pathName.levelDown(son.getAttributes().getOrNull("name").toString())); + } + } catch (Exception e) { + System.out.println("ERROR: failed to add zone"); + } } public static void main(String[] args) { runModulesAsThreads(); + runRegistry(); + initZones(); } } diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/EventBus.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/EventBus.java index 5b09253..a6d3b2d 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/EventBus.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/EventBus.java @@ -20,6 +20,10 @@ public class EventBus implements Runnable { } } + // Allows for testing with a mock EventBus + protected EventBus() { + } + EventBus(HashMap<ModuleType, Executor> executors) { this.executors = executors; setEventBusReference(); diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/NewApiImplementation.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/NewApiImplementation.java new file mode 100644 index 0000000..def1d72 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/NewApiImplementation.java @@ -0,0 +1,143 @@ +package pl.edu.mimuw.cloudatlas.agent; + +import java.io.PrintStream; + +import java.rmi.RemoteException; + +import java.util.concurrent.CompletableFuture; +import java.util.List; +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +import pl.edu.mimuw.cloudatlas.agent.messages.RequestStateMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.ResponseMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.SetAttributeMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.StateMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.UpdateQueriesMessage; +import pl.edu.mimuw.cloudatlas.interpreter.Interpreter; +import pl.edu.mimuw.cloudatlas.interpreter.InterpreterException; +import pl.edu.mimuw.cloudatlas.interpreter.Main; +import pl.edu.mimuw.cloudatlas.interpreter.QueryResult; +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.ValueContact; +import pl.edu.mimuw.cloudatlas.model.Value; +import pl.edu.mimuw.cloudatlas.model.ValueNull; +import pl.edu.mimuw.cloudatlas.model.ValueQuery; +import pl.edu.mimuw.cloudatlas.model.ValueSet; +import pl.edu.mimuw.cloudatlas.model.ValueTime; +import pl.edu.mimuw.cloudatlas.model.Type; +import pl.edu.mimuw.cloudatlas.model.TypePrimitive; +import pl.edu.mimuw.cloudatlas.model.ZMI; +import pl.edu.mimuw.cloudatlas.api.Api; + +public class NewApiImplementation implements Api { + private EventBus eventBus; + + public NewApiImplementation(EventBus eventBus) { + this.eventBus = eventBus; + } + + public Set<String> getZoneSet() throws RemoteException { + CompletableFuture<ResponseMessage> responseFuture = new CompletableFuture(); + RequestStateMessage message = new RequestStateMessage("", 0, responseFuture); + try { + eventBus.addMessage(message); + ResponseMessage response = responseFuture.get(); + + if (response.getType() == ResponseMessage.Type.STATE) { + StateMessage stateMessage = (StateMessage) response; + Set<String> zones = new HashSet<String>(); + collectZoneNames(stateMessage.getZMI(), zones); + return zones; + } else { + System.out.println("ERROR: getZoneSet didn't receive a StateMessage"); + throw new Exception("Failed to retrieve zone set"); + } + } catch (Exception e) { + System.out.println("ERROR: exception caught in getZoneSet"); + throw new RemoteException(e.getMessage()); + } + } + + private void collectZoneNames(ZMI zone, Set<String> names) { + names.add(zone.getPathName().toString()); + for (ZMI son : zone.getSons()) { + collectZoneNames(son, names); + } + } + + public AttributesMap getZoneAttributeValues(String zoneName) throws RemoteException { + CompletableFuture<ResponseMessage> responseFuture = new CompletableFuture(); + RequestStateMessage message = new RequestStateMessage("", 0, responseFuture); + try { + eventBus.addMessage(message); + ResponseMessage response = responseFuture.get(); + + if (response.getType() == ResponseMessage.Type.STATE) { + StateMessage stateMessage = (StateMessage) response; + return stateMessage.getZMI().findDescendant(zoneName).getAttributes(); + } else { + System.out.println("ERROR: getZoneSet didn't receive a StateMessage"); + throw new Exception("Failed to retrieve zone set"); + } + } catch (Exception e) { + System.out.println("ERROR: exception caught in getZoneSet"); + throw new RemoteException(e.getMessage()); + } + } + + public void installQuery(String name, String queryCode) throws RemoteException { + Pattern queryNamePattern = Pattern.compile("&[a-zA-Z][\\w_]*"); + Matcher matcher = queryNamePattern.matcher(name); + if (!matcher.matches()) { + throw new RemoteException("Invalid query identifier"); + } + try { + ValueQuery query = new ValueQuery(queryCode); + Attribute attributeName = new Attribute(name); + ValueTime timestamp = new ValueTime(System.currentTimeMillis()); + Map<Attribute, Entry<ValueQuery, ValueTime>> queries = new HashMap(); + queries.put(attributeName, new SimpleImmutableEntry(query, timestamp)); + UpdateQueriesMessage message = new UpdateQueriesMessage("", 0, queries); + eventBus.addMessage(message); + } catch (Exception e) { + throw new RemoteException("Failed to install query", e); + } + } + + public void uninstallQuery(String queryName) throws RemoteException { + try { + Attribute attributeName = new Attribute(queryName); + ValueTime timestamp = new ValueTime(System.currentTimeMillis()); + Map<Attribute, Entry<ValueQuery, ValueTime>> queries = new HashMap(); + queries.put(attributeName, new SimpleImmutableEntry(null, timestamp)); + UpdateQueriesMessage message = new UpdateQueriesMessage("", 0, queries); + eventBus.addMessage(message); + } catch (Exception e) { + System.out.println("ERROR: failed to remove query"); + throw new RemoteException("Failed to uninstall query", e); + } + } + + public void setAttributeValue(String zoneName, String attributeName, Value value) throws RemoteException { + try { + SetAttributeMessage message = new SetAttributeMessage("", 0, zoneName, new Attribute(attributeName), value, new ValueTime(System.currentTimeMillis())); + eventBus.addMessage(message); + } catch (Exception e) { + System.out.println("ERROR: failed to set attribute"); + throw new RemoteException("Failed to set attribute", e); + } + } + + public void setFallbackContacts(Set<ValueContact> contacts) throws RemoteException { + // this.contacts = contacts; + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/RMIMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/RemikMessage.java index a8e3afb..b0300cb 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/RMIMessage.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/RemikMessage.java @@ -3,9 +3,20 @@ package pl.edu.mimuw.cloudatlas.agent.messages; import pl.edu.mimuw.cloudatlas.agent.modules.Module; import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; -public class RMIMessage extends AgentMessage { - public RMIMessage(String messageId, long timestamp) { +public abstract class RemikMessage extends AgentMessage { + public enum Type { + REQUEST_STATE + } + + private Type type; + + public RemikMessage(String messageId, long timestamp, Type type) { super(messageId, ModuleType.RMI, timestamp); + this.type = type; + } + + public Type getType() { + return type; } public void callMe(Module module) throws InterruptedException, Module.InvalidMessageType { diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/RequestStateMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/RequestStateMessage.java new file mode 100644 index 0000000..698aac7 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/RequestStateMessage.java @@ -0,0 +1,16 @@ +package pl.edu.mimuw.cloudatlas.agent.messages; + +import java.util.concurrent.CompletableFuture; + +public class RequestStateMessage extends RemikMessage { + CompletableFuture<ResponseMessage> responseFuture; + + public RequestStateMessage(String messageId, long timestamp, CompletableFuture<ResponseMessage> responseFuture) { + super(messageId, timestamp, Type.REQUEST_STATE); + this.responseFuture = responseFuture; + } + + public CompletableFuture<ResponseMessage> getFuture() { + return responseFuture; + } +} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/SetAttributeMessage.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/SetAttributeMessage.java new file mode 100644 index 0000000..4888484 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/messages/SetAttributeMessage.java @@ -0,0 +1,37 @@ +package pl.edu.mimuw.cloudatlas.agent.messages; + +import pl.edu.mimuw.cloudatlas.agent.modules.ModuleType; +import pl.edu.mimuw.cloudatlas.model.Attribute; +import pl.edu.mimuw.cloudatlas.model.Value; +import pl.edu.mimuw.cloudatlas.model.ValueTime; + +public class SetAttributeMessage extends StanikMessage { + private String pathName; + private Attribute attribute; + private Value value; + private ValueTime updateTimestamp; + + public SetAttributeMessage(String messageId, long timestamp, String pathName, Attribute attribute, Value value, ValueTime updateTimestamp) { + super(messageId, timestamp, Type.SET_ATTRIBUTE); + this.pathName = pathName; + this.attribute = attribute; + this.value = value; + this.updateTimestamp = updateTimestamp; + } + + public String getPathName() { + return pathName; + } + + public Attribute getAttribute() { + return attribute; + } + + public Value getValue() { + return value; + } + + public ValueTime getUpdateTimestamp() { + return updateTimestamp; + } +} 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 d86de4a..844f31c 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 @@ -7,6 +7,7 @@ public abstract class StanikMessage extends AgentMessage { public enum Type { GET_STATE, REMOVE_ZMI, + SET_ATTRIBUTE, UPDATE_ATTRIBUTES, UPDATE_QUERIES } 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 ba5e1d1..0a934cb 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 @@ -4,8 +4,8 @@ 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.QurnikMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.RemikMessage; import pl.edu.mimuw.cloudatlas.agent.messages.ResponseMessage; -import pl.edu.mimuw.cloudatlas.agent.messages.RMIMessage; import pl.edu.mimuw.cloudatlas.agent.messages.StanikMessage; /* @@ -37,8 +37,8 @@ public abstract class Module { throw new InvalidMessageType("Got a QurnikMessage in module " + moduleType.toString()); } - public void handleTyped(RMIMessage message) throws InterruptedException, InvalidMessageType { - throw new InvalidMessageType("Got an RMIMessage in module " + moduleType.toString()); + public void handleTyped(RemikMessage message) throws InterruptedException, InvalidMessageType { + throw new InvalidMessageType("Got a RemikMessage in module " + moduleType.toString()); } public void handleTyped(StanikMessage message) throws InterruptedException, InvalidMessageType { diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Qurnik.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Qurnik.java index 9b7268a..1ab8a16 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Qurnik.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Qurnik.java @@ -80,13 +80,15 @@ public class Qurnik extends Module { Interpreter interpreter = new Interpreter(zmi); AttributesMap newAttributes = new AttributesMap(); for (ValueQuery query : queries) { - try { - List<QueryResult> result = interpreter.interpretProgram(query.getQuery()); - for(QueryResult r : result) { - newAttributes.addOrChange(r.getName(), r.getValue()); + if (query != null) { + try { + List<QueryResult> result = interpreter.interpretProgram(query.getQuery()); + for(QueryResult r : result) { + newAttributes.addOrChange(r.getName(), r.getValue()); + } + } catch(InterpreterException exception) { + System.out.println("ERROR: thrown while running interpreter: " + exception.getMessage()); } - } catch(InterpreterException exception) { - System.out.println("ERROR: thrown while running interpreter: " + exception.getMessage()); } } diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/RMI.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/RMI.java deleted file mode 100644 index 1a86fc7..0000000 --- a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/RMI.java +++ /dev/null @@ -1,37 +0,0 @@ -package pl.edu.mimuw.cloudatlas.agent.modules; - -import pl.edu.mimuw.cloudatlas.agent.ApiImplementation; -import pl.edu.mimuw.cloudatlas.agent.messages.AgentMessage; -import pl.edu.mimuw.cloudatlas.agent.messages.RMIMessage; -import pl.edu.mimuw.cloudatlas.api.Api; -import pl.edu.mimuw.cloudatlas.interpreter.Main; -import pl.edu.mimuw.cloudatlas.model.ZMI; - -import java.rmi.registry.LocateRegistry; -import java.rmi.registry.Registry; -import java.rmi.server.UnicastRemoteObject; - -public class RMI extends Module { - public RMI(ModuleType moduleType) { - super(moduleType); - runRegistry(); - } - - public void runRegistry() { - try { - ZMI root = Main.createTestHierarchy2(); - ApiImplementation api = new ApiImplementation(root); - Api apiStub = - (Api) UnicastRemoteObject.exportObject(api, 0); - Registry registry = LocateRegistry.getRegistry(); - registry.rebind("Api", apiStub); - System.out.println("Agent: api bound"); - } catch (Exception e) { - System.err.println("Agent registry initialization exception:"); - e.printStackTrace(); - } - } - - public void handleTyped(RMIMessage event) throws InterruptedException { - } -} diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Remik.java b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Remik.java new file mode 100644 index 0000000..9645d71 --- /dev/null +++ b/src/main/java/pl/edu/mimuw/cloudatlas/agent/modules/Remik.java @@ -0,0 +1,54 @@ +package pl.edu.mimuw.cloudatlas.agent.modules; + +import java.util.concurrent.CompletableFuture; +import java.util.HashMap; +import java.util.Map; + + +import pl.edu.mimuw.cloudatlas.agent.messages.GetStateMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.RemikMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.RequestStateMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.ResponseMessage; + +/* + * Remik is a cute little module that allows RMI functions to interface with + * agent's asynchronous modules. + */ +public class Remik extends Module { + private Map<Long, CompletableFuture<ResponseMessage>> awaitingRequests; + private long nextRequestId = 0; + + public Remik() { + super(ModuleType.RMI); + awaitingRequests = new HashMap(); + } + + public void handleTyped(RemikMessage message) throws InvalidMessageType, InterruptedException { + switch (message.getType()) { + case REQUEST_STATE: + handleRequestState((RequestStateMessage) message); + break; + default: + throw new InvalidMessageType("This type of message cannot be handled by Remik"); + } + } + + public void handleTyped(ResponseMessage message) { + CompletableFuture<ResponseMessage> responseFuture = awaitingRequests.get(message.getRequestId()); + + if (responseFuture == null) { + System.out.println("ERROR: Remik got response for nonexistent/finished request"); + } else { + responseFuture.complete(message); + } + } + + private void handleRequestState(RequestStateMessage message) throws InterruptedException { + awaitingRequests.put(nextRequestId, message.getFuture()); + + GetStateMessage getStateMessage = new GetStateMessage("", 0, ModuleType.RMI, nextRequestId); + nextRequestId++; + + sendMessage(getStateMessage); + } +} 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 b030901..3e5b790 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 @@ -6,6 +6,7 @@ import java.util.Map.Entry; import pl.edu.mimuw.cloudatlas.agent.messages.AgentMessage; import pl.edu.mimuw.cloudatlas.agent.messages.GetStateMessage; import pl.edu.mimuw.cloudatlas.agent.messages.RemoveZMIMessage; +import pl.edu.mimuw.cloudatlas.agent.messages.SetAttributeMessage; import pl.edu.mimuw.cloudatlas.agent.messages.StateMessage; import pl.edu.mimuw.cloudatlas.agent.messages.StanikMessage; import pl.edu.mimuw.cloudatlas.agent.messages.UpdateAttributesMessage; @@ -49,6 +50,9 @@ public class Stanik extends Module { case REMOVE_ZMI: handleRemoveZMI((RemoveZMIMessage) message); break; + case SET_ATTRIBUTE: + handleSetAttribte((SetAttributeMessage) message); + break; case UPDATE_ATTRIBUTES: handleUpdateAttributes((UpdateAttributesMessage) message); break; @@ -85,6 +89,27 @@ public class Stanik extends Module { } } + /* + * Always adds the new attribute. + * The zone must already exist. + * The zone's timestamp will be the maximum of its current timestamp or the + * timestamp provided with the new value. + */ + public void handleSetAttribte(SetAttributeMessage message) { + try { + ZMI zmi = hierarchy.findDescendant(new PathName(message.getPathName())); + ValueTime updateTimestamp = message.getUpdateTimestamp(); + ValueTime currentTimestamp = (ValueTime) zmi.getAttributes().getOrNull("timestamp"); + if (ValueUtils.valueLower(currentTimestamp, updateTimestamp)) { + zmi.getAttributes().addOrChange("timestamp", updateTimestamp); + } + + zmi.getAttributes().addOrChange(message.getAttribute(), message.getValue()); + } catch (ZMI.NoSuchZoneException e) { + System.out.println("DEBUG: trying to set attribute in zone that doesn't exist"); + } + } + public void handleUpdateAttributes(UpdateAttributesMessage message) { try { validateUpdateAttributesMessage(message); diff --git a/src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Main.java b/src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Main.java index f91f25e..1e0bb4f 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Main.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/interpreter/Main.java @@ -277,16 +277,19 @@ public class Main { ZMI root = new ZMI(); root.getAttributes().add("level", new ValueInt(0l)); root.getAttributes().add("name", new ValueString(null)); + root.getAttributes().add("timestamp", new ValueTime(10l)); ZMI uw = new ZMI(root); root.addSon(uw); uw.getAttributes().add("level", new ValueInt(1l)); uw.getAttributes().add("name", new ValueString("uw")); + uw.getAttributes().add("timestamp", new ValueTime(10l)); ZMI pjwstk = new ZMI(root); root.addSon(pjwstk); pjwstk.getAttributes().add("level", new ValueInt(1l)); pjwstk.getAttributes().add("name", new ValueString("pjwstk")); + pjwstk.getAttributes().add("timestamp", new ValueTime(10l)); ZMI violet07 = new ZMI(uw); uw.addSon(violet07); 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 54fbf43..cda0296 100644 --- a/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java +++ b/src/main/java/pl/edu/mimuw/cloudatlas/model/ZMI.java @@ -219,8 +219,12 @@ public class ZMI implements Cloneable, Serializable { * @return a <code>PathName</code> object representing this zone */ public PathName getPathName() { - String name = ((ValueString)getAttributes().get("name")).getValue(); - return getFather() == null? PathName.ROOT : getFather().getPathName().levelDown(name); + if (getFather() == null) { + return PathName.ROOT; + } else { + String name = ((ValueString)getAttributes().get("name")).getValue(); + return getFather().getPathName().levelDown(name); + } } public static ZMI deserialize(InputStream in) { |