Simon Hunt

GUI -- Further work on refactoring Topology View server side code.

- includes some cleanup of UiMessageHandler and subclasses thereof.

Change-Id: Ie48d830447a4abe1b3accda41a934530a4d55d0e
......@@ -108,7 +108,7 @@ public class IntentPerfUi {
private boolean streamingEnabled = false;
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new IntentPerfStart(),
new IntentPerfStop()
......@@ -135,8 +135,8 @@ public class IntentPerfUi {
private ObjectNode sampleNode(Sample sample) {
ObjectNode sampleNode = mapper.createObjectNode();
ArrayNode an = mapper.createArrayNode();
ObjectNode sampleNode = objectNode();
ArrayNode an = arrayNode();
sampleNode.put("time", sample.time);
sampleNode.set("data", an);
......
......@@ -83,6 +83,7 @@ public abstract class RequestHandler {
* @param sid message sequence identifier
* @param payload message payload
*/
// TODO: remove sid from signature
protected void sendMessage(String eventType, long sid, ObjectNode payload) {
parent.connection().sendMessage(eventType, sid, payload);
}
......@@ -107,6 +108,7 @@ public abstract class RequestHandler {
* @param sid sequence identifier
* @param payload message payload
*/
// TODO: remove sid from signature
protected void chain(String eventType, long sid, ObjectNode payload) {
parent.exec(eventType, sid, payload);
}
......@@ -114,11 +116,25 @@ public abstract class RequestHandler {
// ===================================================================
// FIXME : Javadocs
/**
* Returns the specified node property as a string.
*
* @param node message event
* @param key property name
* @return property as a string
*/
protected String string(ObjectNode node, String key) {
return JsonUtils.string(node, key);
}
/**
* Returns the specified node property as a string, with a default fallback.
*
* @param node object node
* @param key property name
* @param defValue fallback value if property is absent
* @return property as a string
*/
protected String string(ObjectNode node, String key, String defValue) {
return JsonUtils.string(node, key, defValue);
}
......
......@@ -36,6 +36,7 @@ public interface UiConnection {
* @param sid message sequence number
* @param payload message payload
*/
// TODO: remove sid parameter
void sendMessage(String type, long sid, ObjectNode payload);
}
\ No newline at end of file
......
......@@ -16,6 +16,7 @@
package org.onosproject.ui;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onlab.osgi.ServiceDirectory;
import org.slf4j.Logger;
......@@ -31,41 +32,47 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of an entity capable of processing a JSON message from the user
* Abstraction of an entity capable of processing JSON messages from the user
* interface client.
* <p>
* The message is a JSON object with the following structure:
* The message structure is:
* </p>
* <pre>
* {
* "type": "<em>event-type</em>",
* "sid": "<em>sequence-number</em>",
* "payload": {
* <em>arbitrary JSON object structure</em>
* }
* }
* </pre>
* On {@link #init initialization} the handler will create and cache
* {@link RequestHandler} instances, each of which are bound to a particular
* <em>event-type</em>. On {@link #process arrival} of a new message,
* the <em>event-type</em> is determined, and the message dispatched to the
* corresponding <em>RequestHandler</em>'s
* {@link RequestHandler#process process} method.
*/
public abstract class UiMessageHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Map<String, RequestHandler> handlerMap = new HashMap<>();
private final ObjectMapper mapper = new ObjectMapper();
private UiConnection connection;
private ServiceDirectory directory;
/**
* Mapper for creating ObjectNodes and ArrayNodes etc.
*/
protected final ObjectMapper mapper = new ObjectMapper();
/**
* Subclasses must return the collection of handlers for the
* message types they handle.
* Subclasses must create and return the collection of request handlers
* for the message types they handle.
* <p>
* Note that request handlers should be stateless. When we are
* {@link #destroy destroyed}, we will simply drop our references to them
* and allow them to be garbage collected.
*
* @return the message handler instances
*/
protected abstract Collection<RequestHandler> getHandlers();
protected abstract Collection<RequestHandler> createRequestHandlers();
/**
* Returns the set of message types which this handler is capable of
......@@ -84,10 +91,9 @@ public abstract class UiMessageHandler {
*/
public void process(ObjectNode message) {
String type = JsonUtils.eventType(message);
// TODO: remove sid
long sid = JsonUtils.sid(message);
ObjectNode payload = JsonUtils.payload(message);
exec(type, sid, payload);
// TODO: remove sid
exec(type, 0, payload);
}
/**
......@@ -99,21 +105,10 @@ public abstract class UiMessageHandler {
*/
// TODO: remove sid from signature
void exec(String eventType, long sid, ObjectNode payload) {
RequestHandler handler = handlerMap.get(eventType);
if (handler != null) {
RequestHandler requestHandler = handlerMap.get(eventType);
if (requestHandler != null) {
log.debug("process {} event...", eventType);
handler.process(sid, payload);
}
}
private void bindHandlers() {
Collection<RequestHandler> handlers = getHandlers();
checkNotNull(handlers, "Handlers cannot be null");
checkArgument(!handlers.isEmpty(), "Handlers cannot be empty");
for (RequestHandler h : handlers) {
h.setParent(this);
handlerMap.put(h.eventType(), h);
requestHandler.process(sid, payload);
}
}
......@@ -127,7 +122,15 @@ public abstract class UiMessageHandler {
public void init(UiConnection connection, ServiceDirectory directory) {
this.connection = connection;
this.directory = directory;
bindHandlers();
Collection<RequestHandler> handlers = createRequestHandlers();
checkNotNull(handlers, "Handlers cannot be null");
checkArgument(!handlers.isEmpty(), "Handlers cannot be empty");
for (RequestHandler h : handlers) {
h.setParent(this);
handlerMap.put(h.eventType(), h);
}
}
/**
......@@ -136,6 +139,7 @@ public abstract class UiMessageHandler {
public void destroy() {
this.connection = null;
this.directory = null;
handlerMap.clear();
}
/**
......@@ -168,4 +172,21 @@ public abstract class UiMessageHandler {
return directory.get(serviceClass);
}
/**
* Returns a freshly minted object node.
*
* @return new object node
*/
protected ObjectNode objectNode() {
return mapper.createObjectNode();
}
/**
* Returns a freshly minted array node.
*
* @return new array node
*/
protected ArrayNode arrayNode() {
return mapper.createArrayNode();
}
}
......
......@@ -53,6 +53,7 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
private static final String TOPO_START = "topoStart";
private static final String TOPO_STOP = "topoStop";
private static final String REQ_SUMMARY = "requestSummary";
private static final String CANCEL_SUMMARY = "cancelSummary";
private final Logger log = LoggerFactory.getLogger(getClass());
......@@ -80,6 +81,13 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
currentSummaryGenerator = defaultSummaryGenerator;
}
@Override
public void destroy() {
// cancelAllMonitoring();
// stopListeningToModel();
super.destroy();
}
private String getVersion() {
String ver = directory.get(CoreService.class).version().toString();
......@@ -88,11 +96,12 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new TopoStart(),
new TopoStop(),
new ReqSummary()
new ReqSummary(),
new CancelSummary()
// TODO: add more handlers here.....
);
}
......@@ -157,6 +166,17 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
}
}
private final class CancelSummary extends RequestHandler {
private CancelSummary() {
super(CANCEL_SUMMARY);
}
@Override
public void process(long sid, ObjectNode payload) {
modelService.stopSummaryMonitoring();
}
}
// =====================================================================
private final class DefSummaryGenerator extends AbstractSummaryGenerator {
......
......@@ -57,7 +57,7 @@ public class ApplicationViewMessageHandler extends UiMessageHandler {
};
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new AppDataRequest(),
new AppMgmtRequest()
......
......@@ -54,7 +54,7 @@ public class ClusterViewMessageHandler extends UiMessageHandler {
private static final String ICON_ID_OFFLINE = "inactive";
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(new ClusterDataRequest());
}
......
......@@ -84,7 +84,7 @@ public class DeviceViewMessageHandler extends UiMessageHandler {
private static final String ICON_ID_OFFLINE = "inactive";
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new DataRequestHandler(),
new DetailRequestHandler()
......
......@@ -65,7 +65,7 @@ public class FlowViewMessageHandler extends UiMessageHandler {
};
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(new FlowDataRequest());
}
......
......@@ -56,7 +56,7 @@ public class GroupViewMessageHandler extends UiMessageHandler {
};
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(new GroupDataRequest());
}
......
......@@ -56,7 +56,7 @@ public class HostViewMessageHandler extends UiMessageHandler {
};
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(new HostDataRequest());
}
......
......@@ -63,7 +63,7 @@ public class IntentViewMessageHandler extends UiMessageHandler {
};
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(new IntentDataRequest());
}
......
......@@ -59,7 +59,7 @@ public class LinkViewMessageHandler extends UiMessageHandler {
private static final String ICON_ID_OFFLINE = "inactive";
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(new LinkDataRequest());
}
......
......@@ -54,7 +54,7 @@ public class PortViewMessageHandler extends UiMessageHandler {
};
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(new PortDataRequest());
}
......
......@@ -167,7 +167,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
}
@Override
protected Collection<RequestHandler> getHandlers() {
protected Collection<RequestHandler> createRequestHandlers() {
return ImmutableSet.of(
new TopoStart(),
new TopoStop(),
......@@ -254,8 +254,8 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
@Override
public void process(long sid, ObjectNode payload) {
ObjectNode root = mapper.createObjectNode();
ArrayNode names = mapper.createArrayNode();
ObjectNode root = objectNode();
ArrayNode names = arrayNode();
get(SpriteService.class).getNames().forEach(names::add);
root.set("names", names);
sendMessage("spriteListResponse", sid, root);
......@@ -270,7 +270,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
@Override
public void process(long sid, ObjectNode payload) {
String name = string(payload, "name");
ObjectNode root = mapper.createObjectNode();
ObjectNode root = objectNode();
root.set("data", get(SpriteService.class).get(name));
sendMessage("spriteDataResponse", sid, root);
}
......
......@@ -171,7 +171,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Produces JSON structure from annotations.
private JsonNode props(Annotations annotations) {
ObjectNode props = mapper.createObjectNode();
ObjectNode props = objectNode();
if (annotations != null) {
for (String key : annotations.keys()) {
props.put(key, annotations.value(key));
......@@ -197,7 +197,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Produces a log message event bound to the client.
private ObjectNode message(String severity, long id, String message) {
ObjectNode payload = mapper.createObjectNode()
ObjectNode payload = objectNode()
.put("severity", severity)
.put("message", message);
......@@ -266,14 +266,14 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
protected ObjectNode instanceMessage(ClusterEvent event, String messageType) {
ControllerNode node = event.subject();
int switchCount = mastershipService.getDevicesOf(node.id()).size();
ObjectNode payload = mapper.createObjectNode()
ObjectNode payload = objectNode()
.put("id", node.id().toString())
.put("ip", node.ip().toString())
.put("online", clusterService.getState(node.id()) == ACTIVE)
.put("uiAttached", node.equals(clusterService.getLocalNode()))
.put("switches", switchCount);
ArrayNode labels = mapper.createArrayNode();
ArrayNode labels = arrayNode();
labels.add(node.id().toString());
labels.add(node.ip().toString());
......@@ -291,7 +291,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Produces a device event message to the client.
protected ObjectNode deviceMessage(DeviceEvent event) {
Device device = event.subject();
ObjectNode payload = mapper.createObjectNode()
ObjectNode payload = objectNode()
.put("id", device.id().toString())
.put("type", device.type().toString().toLowerCase())
.put("online", deviceService.isAvailable(device.id()))
......@@ -299,7 +299,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Generate labels: id, chassis id, no-label, optional-name
String name = device.annotations().value(AnnotationKeys.NAME);
ArrayNode labels = mapper.createArrayNode();
ArrayNode labels = arrayNode();
labels.add("");
labels.add(isNullOrEmpty(name) ? device.id().toString() : name);
labels.add(device.id().toString());
......@@ -318,7 +318,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Produces a link event message to the client.
protected ObjectNode linkMessage(LinkEvent event) {
Link link = event.subject();
ObjectNode payload = mapper.createObjectNode()
ObjectNode payload = objectNode()
.put("id", compactLinkString(link))
.put("type", link.type().toString().toLowerCase())
.put("online", link.state() == Link.State.ACTIVE)
......@@ -336,13 +336,13 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
protected ObjectNode hostMessage(HostEvent event) {
Host host = event.subject();
String hostType = host.annotations().value(AnnotationKeys.TYPE);
ObjectNode payload = mapper.createObjectNode()
ObjectNode payload = objectNode()
.put("id", host.id().toString())
.put("type", isNullOrEmpty(hostType) ? "endstation" : hostType)
.put("ingress", compactLinkString(edgeLink(host, true)))
.put("egress", compactLinkString(edgeLink(host, false)));
payload.set("cp", hostConnect(mapper, host.location()));
payload.set("labels", labels(mapper, ip(host.ipAddresses()),
payload.set("cp", hostConnect(host.location()));
payload.set("labels", labels(ip(host.ipAddresses()),
host.mac().toString()));
payload.set("props", props(host.annotations()));
addGeoLocation(host, payload);
......@@ -354,15 +354,15 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
}
// Encodes the specified host location into a JSON object.
private ObjectNode hostConnect(ObjectMapper mapper, HostLocation location) {
return mapper.createObjectNode()
private ObjectNode hostConnect(HostLocation location) {
return objectNode()
.put("device", location.deviceId().toString())
.put("port", location.port().toLong());
}
// Encodes the specified list of labels a JSON array.
private ArrayNode labels(ObjectMapper mapper, String... labels) {
ArrayNode json = mapper.createArrayNode();
private ArrayNode labels(String... labels) {
ArrayNode json = arrayNode();
for (String label : labels) {
json.add(label);
}
......@@ -402,7 +402,7 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
if (slat != null && slng != null && !slat.isEmpty() && !slng.isEmpty()) {
double lat = Double.parseDouble(slat);
double lng = Double.parseDouble(slng);
ObjectNode loc = mapper.createObjectNode()
ObjectNode loc = objectNode()
.put("type", "latlng").put("lat", lat).put("lng", lng);
payload.set("location", loc);
}
......@@ -531,22 +531,22 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Produces JSON message to trigger traffic overview visualization
protected ObjectNode trafficSummaryMessage() {
ObjectNode payload = mapper.createObjectNode();
ArrayNode paths = mapper.createArrayNode();
ObjectNode payload = objectNode();
ArrayNode paths = arrayNode();
payload.set("paths", paths);
ObjectNode pathNodeN = mapper.createObjectNode();
ArrayNode linksNodeN = mapper.createArrayNode();
ArrayNode labelsN = mapper.createArrayNode();
ObjectNode pathNodeN = objectNode();
ArrayNode linksNodeN = arrayNode();
ArrayNode labelsN = arrayNode();
pathNodeN.put("class", "plain").put("traffic", false);
pathNodeN.set("links", linksNodeN);
pathNodeN.set("labels", labelsN);
paths.add(pathNodeN);
ObjectNode pathNodeT = mapper.createObjectNode();
ArrayNode linksNodeT = mapper.createArrayNode();
ArrayNode labelsT = mapper.createArrayNode();
ObjectNode pathNodeT = objectNode();
ArrayNode linksNodeT = arrayNode();
ArrayNode labelsT = arrayNode();
pathNodeT.put("class", "secondary").put("traffic", true);
pathNodeT.set("links", linksNodeT);
......@@ -581,8 +581,8 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Produces JSON message to trigger flow overview visualization
protected ObjectNode flowSummaryMessage(Set<Device> devices) {
ObjectNode payload = mapper.createObjectNode();
ArrayNode paths = mapper.createArrayNode();
ObjectNode payload = objectNode();
ArrayNode paths = arrayNode();
payload.set("paths", paths);
for (Device device : devices) {
......@@ -595,9 +595,9 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
}
private void addLinkFlows(Link link, ArrayNode paths, Integer count) {
ObjectNode pathNode = mapper.createObjectNode();
ArrayNode linksNode = mapper.createArrayNode();
ArrayNode labels = mapper.createArrayNode();
ObjectNode pathNode = objectNode();
ArrayNode linksNode = arrayNode();
ArrayNode labels = arrayNode();
boolean noFlows = count == null || count == 0;
pathNode.put("class", noFlows ? "secondary" : "primary");
pathNode.put("traffic", false);
......@@ -610,8 +610,8 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Produces JSON message to trigger traffic visualization
protected ObjectNode trafficMessage(TrafficClass... trafficClasses) {
ObjectNode payload = mapper.createObjectNode();
ArrayNode paths = mapper.createArrayNode();
ObjectNode payload = objectNode();
ArrayNode paths = arrayNode();
payload.set("paths", paths);
// Classify links based on their traffic traffic first...
......@@ -624,10 +624,10 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
String tc = (biLink.classes() + (hasTraffic ? " animated" : "")).trim();
ObjectNode pathNode = pathNodes.get(tc);
if (pathNode == null) {
pathNode = mapper.createObjectNode()
pathNode = objectNode()
.put("class", tc).put("traffic", hasTraffic);
pathNode.set("links", mapper.createArrayNode());
pathNode.set("labels", mapper.createArrayNode());
pathNode.set("links", arrayNode());
pathNode.set("labels", arrayNode());
pathNodes.put(tc, pathNode);
paths.add(pathNode);
}
......@@ -701,11 +701,11 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// connectivity intent
protected void addPathTraffic(ArrayNode paths, String type, String trafficType,
Iterable<Link> links) {
ObjectNode pathNode = mapper.createObjectNode();
ArrayNode linksNode = mapper.createArrayNode();
ObjectNode pathNode = objectNode();
ArrayNode linksNode = arrayNode();
if (links != null) {
ArrayNode labels = mapper.createArrayNode();
ArrayNode labels = arrayNode();
boolean hasTraffic = false;
for (Link link : links) {
if (isInfrastructureEgress(link)) {
......@@ -767,10 +767,10 @@ public abstract class TopologyViewMessageHandlerBase extends UiMessageHandler {
// Produces JSON property details.
private ObjectNode json(String id, String type, Prop... props) {
ObjectMapper mapper = new ObjectMapper();
ObjectNode result = mapper.createObjectNode()
ObjectNode result = objectNode()
.put("id", id).put("type", type);
ObjectNode pnode = mapper.createObjectNode();
ArrayNode porder = mapper.createArrayNode();
ObjectNode pnode = objectNode();
ArrayNode porder = arrayNode();
for (Prop p : props) {
porder.add(p.key);
pnode.put(p.key, p.value);
......
......@@ -41,7 +41,7 @@ public class UiWebSocket
private static final Logger log = LoggerFactory.getLogger(UiWebSocket.class);
private static final long MAX_AGE_MS = 15000;
private static final long MAX_AGE_MS = 15_000;
private static final byte PING = 0x9;
private static final byte PONG = 0xA;
......@@ -83,8 +83,10 @@ public class UiWebSocket
* @return true if idle or closed
*/
synchronized boolean isIdle() {
boolean idle = (System.currentTimeMillis() - lastActive) > MAX_AGE_MS;
long quietFor = System.currentTimeMillis() - lastActive;
boolean idle = quietFor > MAX_AGE_MS;
if (idle || (connection != null && !connection.isOpen())) {
log.debug("IDLE (or closed) websocket [{} ms]", quietFor);
return true;
} else if (connection != null) {
try {
......@@ -108,7 +110,8 @@ public class UiWebSocket
@Override
public synchronized void onClose(int closeCode, String message) {
destroyHandlers();
log.info("GUI client disconnected");
log.info("GUI client disconnected [close-code={}, message={}]",
closeCode, message);
}
@Override
......
......@@ -50,6 +50,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import static org.onosproject.cluster.ClusterEvent.Type.INSTANCE_ADDED;
import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
......@@ -67,6 +69,9 @@ import static org.onosproject.ui.impl.topo.TopoUiEvent.Type.SUMMARY_UPDATE;
@Service
public class TopoUiModelManager implements TopoUiModelService {
// TODO: put back to 30,000 ms for production
private static final long SUMMARY_PERIOD = 15_000;
private final Logger log = LoggerFactory.getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
......@@ -105,6 +110,11 @@ public class TopoUiModelManager implements TopoUiModelService {
private final TopoMessageFactory messageFactory = new TopoMessageFactory();
private final MetaDb metaDb = new MetaDb();
private final Timer timer = new Timer("topology-view");
private TimerTask summaryTask = null;
private boolean summaryRunning = false;
@Activate
public void activate() {
......@@ -135,7 +145,8 @@ public class TopoUiModelManager implements TopoUiModelService {
// causes the listener (for an AltTopoViewMessageHandler instance) to
// be removed.
// ==== Somehow need to tie this in to the GUI-disconnected event.
// This probably requires client-generated heartbeat messages to
// Keep the connection alive.
@Override
......@@ -159,16 +170,32 @@ public class TopoUiModelManager implements TopoUiModelService {
}
@Override
public void startSummaryMonitoring() {
// TODO: set up periodic monitoring task
// send a summary now, and periodically...
public synchronized void startSummaryMonitoring() {
// first, cancel previous task if not canceled already
stopSummaryMonitoring();
// create and start a summary task, to execute with no delay, and
// every SUMMARY_PERIOD milliseconds thereafter.
summaryTask = new TimerTask() {
@Override
public void run() {
if (summaryRunning) {
post(new TopoUiEvent(SUMMARY_UPDATE, null));
}
}
};
@Override
public void stopSummaryMonitoring() {
// TODO: cancel monitoring task
timer.schedule(summaryTask, 0, SUMMARY_PERIOD);
summaryRunning = true;
}
@Override
public synchronized void stopSummaryMonitoring() {
if (summaryTask != null) {
summaryTask.cancel();
summaryTask = null;
}
summaryRunning = false;
}
@Override
......