ONOS-4972: Augmented UiModelEvents to hold context and memo about the event subject.
Change-Id: Id0e28d8d5d3eb80fba36e0392cc80167effd39bc
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiDeviceLink.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiDeviceLink.java
index 3eaa827..75e2560 100644
--- a/core/api/src/main/java/org/onosproject/ui/model/topo/UiDeviceLink.java
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiDeviceLink.java
@@ -61,12 +61,12 @@
@Override
public String endPortA() {
- return portA.toString();
+ return portA == null ? null : portA.toString();
}
@Override
public String endPortB() {
- return portB.toString();
+ return portB == null ? null : portB.toString();
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
index 328207d..e1d403d 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
@@ -96,6 +96,8 @@
UiSharedTopologyModel sharedModel = directory.get(UiSharedTopologyModel.class);
UiTopoLayoutService layoutService = directory.get(UiTopoLayoutService.class);
+ sharedModel.injectJsonifier(t2json);
+
topoSession = new UiTopoSession(this, t2json, sharedModel, layoutService);
// FIXME: this is temporary to prevent unhandled events being set to GUI...
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
index ebc20fe..6234391 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
@@ -44,6 +44,7 @@
import org.onosproject.ui.impl.topo.model.UiModelEvent;
import org.onosproject.ui.model.topo.UiClusterMember;
import org.onosproject.ui.model.topo.UiDevice;
+import org.onosproject.ui.model.topo.UiElement;
import org.onosproject.ui.model.topo.UiHost;
import org.onosproject.ui.model.topo.UiLink;
import org.onosproject.ui.model.topo.UiNode;
@@ -82,6 +83,8 @@
private static final String HOST = "host";
private static final String TYPE = "type";
private static final String SUBJECT = "subject";
+ private static final String DATA = "data";
+ private static final String MEMO = "memo";
private final Logger log = LoggerFactory.getLogger(getClass());
@@ -267,6 +270,28 @@
}
/**
+ * Creates a JSON representation of a UI element.
+ *
+ * @param element the source element
+ * @return a JSON representation of that element
+ */
+ public ObjectNode jsonUiElement(UiElement element) {
+ if (element instanceof UiNode) {
+ return json((UiNode) element);
+ }
+ if (element instanceof UiLink) {
+ return json((UiLink) element);
+ }
+
+ // TODO: UiClusterMember
+
+ // Unrecognized UiElement class
+ return objectNode()
+ .put("warning", "unknown UiElement... cannot encode")
+ .put("javaclass", element.getClass().toString());
+ }
+
+ /**
* Creates a JSON representation of a UI model event.
*
* @param modelEvent the source model event
@@ -276,6 +301,8 @@
ObjectNode payload = objectNode();
payload.put(TYPE, enumToString(modelEvent.type()));
payload.put(SUBJECT, modelEvent.subject().idAsString());
+ payload.set(DATA, modelEvent.data());
+ payload.put(MEMO, modelEvent.memo());
return payload;
}
@@ -408,14 +435,17 @@
}
private ObjectNode json(UiSynthLink sLink) {
- UiLink uLink = sLink.link();
+ return json(sLink.link());
+ }
+
+ private ObjectNode json(UiLink link) {
ObjectNode data = objectNode()
- .put("id", uLink.idAsString())
- .put("epA", uLink.endPointA())
- .put("epB", uLink.endPointB())
- .put("type", uLink.type());
- String pA = uLink.endPortA();
- String pB = uLink.endPortB();
+ .put("id", link.idAsString())
+ .put("epA", link.endPointA())
+ .put("epB", link.endPointB())
+ .put("type", link.type());
+ String pA = link.endPortA();
+ String pB = link.endPortB();
if (pA != null) {
data.put("portA", pA);
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
index f98ab7b..24fda23 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/ModelCache.java
@@ -16,6 +16,7 @@
package org.onosproject.ui.impl.topo.model;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.NodeId;
import org.onosproject.cluster.RoleInfo;
@@ -30,6 +31,7 @@
import org.onosproject.net.region.Region;
import org.onosproject.net.region.RegionId;
import org.onosproject.ui.UiTopoLayoutService;
+import org.onosproject.ui.impl.topo.Topo2Jsonifier;
import org.onosproject.ui.model.ServiceBundle;
import org.onosproject.ui.model.topo.UiClusterMember;
import org.onosproject.ui.model.topo.UiDevice;
@@ -70,6 +72,10 @@
class ModelCache {
private static final String E_NO_ELEMENT = "Tried to remove non-member {}: {}";
+ private static final String MEMO_ADDED = "added";
+ private static final String MEMO_UPDATED = "updated";
+ private static final String MEMO_REMOVED = "removed";
+ private static final String MEMO_MOVED = "moved";
private static final Logger log = LoggerFactory.getLogger(ModelCache.class);
@@ -77,6 +83,8 @@
private final EventDispatcher dispatcher;
private final UiTopology uiTopology = new UiTopology();
+ private Topo2Jsonifier t2json;
+
ModelCache(ServiceBundle services, EventDispatcher eventDispatcher) {
this.services = services;
this.dispatcher = eventDispatcher;
@@ -87,8 +95,13 @@
return "ModelCache{" + uiTopology + "}";
}
- private void postEvent(UiModelEvent.Type type, UiElement subject) {
- dispatcher.post(new UiModelEvent(type, subject));
+ private void postEvent(UiModelEvent.Type type, UiElement subject, String memo) {
+ ObjectNode data = t2json != null ? t2json.jsonUiElement(subject) : null;
+ dispatcher.post(new UiModelEvent(type, subject, data, memo));
+ }
+
+ void injectJsonifier(Topo2Jsonifier t2json) {
+ this.t2json = t2json;
}
void clear() {
@@ -131,13 +144,15 @@
// invoked from UiSharedTopologyModel cluster event listener
void addOrUpdateClusterMember(ControllerNode cnode) {
NodeId id = cnode.id();
+ String memo = MEMO_UPDATED;
UiClusterMember member = uiTopology.findClusterMember(id);
if (member == null) {
member = addNewClusterMember(cnode);
+ memo = MEMO_ADDED;
}
updateClusterMember(member);
- postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member);
+ postEvent(CLUSTER_MEMBER_ADDED_OR_UPDATED, member, memo);
}
// package private for unit test access
@@ -151,7 +166,7 @@
UiClusterMember member = uiTopology.findClusterMember(id);
if (member != null) {
uiTopology.remove(member);
- postEvent(CLUSTER_MEMBER_REMOVED, member);
+ postEvent(CLUSTER_MEMBER_REMOVED, member, MEMO_REMOVED);
} else {
log.warn(E_NO_ELEMENT, "cluster node", id);
}
@@ -256,13 +271,15 @@
// invoked from UiSharedTopologyModel region listener
void addOrUpdateRegion(Region region) {
RegionId id = region.id();
+ String memo = MEMO_UPDATED;
UiRegion uiRegion = uiTopology.findRegion(id);
if (uiRegion == null) {
uiRegion = addNewRegion(region);
+ memo = MEMO_ADDED;
}
updateRegion(uiRegion);
- postEvent(REGION_ADDED_OR_UPDATED, uiRegion);
+ postEvent(REGION_ADDED_OR_UPDATED, uiRegion, memo);
}
// package private for unit test access
@@ -276,7 +293,7 @@
UiRegion uiRegion = uiTopology.findRegion(id);
if (uiRegion != null) {
uiTopology.remove(uiRegion);
- postEvent(REGION_REMOVED, uiRegion);
+ postEvent(REGION_REMOVED, uiRegion, MEMO_REMOVED);
} else {
log.warn(E_NO_ELEMENT, "region", id);
}
@@ -313,14 +330,16 @@
// invoked from UiSharedTopologyModel device listener
void addOrUpdateDevice(Device device) {
DeviceId id = device.id();
+ String memo = MEMO_UPDATED;
UiDevice uiDevice = uiTopology.findDevice(id);
if (uiDevice == null) {
uiDevice = addNewDevice(device);
+ memo = MEMO_ADDED;
} else {
updateDevice(uiDevice);
}
- postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice);
+ postEvent(DEVICE_ADDED_OR_UPDATED, uiDevice, memo);
}
// package private for unit test access
@@ -334,7 +353,7 @@
UiDevice uiDevice = uiTopology.findDevice(id);
if (uiDevice != null) {
uiTopology.remove(uiDevice);
- postEvent(DEVICE_REMOVED, uiDevice);
+ postEvent(DEVICE_REMOVED, uiDevice, MEMO_REMOVED);
} else {
log.warn(E_NO_ELEMENT, "device", id);
}
@@ -378,13 +397,15 @@
// invoked from UiSharedTopologyModel link listener
void addOrUpdateDeviceLink(Link link) {
UiLinkId id = uiLinkId(link);
+ String memo = MEMO_UPDATED;
UiDeviceLink uiDeviceLink = uiTopology.findDeviceLink(id);
if (uiDeviceLink == null) {
uiDeviceLink = addNewDeviceLink(id);
+ memo = MEMO_ADDED;
}
updateDeviceLink(uiDeviceLink, link);
- postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink);
+ postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, memo);
}
// package private for unit test access
@@ -399,10 +420,10 @@
if (uiDeviceLink != null) {
boolean remaining = uiDeviceLink.detachBackingLink(link);
if (remaining) {
- postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink);
+ postEvent(LINK_ADDED_OR_UPDATED, uiDeviceLink, MEMO_UPDATED);
} else {
uiTopology.remove(uiDeviceLink);
- postEvent(LINK_REMOVED, uiDeviceLink);
+ postEvent(LINK_REMOVED, uiDeviceLink, MEMO_REMOVED);
}
} else {
log.warn(E_NO_ELEMENT, "Device link", id);
@@ -474,13 +495,15 @@
// invoked from UiSharedTopologyModel host listener
void addOrUpdateHost(Host host) {
HostId id = host.id();
+ String memo = MEMO_UPDATED;
UiHost uiHost = uiTopology.findHost(id);
if (uiHost == null) {
uiHost = addNewHost(host);
+ memo = MEMO_ADDED;
}
updateHost(uiHost, host);
- postEvent(HOST_ADDED_OR_UPDATED, uiHost);
+ postEvent(HOST_ADDED_OR_UPDATED, uiHost, memo);
}
// invoked from UiSharedTopologyModel host listener
@@ -488,7 +511,7 @@
UiHost uiHost = uiTopology.findHost(prevHost.id());
if (uiHost != null) {
updateHost(uiHost, host);
- postEvent(HOST_MOVED, uiHost);
+ postEvent(HOST_MOVED, uiHost, MEMO_MOVED);
} else {
log.warn(E_NO_ELEMENT, "host", prevHost.id());
}
@@ -507,7 +530,7 @@
UiEdgeLink edgeLink = uiTopology.findEdgeLink(uiHost.edgeLinkId());
uiTopology.remove(edgeLink);
uiTopology.remove(uiHost);
- postEvent(HOST_REMOVED, uiHost);
+ postEvent(HOST_REMOVED, uiHost, MEMO_REMOVED);
} else {
log.warn(E_NO_ELEMENT, "host", id);
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiModelEvent.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiModelEvent.java
index 10a3523..4e2a15c 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiModelEvent.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiModelEvent.java
@@ -16,6 +16,7 @@
package org.onosproject.ui.impl.topo.model;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.event.AbstractEvent;
import org.onosproject.ui.model.topo.UiElement;
@@ -24,10 +25,9 @@
*/
public class UiModelEvent extends AbstractEvent<UiModelEvent.Type, UiElement> {
- protected UiModelEvent(Type type, UiElement subject) {
- super(type, subject);
- }
-
+ /**
+ * Enumeration of event types.
+ */
enum Type {
CLUSTER_MEMBER_ADDED_OR_UPDATED,
CLUSTER_MEMBER_REMOVED,
@@ -45,4 +45,42 @@
HOST_MOVED,
HOST_REMOVED
}
+
+ private final ObjectNode data;
+ private final String memo;
+
+ /**
+ * Creates a UI model event. Note that the memo field can be used to
+ * pass a hint to the listener about the event.
+ *
+ * @param type event type
+ * @param subject subject of the event
+ * @param data data containing details of the subject
+ * @param memo a note about the event
+ */
+ protected UiModelEvent(Type type, UiElement subject, ObjectNode data,
+ String memo) {
+ super(type, subject);
+ this.data = data;
+ this.memo = memo;
+ }
+
+ /**
+ * Returns the data of the subject.
+ *
+ * @return the subject data
+ */
+ public ObjectNode data() {
+ return data;
+ }
+
+ /**
+ * Returns the memo.
+ *
+ * @return the memo
+ */
+ public String memo() {
+ return memo;
+ }
+
}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
index 2a66b8e..80d134a 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/model/UiSharedTopologyModel.java
@@ -22,7 +22,6 @@
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
-import org.onlab.util.Tools;
import org.onosproject.cluster.ClusterEvent;
import org.onosproject.cluster.ClusterEventListener;
import org.onosproject.cluster.ClusterService;
@@ -61,6 +60,7 @@
import org.onosproject.net.statistic.StatisticService;
import org.onosproject.net.topology.TopologyService;
import org.onosproject.ui.UiTopoLayoutService;
+import org.onosproject.ui.impl.topo.Topo2Jsonifier;
import org.onosproject.ui.impl.topo.UiTopoSession;
import org.onosproject.ui.model.ServiceBundle;
import org.onosproject.ui.model.topo.UiClusterMember;
@@ -75,7 +75,9 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
/**
* Service that creates and maintains the UI-model of the network topology.
@@ -143,7 +145,7 @@
@Activate
protected void activate() {
cache = new ModelCache(new DefaultServiceBundle(), eventDispatcher);
- eventHandler = Executors.newSingleThreadExecutor(Tools.groupedThreads("onos/ui/topo", "event-handler", log));
+ eventHandler = newSingleThreadExecutor(groupedThreads("onos/ui/topo", "event-handler", log));
eventDispatcher.addSink(UiModelEvent.class, listenerRegistry);
@@ -182,6 +184,17 @@
log.info("Stopped");
}
+ /**
+ * Injects an instance of the JSON-ifier (which has been bound to the
+ * services (link, host, device, ...)) to be passed on to the Model Cache,
+ * for use in forming UiModelEvent payloads.
+ *
+ * @param t2json JSONifier
+ */
+ public void injectJsonifier(Topo2Jsonifier t2json) {
+ cache.injectJsonifier(t2json);
+ }
+
/**
* Registers a UI topology session with the topology model.