Simon Hunt
Committed by Gerrit Code Review

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

- added topology client heartbeat.
- modified AbstractListenerRegistry to allow for extension.

Change-Id: Ib8ea6ad4ba34f5732d062f1c9ef545f105eb167b
...@@ -33,7 +33,7 @@ public class AbstractListenerRegistry<E extends Event, L extends EventListener<E ...@@ -33,7 +33,7 @@ public class AbstractListenerRegistry<E extends Event, L extends EventListener<E
33 33
34 private final Logger log = getLogger(getClass()); 34 private final Logger log = getLogger(getClass());
35 35
36 - private final Set<L> listeners = new CopyOnWriteArraySet<>(); 36 + protected final Set<L> listeners = new CopyOnWriteArraySet<>();
37 private volatile boolean shutdown = false; 37 private volatile boolean shutdown = false;
38 38
39 /** 39 /**
...@@ -93,5 +93,4 @@ public class AbstractListenerRegistry<E extends Event, L extends EventListener<E ...@@ -93,5 +93,4 @@ public class AbstractListenerRegistry<E extends Event, L extends EventListener<E
93 shutdown = true; 93 shutdown = true;
94 } 94 }
95 95
96 -
97 } 96 }
......
...@@ -41,6 +41,7 @@ import java.util.HashMap; ...@@ -41,6 +41,7 @@ import java.util.HashMap;
41 import java.util.Map; 41 import java.util.Map;
42 42
43 import static com.google.common.base.Preconditions.checkNotNull; 43 import static com.google.common.base.Preconditions.checkNotNull;
44 +import static java.lang.System.currentTimeMillis;
44 import static org.onosproject.ui.impl.topo.TopoUiEvent.Type.SUMMARY_UPDATE; 45 import static org.onosproject.ui.impl.topo.TopoUiEvent.Type.SUMMARY_UPDATE;
45 46
46 /** 47 /**
...@@ -51,6 +52,7 @@ public class AltTopoViewMessageHandler extends UiMessageHandler ...@@ -51,6 +52,7 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
51 implements OverlayService { 52 implements OverlayService {
52 53
53 private static final String TOPO_START = "topoStart"; 54 private static final String TOPO_START = "topoStart";
55 + private static final String TOPO_HEARTBEAT = "topoHeartbeat";
54 private static final String TOPO_STOP = "topoStop"; 56 private static final String TOPO_STOP = "topoStop";
55 private static final String REQ_SUMMARY = "requestSummary"; 57 private static final String REQ_SUMMARY = "requestSummary";
56 private static final String CANCEL_SUMMARY = "cancelSummary"; 58 private static final String CANCEL_SUMMARY = "cancelSummary";
...@@ -60,7 +62,7 @@ public class AltTopoViewMessageHandler extends UiMessageHandler ...@@ -60,7 +62,7 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
60 protected ServiceDirectory directory; 62 protected ServiceDirectory directory;
61 protected TopoUiModelService modelService; 63 protected TopoUiModelService modelService;
62 64
63 - private TopoUiListener modelListener; 65 + private ModelListener modelListener;
64 private String version; 66 private String version;
65 private SummaryGenerator defaultSummaryGenerator; 67 private SummaryGenerator defaultSummaryGenerator;
66 private SummaryGenerator currentSummaryGenerator; 68 private SummaryGenerator currentSummaryGenerator;
...@@ -83,22 +85,17 @@ public class AltTopoViewMessageHandler extends UiMessageHandler ...@@ -83,22 +85,17 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
83 85
84 @Override 86 @Override
85 public void destroy() { 87 public void destroy() {
86 -// cancelAllMonitoring(); 88 + cancelAllMonitoring();
87 -// stopListeningToModel(); 89 + stopListeningToModel();
88 super.destroy(); 90 super.destroy();
89 } 91 }
90 92
91 93
92 - private String getVersion() {
93 - String ver = directory.get(CoreService.class).version().toString();
94 - return ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", "");
95 - }
96 -
97 -
98 @Override 94 @Override
99 protected Collection<RequestHandler> createRequestHandlers() { 95 protected Collection<RequestHandler> createRequestHandlers() {
100 return ImmutableSet.of( 96 return ImmutableSet.of(
101 new TopoStart(), 97 new TopoStart(),
98 + new TopoHeartbeat(),
102 new TopoStop(), 99 new TopoStop(),
103 new ReqSummary(), 100 new ReqSummary(),
104 new CancelSummary() 101 new CancelSummary()
...@@ -107,6 +104,27 @@ public class AltTopoViewMessageHandler extends UiMessageHandler ...@@ -107,6 +104,27 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
107 } 104 }
108 105
109 // ===================================================================== 106 // =====================================================================
107 +
108 + private void cancelAllMonitoring() {
109 + // TODO:
110 + }
111 +
112 + private void startListeningToModel() {
113 + topoActive = true;
114 + modelService.addListener(modelListener);
115 + }
116 +
117 + private void stopListeningToModel() {
118 + topoActive = false;
119 + modelService.removeListener(modelListener);
120 + }
121 +
122 + private String getVersion() {
123 + String ver = directory.get(CoreService.class).version().toString();
124 + return ver.replace(".SNAPSHOT", "*").replaceFirst("~.*$", "");
125 + }
126 +
127 + // =====================================================================
110 // Overlay Service 128 // Overlay Service
111 // TODO: figure out how we are going to switch overlays in and out... 129 // TODO: figure out how we are going to switch overlays in and out...
112 130
...@@ -136,12 +154,21 @@ public class AltTopoViewMessageHandler extends UiMessageHandler ...@@ -136,12 +154,21 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
136 154
137 @Override 155 @Override
138 public void process(long sid, ObjectNode payload) { 156 public void process(long sid, ObjectNode payload) {
139 - topoActive = true; 157 + startListeningToModel();
140 - modelService.addListener(modelListener);
141 sendMessages(modelService.getInitialState()); 158 sendMessages(modelService.getInitialState());
142 } 159 }
143 } 160 }
144 161
162 + private final class TopoHeartbeat extends RequestHandler {
163 + private TopoHeartbeat() {
164 + super(TOPO_HEARTBEAT);
165 + }
166 + @Override
167 + public void process(long sid, ObjectNode payload) {
168 + modelListener.nudge();
169 + }
170 + }
171 +
145 private final class TopoStop extends RequestHandler { 172 private final class TopoStop extends RequestHandler {
146 private TopoStop() { 173 private TopoStop() {
147 super(TOPO_STOP); 174 super(TOPO_STOP);
...@@ -149,8 +176,7 @@ public class AltTopoViewMessageHandler extends UiMessageHandler ...@@ -149,8 +176,7 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
149 176
150 @Override 177 @Override
151 public void process(long sid, ObjectNode payload) { 178 public void process(long sid, ObjectNode payload) {
152 - topoActive = false; 179 + stopListeningToModel();
153 - modelService.removeListener(modelListener);
154 } 180 }
155 } 181 }
156 182
...@@ -227,6 +253,10 @@ public class AltTopoViewMessageHandler extends UiMessageHandler ...@@ -227,6 +253,10 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
227 // Our listener for model events so we can push changes out to the UI... 253 // Our listener for model events so we can push changes out to the UI...
228 254
229 private class ModelListener implements TopoUiListener { 255 private class ModelListener implements TopoUiListener {
256 + private static final long AWAKE_THRESHOLD_MS = 6000;
257 +
258 + private long lastNudged = currentTimeMillis();
259 +
230 @Override 260 @Override
231 public void event(TopoUiEvent event) { 261 public void event(TopoUiEvent event) {
232 log.debug("Handle Event: {}", event); 262 log.debug("Handle Event: {}", event);
...@@ -238,6 +268,15 @@ public class AltTopoViewMessageHandler extends UiMessageHandler ...@@ -238,6 +268,15 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
238 } 268 }
239 handler.handleEvent(event); 269 handler.handleEvent(event);
240 } 270 }
271 +
272 + @Override
273 + public boolean isAwake() {
274 + return currentTimeMillis() - lastNudged < AWAKE_THRESHOLD_MS;
275 + }
276 +
277 + public void nudge() {
278 + lastNudged = currentTimeMillis();
279 + }
241 } 280 }
242 281
243 282
...@@ -271,5 +310,4 @@ public class AltTopoViewMessageHandler extends UiMessageHandler ...@@ -271,5 +310,4 @@ public class AltTopoViewMessageHandler extends UiMessageHandler
271 eventHandlerBinding.put(SUMMARY_UPDATE, summaryHandler); 310 eventHandlerBinding.put(SUMMARY_UPDATE, summaryHandler);
272 // NOTE: no need to bind pass-thru handlers 311 // NOTE: no need to bind pass-thru handlers
273 } 312 }
274 -
275 } 313 }
......
...@@ -99,6 +99,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { ...@@ -99,6 +99,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
99 private static final String SPRITE_LIST_REQ = "spriteListRequest"; 99 private static final String SPRITE_LIST_REQ = "spriteListRequest";
100 private static final String SPRITE_DATA_REQ = "spriteDataRequest"; 100 private static final String SPRITE_DATA_REQ = "spriteDataRequest";
101 private static final String TOPO_START = "topoStart"; 101 private static final String TOPO_START = "topoStart";
102 + private static final String TOPO_HEARTBEAT = "topoHeartbeat";
102 private static final String TOPO_STOP = "topoStop"; 103 private static final String TOPO_STOP = "topoStop";
103 104
104 105
...@@ -170,6 +171,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { ...@@ -170,6 +171,7 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
170 protected Collection<RequestHandler> createRequestHandlers() { 171 protected Collection<RequestHandler> createRequestHandlers() {
171 return ImmutableSet.of( 172 return ImmutableSet.of(
172 new TopoStart(), 173 new TopoStart(),
174 + new TopoHeartbeat(),
173 new TopoStop(), 175 new TopoStop(),
174 new ReqSummary(), 176 new ReqSummary(),
175 new CancelSummary(), 177 new CancelSummary(),
...@@ -211,6 +213,18 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase { ...@@ -211,6 +213,18 @@ public class TopologyViewMessageHandler extends TopologyViewMessageHandlerBase {
211 } 213 }
212 214
213 @Deprecated 215 @Deprecated
216 + private final class TopoHeartbeat extends RequestHandler {
217 + private TopoHeartbeat() {
218 + super(TOPO_HEARTBEAT);
219 + }
220 +
221 + @Override
222 + public void process(long sid, ObjectNode payload) {
223 + // place holder for now
224 + }
225 + }
226 +
227 + @Deprecated
214 private final class TopoStop extends RequestHandler { 228 private final class TopoStop extends RequestHandler {
215 private TopoStop() { 229 private TopoStop() {
216 super(TOPO_STOP); 230 super(TOPO_STOP);
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + *
16 + */
17 +
18 +package org.onosproject.ui.impl.topo;
19 +
20 +import org.onosproject.event.AbstractListenerRegistry;
21 +import org.slf4j.Logger;
22 +
23 +import java.util.HashSet;
24 +import java.util.Set;
25 +
26 +import static org.slf4j.LoggerFactory.getLogger;
27 +
28 +/**
29 + * A listener registry that automatically prunes listeners that have not
30 + * been sending heartbeat messages to assure us they are really listening.
31 + */
32 +// package private
33 +class ModelListenerRegistry
34 + extends AbstractListenerRegistry<TopoUiEvent, TopoUiListener> {
35 +
36 + private final Logger log = getLogger(getClass());
37 +
38 + private final Set<TopoUiListener> zombies = new HashSet<>();
39 +
40 + @Override
41 + public void process(TopoUiEvent event) {
42 + zombies.clear();
43 + for (TopoUiListener listener : listeners) {
44 + try {
45 + if (listener.isAwake()) {
46 + listener.event(event);
47 + } else {
48 + zombies.add(listener);
49 + }
50 + } catch (Exception error) {
51 + reportProblem(event, error);
52 + }
53 + }
54 +
55 + // clean up zombie listeners
56 + for (TopoUiListener z : zombies) {
57 + log.debug("Removing zombie model listener: {}", z);
58 + removeListener(z);
59 + }
60 + }
61 +}
...@@ -22,6 +22,7 @@ import org.onosproject.ui.impl.topo.overlay.SummaryGenerator; ...@@ -22,6 +22,7 @@ import org.onosproject.ui.impl.topo.overlay.SummaryGenerator;
22 /** 22 /**
23 * Provides the API for external agents to inject topology overlay behavior. 23 * Provides the API for external agents to inject topology overlay behavior.
24 */ 24 */
25 +// TODO: move to core-api module
25 public interface OverlayService { 26 public interface OverlayService {
26 27
27 /** 28 /**
......
...@@ -20,6 +20,7 @@ package org.onosproject.ui.impl.topo; ...@@ -20,6 +20,7 @@ package org.onosproject.ui.impl.topo;
20 /** 20 /**
21 * Provides basic summary data for the topology. 21 * Provides basic summary data for the topology.
22 */ 22 */
23 +// TODO: review -- move to core-api module?
23 public interface SummaryData { 24 public interface SummaryData {
24 25
25 /** 26 /**
......
...@@ -24,4 +24,10 @@ import org.onosproject.event.EventListener; ...@@ -24,4 +24,10 @@ import org.onosproject.event.EventListener;
24 */ 24 */
25 public interface TopoUiListener extends EventListener<TopoUiEvent> { 25 public interface TopoUiListener extends EventListener<TopoUiEvent> {
26 26
27 + /**
28 + * Returns true if the listener really is listening.
29 + *
30 + * @return true if awake
31 + */
32 + boolean isAwake();
27 } 33 }
......
...@@ -27,7 +27,6 @@ import org.apache.felix.scr.annotations.Service; ...@@ -27,7 +27,6 @@ import org.apache.felix.scr.annotations.Service;
27 import org.onosproject.cluster.ClusterEvent; 27 import org.onosproject.cluster.ClusterEvent;
28 import org.onosproject.cluster.ClusterService; 28 import org.onosproject.cluster.ClusterService;
29 import org.onosproject.cluster.ControllerNode; 29 import org.onosproject.cluster.ControllerNode;
30 -import org.onosproject.event.AbstractListenerRegistry;
31 import org.onosproject.event.EventDeliveryService; 30 import org.onosproject.event.EventDeliveryService;
32 import org.onosproject.mastership.MastershipService; 31 import org.onosproject.mastership.MastershipService;
33 import org.onosproject.net.Device; 32 import org.onosproject.net.Device;
...@@ -103,9 +102,8 @@ public class TopoUiModelManager implements TopoUiModelService { ...@@ -103,9 +102,8 @@ public class TopoUiModelManager implements TopoUiModelService {
103 protected EventDeliveryService eventDispatcher; 102 protected EventDeliveryService eventDispatcher;
104 103
105 104
106 - private AbstractListenerRegistry<TopoUiEvent, TopoUiListener> 105 + private final ModelListenerRegistry listenerRegistry =
107 - listenerRegistry = new AbstractListenerRegistry<>(); 106 + new ModelListenerRegistry();
108 -
109 107
110 private final TopoMessageFactory messageFactory = new TopoMessageFactory(); 108 private final TopoMessageFactory messageFactory = new TopoMessageFactory();
111 private final MetaDb metaDb = new MetaDb(); 109 private final MetaDb metaDb = new MetaDb();
...@@ -138,17 +136,6 @@ public class TopoUiModelManager implements TopoUiModelService { ...@@ -138,17 +136,6 @@ public class TopoUiModelManager implements TopoUiModelService {
138 } 136 }
139 137
140 138
141 - // TODO: figure out how to cull zombie listeners
142 - // The problem is when one refreshes the GUI (topology view)
143 - // a new instance of AltTopoViewMessageHandler is created and added
144 - // as a listener, but we never got a TopoStop event, which is what
145 - // causes the listener (for an AltTopoViewMessageHandler instance) to
146 - // be removed.
147 - // ==== Somehow need to tie this in to the GUI-disconnected event.
148 - // This probably requires client-generated heartbeat messages to
149 - // Keep the connection alive.
150 -
151 -
152 @Override 139 @Override
153 public void addListener(TopoUiListener listener) { 140 public void addListener(TopoUiListener listener) {
154 listenerRegistry.addListener(listener); 141 listenerRegistry.addListener(listener);
...@@ -156,7 +143,12 @@ public class TopoUiModelManager implements TopoUiModelService { ...@@ -156,7 +143,12 @@ public class TopoUiModelManager implements TopoUiModelService {
156 143
157 @Override 144 @Override
158 public void removeListener(TopoUiListener listener) { 145 public void removeListener(TopoUiListener listener) {
159 - listenerRegistry.removeListener(listener); 146 + // we don't really care if the listener is not listed...
147 + try {
148 + listenerRegistry.removeListener(listener);
149 + } catch (IllegalArgumentException e) {
150 + log.debug("Oops, listener not registered: {}", listener);
151 + }
160 } 152 }
161 153
162 @Override 154 @Override
......
...@@ -27,11 +27,14 @@ ...@@ -27,11 +27,14 @@
27 'use strict'; 27 'use strict';
28 28
29 // injected refs 29 // injected refs
30 - var $log, wss, tps, tis, tfs, tss, tts, tspr; 30 + var $log, $interval, wss, tps, tis, tfs, tss, tts, tspr;
31 31
32 // internal state 32 // internal state
33 var handlerMap, 33 var handlerMap,
34 - openListener; 34 + openListener,
35 + heartbeatTimer;
36 +
37 + var heartbeatPeriod = 5000; // 5 seconds
35 38
36 // ========================== 39 // ==========================
37 40
...@@ -68,14 +71,31 @@ ...@@ -68,14 +71,31 @@
68 wss.sendEvent('topoStart'); 71 wss.sendEvent('topoStart');
69 } 72 }
70 73
74 + function cancelHeartbeat() {
75 + if (heartbeatTimer) {
76 + $interval.cancel(heartbeatTimer);
77 + }
78 + heartbeatTimer = null;
79 + }
80 +
81 + function scheduleHeartbeat() {
82 + cancelHeartbeat();
83 + heartbeatTimer = $interval(function () {
84 + wss.sendEvent('topoHeartbeat');
85 + }, heartbeatPeriod);
86 + }
87 +
88 +
71 angular.module('ovTopo') 89 angular.module('ovTopo')
72 .factory('TopoEventService', 90 .factory('TopoEventService',
73 - ['$log', '$location', 'WebSocketService', 91 + ['$log', '$interval', 'WebSocketService',
74 'TopoPanelService', 'TopoInstService', 'TopoForceService', 92 'TopoPanelService', 'TopoInstService', 'TopoForceService',
75 'TopoSelectService', 'TopoTrafficService', 'TopoSpriteService', 93 'TopoSelectService', 'TopoTrafficService', 'TopoSpriteService',
76 94
77 - function (_$log_, $loc, _wss_, _tps_, _tis_, _tfs_, _tss_, _tts_, _tspr_) { 95 + function (_$log_, _$interval_, _wss_,
96 + _tps_, _tis_, _tfs_, _tss_, _tts_, _tspr_) {
78 $log = _$log_; 97 $log = _$log_;
98 + $interval = _$interval_;
79 wss = _wss_; 99 wss = _wss_;
80 tps = _tps_; 100 tps = _tps_;
81 tis = _tis_; 101 tis = _tis_;
...@@ -90,10 +110,12 @@ ...@@ -90,10 +110,12 @@
90 openListener = wss.addOpenListener(wsOpen); 110 openListener = wss.addOpenListener(wsOpen);
91 wss.bindHandlers(handlerMap); 111 wss.bindHandlers(handlerMap);
92 wss.sendEvent('topoStart'); 112 wss.sendEvent('topoStart');
113 + scheduleHeartbeat();
93 $log.debug('topo comms started'); 114 $log.debug('topo comms started');
94 } 115 }
95 116
96 function stop() { 117 function stop() {
118 + cancelHeartbeat();
97 wss.sendEvent('topoStop'); 119 wss.sendEvent('topoStop');
98 wss.unbindHandlers(handlerMap); 120 wss.unbindHandlers(handlerMap);
99 wss.removeOpenListener(openListener); 121 wss.removeOpenListener(openListener);
......